Authdog is the identity provider. The framework keeps its own auth hook. Register an agent, mint a short-lived bearer with `client_credentials`, and verify that bearer at the hook before the agent, tool, or graph runs.

Each hook is grouped under [AI](/docs/ai). The group has pages for the auth boundary, machine credentials, tool checks, and what you put in the prompt.

Runnable copies live in [authdog/agents-samples](https://github.com/authdog/agents-samples). Copy one folder. Each folder is its own project.

## Which path

| You need | Open |
| --- | --- |
| An agent signs into your app through the browser | [Agent Sign-In](/docs/agent-sign-in) |
| An agent calls your API, an MCP server, or another agent | The framework pages below |
| An MCP client signs a person in through hosted OAuth | [MCP authentication](/docs/mcp-authentication) |
| An agent acts as a person | [Acting for a person](#acting-for-a-person) |

## Where the hook is

| Framework | Hook | Role in the sample |
| --- | --- | --- |
| [A2A](/docs/agent-frameworks/a2a) | Agent card security scheme, then `require_auth` on the JSON-RPC mount | Server admits the caller. The client sends the bearer on HTTP. |
| [Eve](/docs/agent-frameworks/eve) | Channel `auth` walk: `oidc({ issuer, audiences })` | Session routes verify the bearer JWT. |
| [MCP](/docs/agent-frameworks/mcp) | `token_verifier` on the MCP server | `whoami` runs only after userinfo accepts the token. |
| [LangGraph](/docs/agent-frameworks/langgraph) | `require_auth` on `POST /invoke` | The graph runs only after that dependency returns a user. |
| [OpenAI Agents SDK](/docs/agent-frameworks/openai-agents) | Tool `RunContext` plus `require_auth` on the API the tool calls | The tool presents the token. The route decides. |
| [CrewAI](/docs/agent-frameworks/crewai) | `EXECUTION_START` raises `HookAborted` | The crew does not start when userinfo rejects the bearer. Sample planned. |
| [Google ADK](/docs/agent-frameworks/adk) | `before_agent_callback` | Returning content skips the agent. Sample planned. |
| [Pydantic AI](/docs/agent-frameworks/pydantic-ai) | `RunContext` deps, then `require_auth` on your API | The tool presents the token. The route decides. Sample planned. |
| [Claude Agent SDK](/docs/agent-frameworks/claude-agent) | `PreToolUse` returns `permissionDecision: deny` | The hook blocks the tool. Sample planned. |
| [Mastra](/docs/agent-frameworks/mastra) | `server.auth.authenticateToken` | API routes require a userinfo-accepted bearer. Sample planned. |
| [LlamaIndex Workflows](/docs/agent-frameworks/llamaindex) | `require_auth` on the route that calls `workflow.run` | The first step stops when the subject is missing. Sample planned. |
| [Semantic Kernel](/docs/agent-frameworks/semantic-kernel) | `FUNCTION_INVOCATION` filter skips `next` | The kernel function does not run. Sample planned. |
| [Vercel AI SDK](/docs/agent-frameworks/ai-sdk) | `toolsContext` on the tool, then `require_auth` on your API | The tool presents the token. The route decides. Sample planned. |
| [Strands Agents](/docs/agent-frameworks/strands) | `BeforeToolCallEvent.cancel_tool` | A cancelled call does not run the tool. Sample planned. |
| [LangChain](/docs/agent-frameworks/langchain) | `wrap_tool_call` does not call the handler | The middleware stops the tool. Sample planned. |

Rows marked "Sample planned" describe the hook. The folder is not in the repository yet, and the page does not pin a package version. `subject_if_accepted` and `subjectIfAccepted` in those snippets are the sample's userinfo helper. They are not framework APIs. They return the subject, or nothing when the bearer is missing or rejected.

## Register the caller

In the console, open **Agents → Create new agent identity**. Copy the client id and client secret. The secret is shown once.

The protected process takes the environment **public key** (`pk_...`) from Project settings. That key selects the identity host. It does not mint tokens.

Keep the client secret in the process that calls the token endpoint. Put the access token on the HTTP request. Leave both out of prompts, graph state, and logs.

The Agents module is sales-gated. Until access is approved, an [M2M application](/docs/machine-identity) issues the same `client_credentials` grant. Keep a development token off your production API.

## Mint the bearer

Read `token_endpoint` from the environment discovery document. Authdog's path is `/oauth2/token`.

```bash
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"
```

The response carries `access_token`, `token_type` (`Bearer`), and `expires_in`. There is no refresh token. Repeat the exchange before expiry.

Send it as `Authorization: Bearer <access_token>`.

## What verification returns

`require_auth` and `fetch_user_data` both check the bearer with the environment userinfo endpoint. The public key chooses that host. A missing bearer, a userinfo rejection, or a payload with no subject is `401`. An accepted payload looks like the samples' stand-in:

```json
{"meta": {"code": 200}, "user": {"id": "agent_1"}}
```

The samples read `user.id`, then `user.sub`, and return that string. Where the framework has a scope field, they record `agent:invoke`.

| Check | Result |
| --- | --- |
| Missing or rejected bearer | `401` |
| Accepted userinfo | Subject from `id`, then `sub` |
| Scope field, when the framework has one | `agent:invoke` |
| Refresh token | None. Exchange again before `expires_in`. |
| `pk_...` | Selects the identity host. Stays on the resource server. |

Eve is the exception on this page: it verifies the JWT with discovery instead of calling userinfo. The subject still has to be the one in that token. See [Eve checks the JWT](#eve-checks-the-jwt).

## After the subject is known

The samples stop once the subject is known. A `200` from `whoami` means userinfo accepted the bearer. It does not mean the agent may call every tool.

The [Agents](/docs/console/agents) registry is the next check on that subject:

| Control | Effect |
| --- | --- |
| Allowed scopes and tools | Globs on the trust-store row. `*` allows every tool. |
| Trust-store enforce | The [MCP trust-store sample](https://github.com/authdog/agents-samples/tree/main/trust-store/python) applies that row. The other samples stop at the subject. |
| Revoke | New tokens fail when the subject is the OIDC client id. Tokens already issued last until `expires_in` unless the resource server enforces the trust store. |

Apply [authorization](/docs/concepts/authorization) after the identity check when the API is yours.

## Acting for a person

The framework hook stays the same. Replace the `client_credentials` access token with an [RFC 8693 token](/guides/agent-delegation). `sub` stays the user. `act` names the agent.

```json
{
  "sub": "<user id>",
  "act": { "sub": "client:<agent client_id>" }
}
```

Put that token in the `Authorization` header, or in the OpenAI Agents [`Caller.token`](/docs/agent-frameworks/openai-agents). Grant token exchange on the agent in [Agents](/docs/console/agents). The [delegation sample](https://github.com/authdog/agents-samples/tree/main/delegation/python) reads a token whose subject is the person. The other samples mint a token whose subject is the agent client.

## One token, three callers

Mint once. Present that bearer to each userinfo gate. Run one sample at a time, or bind the ports below so they do not share `8000`.

```bash
TOKEN=$(curl -s -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" \
  | python -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
```

LangGraph, from `langgraph/python` with `PK_AUTHDOG` set:

```bash
uvicorn server:create_app --factory --port 8001
curl -s -X POST http://127.0.0.1:8001/invoke \
  -H "Authorization: Bearer $TOKEN"
```

The JSON reply is `{"reply":"subject:<id>"}`. The same call without the header is `401` and the body has no `subject:`.

MCP, from `mcp/python` with `PK_AUTHDOG` set. The verifier reads this header. `POST /mcp` without it is `401`. The sample's `whoami` tool returns the subject:

```bash
uvicorn server:create_app --factory --port 8002
curl -s -D - -o /dev/null -X POST http://127.0.0.1:8002/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize"}'
```

OpenAI Agents, from `openai-agents/python`. The route is the gate. The tool only attaches the header. `OPENAI_API_KEY` is required for `Runner.run`. `pytest` does not need it.

```bash
uvicorn tool_api:create_app --factory --port 8003
export WHOAMI_URL=http://127.0.0.1:8003/whoami
export AUTHDOG_ACCESS_TOKEN="$TOKEN"
```

```python
context=Caller(token=os.environ["AUTHDOG_ACCESS_TOKEN"])
```

`GET /whoami` with the bearer returns `{"subject":"<id>"}`.

### Eve checks the JWT

Eve does not call userinfo. `oidc()` verifies the bearer as a JWT against the issuer discovery document: signature, issuer, expiry, and audience. Agent access tokens for the environment are JWTs. Set `AUTHDOG_ISSUER` to the token's `iss` and `AUTHDOG_AUDIENCE` to its `aud`. The Eve process reads neither the client secret nor `PK_AUTHDOG`.

`npx eve dev` listens on port 2000 unless `PORT` is set.

```bash
curl -s -D - -o /dev/null -X POST http://127.0.0.1:2000/eve/v1/session \
  -H "Authorization: Bearer $TOKEN"
```

A session request with no bearer is `401`. The health route stays public.

## Python gate

A2A, MCP, LangGraph, and the OpenAI Agents sample import `require_auth` or `fetch_user_data` from the Python SDK in [authdog/web-sdk](https://github.com/authdog/web-sdk). That package is installed from commit `afad543932541a9e9d3c891421151509d04b49b2` in each sample's `requirements.txt`. `pip install authdog-fastapi` on the public index is a different name and is not the install those samples use.

Eve verifies the JWT with Eve's own `oidc()` helper and the issuer discovery document.

## Related

| Read | To learn how to |
| --- | --- |
| [Machine identity](/docs/machine-identity) | Choose M2M, Agents, a service account, or a PAT |
| [Agents quickstart](/docs/quickstarts/agents) | Register an agent and mint the first token |
| [MCP authentication](/docs/mcp-authentication) | Let an MCP client sign in through the hosted issuer |
| [Agent delegation](/guides/agent-delegation) | Keep the user as `sub` when an agent acts for them |
