Authdog
Log In

AI guide

AI Agent Security

An agent should sign in as itself, not by borrowing someone's browser session.

Give it a named Agents identity when it lives inside a project you already have. Use a machine-to-machine application when the whole project is the workload. Both mint a confidential OIDC client and take client_credentials. Authorization still happens on your server, after the token arrives. Agents is sales-gated. Until access is approved, that page is a locked landing with Request access. See Support. If you already have access, the five-minute path is the Agents quickstart.

Prerequisites

  • An Authdog project and environment.
  • Approved Agents access, or an M2M application you can create yourself (below).
  • The client ID and client secret from creation. The secret is shown once. Copy it into a secret manager before you leave the page.
  • A trusted server, worker, job, or agent runtime. Never put these credentials in browser code or in a prompt.
  • An API or MCP server that validates the bearer token and enforces authorization on its own.

Use a separate client, or a separate Agents entry, for each workload and each environment. A token from development must not open production.

Implementation

1. Register the identity before you ask for a token

Agents, when you want a named agent inside a web or MCP project. Open Agents and choose Create new agent identity, or attach an existing client ID, a spiffe:// ID, or a URI. SPIFFE and URI subjects are labels in the registry. The issuer kill switch applies when subject is the OIDC client_id.

Machine-to-machine, when the whole project is the workload. Open Projects and create a Machine-to-machine application. You get the same token grant. You do not get Agents activity or a tool allowlist.

Write down which path you used. The rest of this guide assumes you can exchange that client's secret for an access token without a user present.

2. Exchange the secret for a short-lived token

Discover token_endpoint from the environment's OIDC metadata. It is the /oauth2/token URL. Send the client credentials with HTTP Basic auth:

curl -X POST "$TOKEN_ENDPOINT" \
  -u "$AUTHDOG_CLIENT_ID:$AUTHDOG_CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials"

You should get access_token, token_type, and expires_in. There is no refresh token. Call this again before the access token expires.

Revoking the Agents entry blocks new tokens when subject is the client_id. Tokens already issued stay valid until expires_in runs out, unless the resource server is running the trust store in enforce. If you need a stolen token to die immediately, enforce is the control that does it. Revoke alone does not.

3. Send the token to the service you are protecting

Authorization: Bearer <access_token>

For an MCP service, examples/mcp-auth-sample authenticates the request before the transport runs. @authdog/mcp-sdk ships as source in packages/mcp-sdk. It is not on npm. It verifies JWTs through JWKS and can check issuer, audience, scopes, and the trust store. Walk through MCP security once this token exchange works.

4. Reject the token at more than one place

Authenticate every request before you dispatch it. Reject the wrong issuer or the wrong audience. Require a baseline scope such as mcp:invoke before the handler runs. Then apply a narrower check, such as mcp:admin, on the tools that change state. A valid workload identity is input to authorization. It is not a blanket permission.

5. Keep the token in memory, and refresh it early

Hold the access token in the process that will send it, and only until shortly before expires_in. When many instances refresh at the same moment they stampede the token endpoint. Add jitter so they do not.

Security considerations

  • Store the client secret in a secret manager. It does not belong in prompts, shell history, or logs.
  • Split service-wide scope (mcp:invoke) from tool scope (mcp:admin). The first gets the agent in the door. The second is what a dangerous tool should demand.
  • Do not hand an unattended job a human cookie or a personal access token. You will not be able to tell the job apart from the person.
  • Record the client ID and the workload name in audit context. Redact the secret and the full token.
  • If the secret leaks, revoke the Agents entry or replace the client credentials. If the resource server can enforce the trust store, do that too, so outstanding JWTs stop working before they expire.

Validation checklist

  • Agent obtains a token with client_credentials, without a browser or user session.
  • Invalid client credentials are rejected by the token endpoint.
  • Protected service rejects missing, expired, or wrong-audience tokens.
  • Baseline scope failure prevents request dispatch.
  • Sensitive operations enforce their own narrower scope.
  • Development token does not grant production access.
  • Logs contain workload identifiers but no secrets or complete tokens.

Troubleshooting

The token exchange fails. Confirm the client is active, uses the auth method you are sending, and is allowed to use client_credentials. If you revoked an Agents row, new tokens fail for client_id subjects. That failure is the revoke working.

The protected service returns 401. Check issuer, JWKS URI, audience, and that the header is Authorization: Bearer plus the token. A 403 means authentication succeeded and a required scope is missing.

The Agents page is locked. Request access. The module is sales-gated. Use an M2M application if you need a workload token before that approval lands.

It only fails when you cross environments. The agent is using a client or token endpoint from a different environment than the service it is calling. Point both at the same one and request a new token.

Next steps