> ## Documentation Index
> Fetch the complete documentation index at: https://phidatainc.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth Middleware

> Configure AuthMiddleware for JWT validation, claim injection, and RBAC across REST, MCP, and WebSocket connections.

<Badge icon="code-branch" color="orange">
  <Tooltip tip="Introduced in v2.1.0" cta="View release notes" href="https://github.com/agno-agi/agno/releases/tag/v2.1.0">v2.1.0</Tooltip>
</Badge>

`AuthMiddleware` is the AgentOS authentication layer. It validates JWTs, service-account tokens (`agno_pat_...`), and the OS security key. For JWTs it extracts tokens from Authorization headers or cookies, validates them, and injects `user_id`, `session_id`, and custom claims into your endpoints. This page covers the JWT configuration. For the other two credential types, see [Service Accounts](/agent-os/security/authorization/service-accounts) and the [security key](/agent-os/security/overview#security-key).

<Note>
  The class was renamed from `JWTMiddleware` in v2.7. `JWTMiddleware` remains as an alias, so existing `app.add_middleware(JWTMiddleware, ...)` setups keep working.
</Note>

The middleware provides three main features:

1. **Token Validation**: Validates JWT tokens and handles authentication
2. **Parameter Injection**: Automatically injects user\_id, session\_id, and custom claims into endpoint parameters
3. **RBAC Authorization**: Validates scopes against required permissions for each endpoint

```python auth_middleware_setup.py theme={null}
from agno.os.middleware.jwt import AuthMiddleware

app.add_middleware(
    AuthMiddleware,
    verification_keys=["your-jwt-verification-key"],  # or use JWT_VERIFICATION_KEY environment variable
    algorithm="RS256",  # RS256 for asymmetric keys, HS256 for symmetric
    user_id_claim="sub",  # Extract user_id from 'sub' claim
    session_id_claim="session_id",  # Extract session_id from claim
    dependencies_claims=["name", "email", "roles"],  # Additional claims
    validate=True,  # Enable token validation
    authorization=True,  # Enable RBAC scope checking
    verify_audience=True,  # Verify `aud` claim matches AgentOS ID
)
```

## Coverage Across Surfaces

AgentOS installs a single `AuthMiddleware` instance on the parent app in every authenticated deployment mode. A token accepted on one surface is accepted with identical constraints on the others.

| Surface            | How it is covered                                                                                                                                                       |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| REST routes        | Requests pass through the middleware directly                                                                                                                           |
| Mounted `/mcp` app | Parent-app middleware runs before the mount dispatches, so the MCP server carries no auth code of its own                                                               |
| WebSockets         | The middleware publishes its validator, audience, admin scope, and user-isolation settings to `app.state`; WebSocket handshakes validate against the same configuration |

### Credential Dispatch

The middleware resolves each bearer credential in order:

1. Tokens with the `agno_pat_` prefix authenticate as service accounts against the AgentOS database. This happens before JWT validation. Service-account scopes are enforced even when `authorization=False`, since they are ACL data owned by your AgentOS instance.
2. The internal service token, used by the [scheduler](/agent-os/scheduler/overview) executor to run scheduled jobs.
3. The OS security key, when no JWT source is configured.
4. Anything else is validated as a JWT.

## Token Sources

The middleware supports three token sources:

<Tabs>
  <Tab title="Authorization Header">
    Extract JWT from `Authorization: Bearer <token>` header.

    ```python theme={null}
    from agno.os.middleware.jwt import AuthMiddleware, TokenSource

    app.add_middleware(
        AuthMiddleware,
        verification_keys=["your-key"],
        token_source=TokenSource.HEADER,  # Default
    )
    ```
  </Tab>

  <Tab title="HTTP-Only Cookies">
    Extract JWT from HTTP-only cookies for web applications.

    ```python theme={null}
    app.add_middleware(
        AuthMiddleware,
        verification_keys=["your-key"],
        token_source=TokenSource.COOKIE,
        cookie_name="access_token",  # Default
    )
    ```
  </Tab>

  <Tab title="Both Sources">
    Try both header and cookie (header takes precedence).

    ```python theme={null}
    app.add_middleware(
        AuthMiddleware,
        verification_keys=["your-key"],
        token_source=TokenSource.BOTH,
        cookie_name="access_token",  # Default
        token_header_key="Authorization",  # Default
    )
    ```
  </Tab>
</Tabs>

## JWKS File Support

For environments using RSA keys managed via JWKS (JSON Web Key Set), you can point to a static JWKS file instead of providing raw public keys:

```python jwks_file_setup.py theme={null}
app.add_middleware(
    AuthMiddleware,
    jwks_file="/path/to/jwks.json",
    algorithm="RS256",
    authorization=True,
)
```

The middleware will:

1. Load public keys from the JWKS file at startup
2. Match incoming tokens by their `kid` (key ID) header claim
3. Validate signatures using the appropriate key

### JWKS File Format

The JWKS file should follow the standard format:

```json theme={null}
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "my-key-id",
      "use": "sig",
      "alg": "RS256",
      "n": "0vx7agoebGc...",
      "e": "AQAB"
    }
  ]
}
```

### Environment Variable

You can also set the JWKS file path via environment variable:

```bash theme={null}
export JWT_JWKS_FILE="/path/to/jwks.json"
```

<Note>
  JWKS keys are tried first (matched by `kid`). If no matching key is found, the middleware falls back to `verification_keys` if provided.
</Note>

## Parameter Injection

The middleware automatically injects JWT claims into AgentOS endpoints. The following parameters are extracted from tokens and injected into requests:

* `user_id` - User identifier from token claims
* `session_id` - Session identifier from token claims
* `dependencies` - Custom claims for agent tools
* `session_state` - Custom claims for session management

For example, the `/agents/{agent_id}/runs` endpoint automatically uses `user_id`, `session_id`, `dependencies`, and `session_state` from the JWT token when available.

This is useful for:

* Automatically using the `user_id` and `session_id` from your JWT token when running an agent
* Automatically filtering sessions retrieved from `/sessions` endpoints by `user_id` (where applicable)
* Automatically injecting `dependencies` from claims in your JWT token into the agent run, which then is available on tools called by your agent

See the [full example](/agent-os/usage/middleware/jwt-middleware).

## Security Features

<Note>
  Use strong verification keys, store them securely (not in code), and enable validation in production.
</Note>

**Token Validation**: When `validate=True`, the middleware:

* Verifies JWT signature using the verification key
* Checks token expiration (`exp` claim)
* Returns 401 errors for invalid/expired tokens

**Audience Verification**: When `verify_audience=True`, the middleware:

* If `audience` is provided, it will validate the token's audience claim matches the expected audience claim
* If `audience` is not provided, it will validate the token's audience claim matches the AgentOS ID
* Optionally set the `audience_claim` to validate a custom audience claim
* Returns 401 for tokens with mismatched audience

<Tip>
  **HTTP-Only Cookies**: When using cookies:

  * Set `httponly=True` to prevent JavaScript access (XSS protection)
  * Set `secure=True` for HTTPS-only transmission
  * Set `samesite="strict"` for CSRF protection
</Tip>

## Local Development

<Warning>
  Do not use `validate=False` in production. The middleware decodes claims without verifying the JWT signature.
</Warning>

Skip signature verification in local development, or when an upstream API gateway already validates JWTs.

```python theme={null}
from agno.os.middleware.jwt import AuthMiddleware

app.add_middleware(
    AuthMiddleware,
    validate=False,
)
```

No verification key is required. Claims are extracted from the token but not authenticated.

## RBAC Authorization

Enable Role-Based Access Control (RBAC) to validate JWT scopes against required permissions:

```python jwt_with_rbac.py theme={null}
app.add_middleware(
    AuthMiddleware,
    verification_keys=["your-jwt-key"],
    algorithm="RS256",
    authorization=True,  # Enable RBAC
    verify_audience=True,  # Verify aud matches AgentOS ID
)
```

When `authorization=True`, the middleware:

* Checks the `scopes` claim in JWT tokens
* Validates scopes against required permissions for each endpoint
* Returns 403 Forbidden for insufficient permissions

### Scope Format

| Format                 | Example               | Description              |
| ---------------------- | --------------------- | ------------------------ |
| `resource:action`      | `agents:read`         | Access all resources     |
| `resource:<id>:action` | `agents:my-agent:run` | Access specific resource |
| `resource:*:action`    | `agents:*:run`        | Wildcard access          |
| `agent_os:admin`       | `agent_os:admin`      | Full admin access        |

### Custom Scope Mappings

Override or extend default scope mappings:

```python custom_scope_mappings.py theme={null}
app.add_middleware(
    AuthMiddleware,
    verification_keys=["your-key"],
    authorization=True,
    scope_mappings={
        # Override default
        "GET /agents": ["custom:agents:list"],
        # Add new endpoint
        "POST /custom/action": ["custom:write"],
        # Allow without scopes
        "GET /public": [],
    }
)
```

<Tip>
  For all available scopes and default endpoint mappings, see [Scopes](/agent-os/security/authorization/scopes).
</Tip>

## User Isolation

RBAC controls which endpoints a caller can hit. User isolation controls which rows they can see and mutate. The two are independent toggles.

```python jwt_with_user_isolation.py theme={null}
app.add_middleware(
    AuthMiddleware,
    verification_keys=["your-jwt-key"],
    authorization=True,
    user_isolation=True,
)
```

When `user_isolation=True`, non-admin callers are scoped to their own `user_id` (from the JWT `sub` claim) for sessions, memory, traces and approvals. Callers holding `admin_scope` bypass isolation. See [Per-User Data Isolation](/agent-os/security/authorization/user-isolation) for the full behavior.

## Excluded Routes

These routes skip JWT and RBAC checks by default:

```python theme={null}
["/", "/health", "/info", "/docs", "/redoc", "/openapi.json", "/docs/oauth2-redirect"]
```

Override them with `excluded_route_paths`:

```python jwt_excluded_routes.py theme={null}
app.add_middleware(
    AuthMiddleware,
    verification_keys=["your-key"],
    excluded_route_paths=[
        "/health",
        "/auth/login",
        "/auth/register",
        "/public/*",  # Wildcards supported
    ]
)
```

<Warning>
  `excluded_route_paths` replaces the defaults. Re-include any default routes you want to keep.
</Warning>

## Configuration Options

See the [AuthMiddleware reference](/reference/agent-os/jwt-middleware) for the complete list of configuration options.

### Authentication Options

| Parameter                  | Description                                                                                                                                                | Default                        |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| `verification_keys`        | List of keys for JWT verification. For RS256, use public keys. For HS256, use shared secrets. Each key is tried in order until one succeeds.               | `JWT_VERIFICATION_KEY` env var |
| `jwks_file`                | Path to a static JWKS file containing public keys. Keys are matched by `kid` from the JWT header.                                                          | `JWT_JWKS_FILE` env var        |
| `secret_key`               | **(Deprecated)** Use `verification_keys` instead.                                                                                                          | -                              |
| `algorithm`                | JWT algorithm (RS256, HS256, ES256, etc.)                                                                                                                  | `"RS256"`                      |
| `validate`                 | Enable token validation                                                                                                                                    | `True`                         |
| `security_key`             | Static OS security key credential. Only consulted when no JWT source is configured.                                                                        | `None`                         |
| `service_account_verifier` | Verifier for `agno_pat_` service-account tokens. Falls back to `app.state.service_account_verifier`, which AgentOS sets whenever a database is configured. | `None`                         |

<Note>
  Constructing the middleware requires at least one credential source: a JWT key source (`verification_keys`, `jwks_file`, or their environment variables), `validate=False`, a `security_key`, or a `service_account_verifier`. `authorization=True` also requires a JWT source (verification keys, a JWKS file, or `validate=False` for unverified dev mode).
</Note>

### Token Source Options

| Parameter          | Description                                                | Default              |
| ------------------ | ---------------------------------------------------------- | -------------------- |
| `token_source`     | Where to extract token from: `HEADER`, `COOKIE`, or `BOTH` | `TokenSource.HEADER` |
| `token_header_key` | Header key for Authorization (when using HEADER or BOTH)   | `"Authorization"`    |
| `cookie_name`      | Cookie name (when using COOKIE or BOTH)                    | `"access_token"`     |

### Claim Extraction Options

| Parameter              | Description                                             | Default        |
| ---------------------- | ------------------------------------------------------- | -------------- |
| `user_id_claim`        | JWT claim for user ID                                   | `"sub"`        |
| `session_id_claim`     | JWT claim for session ID                                | `"session_id"` |
| `scopes_claim`         | JWT claim for scopes                                    | `"scopes"`     |
| `audience_claim`       | JWT claim for audience/OS ID                            | `"aud"`        |
| `dependencies_claims`  | List of claims to extract for `dependencies` parameter  | `None`         |
| `session_state_claims` | List of claims to extract for `session_state` parameter | `None`         |

### Authorization Options (RBAC)

| Parameter              | Description                                                                                                                                                                                                                                                                    | Default                                 |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------- |
| `authorization`        | Enable RBAC scope checking. Auto-enabled when `scope_mappings` is provided.                                                                                                                                                                                                    | `None`                                  |
| `verify_audience`      | Verify `aud` claim matches AgentOS ID                                                                                                                                                                                                                                          | `False`                                 |
| `audience`             | Expected audience claim to validate against the token's audience claim                                                                                                                                                                                                         | `AgentOS ID`                            |
| `user_isolation`       | Opt in to per-user data isolation. When `True`, AgentOS uses the JWT `sub` claim as the `user_id` for every non-admin caller: reads are scoped to it and writes are coerced to it, so callers can't see or persist other users' rows. Callers holding `admin_scope` bypass it. | `False`                                 |
| `scope_mappings`       | Custom route-to-scope mappings (additive to defaults)                                                                                                                                                                                                                          | `None`                                  |
| `admin_scope`          | Scope that grants full admin access                                                                                                                                                                                                                                            | `"agent_os:admin"`                      |
| `excluded_route_paths` | Routes to skip JWT/RBAC checks                                                                                                                                                                                                                                                 | See [Excluded Routes](#excluded-routes) |

## Examples

<CardGroup cols={2}>
  <Card title="JWT with Headers" icon="shield" href="/agent-os/usage/middleware/jwt-middleware">
    JWT authentication using Authorization headers for API clients.
  </Card>

  <Card title="JWT with Cookies" icon="cookie" href="/agent-os/usage/middleware/jwt-cookies">
    JWT authentication using HTTP-only cookies for web applications.
  </Card>

  <Card title="Custom FastAPI + JWT" icon="code" href="/agent-os/usage/middleware/custom-fastapi-jwt">
    Custom FastAPI app with JWT middleware and AgentOS integration.
  </Card>

  <Card title="Authorization" icon="lock" href="/agent-os/security/authorization/overview">
    Scopes, roles, and access control configuration.
  </Card>

  <Card title="AuthMiddleware Reference" icon="code" href="/reference/agent-os/jwt-middleware">
    Complete auth middleware class reference.
  </Card>
</CardGroup>

## External Resources

<CardGroup cols={2}>
  <Card title="PyJWT Documentation" icon="book" href="https://pyjwt.readthedocs.io/">
    Official PyJWT library documentation for JWT encoding and decoding.
  </Card>
</CardGroup>
