Outcome: an agent identity that can mint a client_credentials token and call one protected route. About five minutes from an empty environment.
Self-serve. Agents is open in every environment. Usage counts against your plan's agent-run quota. Do not put a human cookie or PAT in the agent runtime.
0. Prerequisites
- An Authdog tenant, project, and environment.
- A token endpoint from the environment OIDC metadata (
/oauth2/token).
1. Register an agent
In the console, open Agents (/dashboard/agents):
- New agent → Create new agent identity.
- Name it. Give it the
mcp:invokescope. Optional: allowed tools (*or globs). - Copy the client ID and client secret. The secret is shown once.
This provisions a confidential OIDC client and a trust-store row whose subject is that client ID.
REST equivalent (after you have a management token):
POST /v1/tenants/{tenantId}/environments/{environmentId}/mcp/trust-storeThe wizard is the path that also creates the OIDC client. REST registers a subject that already authenticates.
2. Exchange credentials for a token
Ask for the mcp:invoke scope and name the MCP server as the resource. The resource must be an https URL. With both, Authdog mints a signed JWT whose audience is that server. Without them the token is opaque and carries no audience.
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" \
--data-urlencode "scope=mcp:invoke" \
--data-urlencode "resource=https://mcp.example.com"You get access_token, token_type, expires_in. No refresh token. Repeat the exchange before expiry.
A revoked agent whose subject is this client_id gets 401 on this call. Tokens already issued stay valid until expires_in unless the resource server runs trust-store enforce.
3. Call a protected route
Authorization: Bearer <access_token>For an HTTP API, use a backend SDK require_auth gate (Express, FastAPI).
For an MCP server in Node.js, install the verifier and the MCP SDK:
npm install @authdog/mcp-sdk @modelcontextprotocol/sdk expressThen guard the /mcp route. Setting issuer verifies the JWT locally against your environment's keys, and audience rejects tokens minted for another server:
import express from "express"
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"
import { AuthdogVerifier } from "@authdog/mcp-sdk"
import { createMcpTokenVerifier } from "@authdog/mcp-sdk/mcp"
const authdog = new AuthdogVerifier({
baseUrl: "https://api.authdog.com",
issuer: "https://api.authdog.com/oidc/<environmentId>",
audience: "https://mcp.example.com",
})
const app = express()
app.use(
"/mcp",
requireBearerAuth({
verifier: createMcpTokenVerifier(authdog, {
resource: "https://mcp.example.com",
}),
requiredScopes: ["mcp:invoke"],
resourceMetadataUrl:
"https://mcp.example.com/.well-known/oauth-protected-resource",
expectedResource: "https://mcp.example.com",
}),
)
app.listen(3000)See the Node.js SDK for every option. To also enforce allowed tools and per-tool decisions, add the private runtime guard described in MCP SDK, or run examples/mcp-auth-sample with MCP_TRUST_STORE_MODE=enforce and MCP_AUTHZ_MODE=enforce.
Agent frameworks puts this same bearer on the hook that framework already exposes.
4. Verify
- Token exchange without credentials → 401.
GETthe protected route without a bearer → 401.- With the bearer → the caller is the agent, not a human session.
Next
- Agent frameworks: the hook for each framework, including the ones whose samples are still planned.
- Agents: verify, revoke, pin, activity.
- Machine identity: M2M project vs Agents vs service account vs PAT.
- AI agent security: same primitives, longer form.
- MCP security: protect your MCP server.