agent-taskbay
Operator handbook

Connect an OAuth-protected agent

Register a protected Agent Card, configure a client-credentials binding, verify discovery, and manage rotation.

Taskbay can call OAuth-protected agents using the OAuth 2.0 client-credentials grant. Each agent has one server-side service credential shared by all authorized people. The console's OIDC login token is never forwarded to an agent.

The token endpoint is explicitly configured, uses HTTPS and must accept client_secret_post. Taskbay requests a bearer access token for each connection. There is no discovery of an agent's OAuth metadata, refresh-token flow, consent redirect, per-user token store, token exchange, configurable audience/resource parameter or automatic completion of in-task AUTH_REQUIRED authorization.

1. Prepare the agent and OAuth client

Get the Agent Card URL, every advertised HTTP interface origin, the OAuth issuer, the exact token endpoint, a client ID and client secret, and any required scope from the agent operator. The agent must offer JSON-RPC or HTTP+JSON; gRPC is unsupported.

In this example:

PurposeExample
Agent Cardhttps://catalog.example.com/team/card.json
A2A interfacehttps://agents.example.com/a2a
OAuth issuerhttps://identity.example.com
Token endpointhttps://identity.example.com/oauth/token

Origins include scheme, hostname and optional port, without a path. URLs must not embed credentials, queries or fragments.

2. Configure outbound policy and the vault

Generate a fresh 32-byte random vault key using openssl rand -hex 32. Store it as 64 hex characters in the key ring, using your own key ID:

A2A_ALLOWED_AGENT_ORIGINS=https://catalog.example.com,https://agents.example.com,https://identity.example.com
A2A_VAULT_KEYS='{"key-1":"REPLACE_WITH_64_HEX_CHARACTERS"}'
A2A_VAULT_ACTIVE_KEY=key-1

Set the same key ring and outbound policy on web and every worker, then restart them. The allowlist must include both the card and interface origins and the token endpoint origin. It also needs the notification webhook origin when enabled. A configured allowlist is enforced in development as well as production.

Production requires HTTPS and blocks private/loopback addresses by default. For an intentionally internal agent, set A2A_ALLOW_PRIVATE_NETWORKS=true while retaining a narrow allowlist. Metadata, link-local and reserved ranges remain blocked. DNS is checked on the actual connection, redirects are rejected and TLS validation stays enabled.

3. Register the protected card URL

As an administrator, open Agents → Add agent. If Fetch Agent Card fails because the card itself requires a credential, choose Register URL to save the URL without fetching it. Preview sends no credentials.

Obtain the local agent UUID from the catalog or the id field of GET /api/agents. This is the ID used by the credential CLI, not the agent's remote name or card URL.

4. Store an OAuth binding through stdin

Create binding.json in an operator-controlled location with restrictive permissions. Replace every REPLACE_... value before running the command:

{
  "origins": [
    "https://catalog.example.com",
    "https://agents.example.com"
  ],
  "credential": {
    "type": "oauthClient",
    "issuer": "https://identity.example.com",
    "tokenEndpoint": "https://identity.example.com/oauth/token",
    "clientId": "REPLACE_WITH_CLIENT_ID",
    "clientSecret": "REPLACE_WITH_CLIENT_SECRET",
    "scope": "REPLACE_WITH_AGENT_SCOPE"
  }
}

Omit scope if the authorization server does not require one. The binding's origins constrain where the resulting bearer token can be sent; list both the protected card and all interface origins that need it. The token endpoint is separately authorized by the global allowlist and the OAuth request's own destination constraint.

chmod 600 binding.json
npm run security:credentials -- --organization acme --agent REPLACE_WITH_AGENT_UUID --action store < binding.json
rm binding.json

Use your actual organization slug (local for local development, or A2A_AUTH_ORGANIZATION_SLUG for OIDC). With PGlite, stop web and add --offline-pglite. Migrations must already be applied. Do not pass secrets in CLI arguments or commit the file. The credential CLI loads .env files, prints no secret values and reports a generic error on failure.

The vault encrypts the binding as JWE with dir and A256GCM, bound to the organization and agent. The fetched access token is held in process memory, not persisted. The token endpoint must return a nonempty bearer token of at least eight characters.

5. Verify discovery and permission separately

After Agent credential binding updated., check Settings → Agent credentials for an enabled oauthClient binding. Settings shows status, never the secret. Run discovery on the registered agent and inspect its interfaces, skills, security schemes and trust status. Then start a chat as an administrator.

For operators and viewers, add grants in Settings → Teams and access. A working credential proves Taskbay can authenticate to the agent; it does not grant a person permission to use it. The card's security schemes are displayed and validated for shape, but do not automatically select or negotiate a credential type.

Rotate and revoke

Store a new binding to rotate the client secret; this replaces the prior binding and enables it. Revoke with:

npm run security:credentials -- --organization acme --agent REPLACE_WITH_AGENT_UUID --action revoke

Later calls fail closed with Agent credentials are disabled. rather than becoming anonymous. An in-flight call can finish.

For vault-key rotation, add the new key while retaining old keys, switch A2A_VAULT_ACTIVE_KEY, restart all processes, and run --action reencrypt for every enabled binding. Remove old keys only when no bindings need them. A revoked binding requires a replacement instead. Back up the vault keys separately from the database; losing a needed key makes its bindings unreadable.

Other credential types

All types use the same origins envelope and the same CLI lifecycle:

TypeCredential fieldsBehavior
apiKeyname, valueCustom header. Host, cookie, connection, content/transfer and proxy-authorization headers are restricted.
bearertokenAuthorization: Bearer .... Rotation is manual.
oauthClientissuer, tokenEndpoint, clientId, clientSecret, optional scopeClient credentials with client_secret_post; bearer response only.
mtlsPEM cert, key, optional caMutual TLS; server certificate validation remains on.

Bindings allow 1–20 exact HTTPS origins. Secret fields are 8–100000 characters, client IDs at most 255, scope at most 1000; the CLI accepts at most 256000 bytes on stdin. Extra fields are rejected. HTTP Basic, query-string keys, cookie credentials and simultaneous multiple credential schemes are not supported by the binding schema.

Troubleshoot the failing boundary

ErrorCheck
Agent OAuth credentials unavailable.Token endpoint allowlist, HTTPS/TLS, endpoint/client/secret/scope, support for client_secret_post, bearer response. The UI deliberately hides endpoint error detail.
Agent credential destination is not permitted.Binding origins include the card and selected interface origins.
Agent target is not in the configured origin allowlist.Global policy includes card, interface and token endpoint origins.
Credential vault unavailable.Valid key ring and active ID, matching keys in all processes, retained encryption keys.
Agent credentials are required.A card declares security requirements but no credential binding is available.
Agent Card trust policy rejected this connection.Inspect signature trust separately from OAuth: pin the correct unexpired public key for the card origin.

To require signed cards, configure A2A_TRUSTED_CARD_KEYS and A2A_REQUIRE_SIGNED_CARDS=true. OAuth protects calls; a trusted card signature establishes the card's configured signing identity. Neither replaces human grants.

Created and maintained by Shashi Kanth G S at allsrc.dev. Visit shashikanth.me.

On this page