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

# Security & Auth

> Build a layered security model: authenticate every request, use scopes and roles for permissions, and isolate data between users.

AgentOS security works in layers. Each one stops a different class of attack, from unauthenticated requests at the edge to cross-user data access in the database. AgentOS gives you good defaults at every layer.

<Note>
  Network-layer controls (rate limiting, WAF, IP allowlists, mTLS) live at your reverse proxy or API gateway layer.
</Note>

## Authentication

A production AgentOS sits behind JWT-validating middleware. Tokens can come from the [AgentOS control plane](https://os.agno.com), your own backend, or a third-party identity provider. Your service verifies them with the matching public key. See [Self-Hosted](/agent-os/security/authorization/self-hosted) for BYO and third-party setup.

```python theme={null}
from agno.os import AgentOS

agent_os = AgentOS(
    agents=[agent],
    db=db,
    authorization=True,  # reads JWT_VERIFICATION_KEY from env
)
```

`authorization=True` drives both layers:

* **Authentication** (this section): requires a valid JWT on every request.
* **Authorization** (below): enforces the token's scopes per endpoint.

A small set of public routes are exempt from the JWT requirement: `/`, `/health`, `/info`, `/docs`, `/redoc`, `/openapi.json`, `/docs/oauth2-redirect`.

### Generate a Verification Key from the Control Plane

<Steps>
  <Step title="Toggle JWT authorization">
    Open [os.agno.com](https://os.agno.com) → **Add OS** → **Live** → paste your URL. Enable JWT authorization when connecting a new AgentOS, or later from the OS Settings page.
  </Step>

  <Step title="Copy the public key">
    Copy the public key for your AgentOS from the modal.
  </Step>

  <Step title="Set the verification key">
    Set the `JWT_VERIFICATION_KEY` environment variable to your public key in your `.env` file or export it directly in your terminal:

    ```bash theme={null}
    export JWT_VERIFICATION_KEY="your-public-key"
    ```

    Or, if you manage keys via a JWKS file, point AgentOS at it instead:

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

When tokens are issued by the AgentOS control plane, it keeps the private key and your service only sees the public key. See [Generate a Verification Key from the Control Plane](/agent-os/security/authorization/quickstart#generate-a-verification-key-from-the-control-plane) for the full walkthrough.

### Configure JWTs from Your Backend or IDP

If you're issuing JWTs from your own backend, or from a third-party identity provider like WorkOS, Auth0, or Okta, pass an `AuthorizationConfig`:

```python theme={null}
from agno.os import AgentOS
from agno.os.config import AuthorizationConfig

agent_os = AgentOS(
    agents=[agent],
    db=db,
    authorization=True,
    authorization_config=AuthorizationConfig(
        verification_keys=["public-key-1", "public-key-2"],   # multi-issuer
        algorithm="RS256",
        verify_audience=True,
        audience="my-agent-os",   # must match the token's `aud`; defaults to the AgentOS id
        user_isolation=True,      # see below
    ),
)
```

`verification_keys` is a list. AgentOS tries each key in order until one verifies the token, so you can accept tokens from multiple issuers at the same time. For key rotation, use a JWKS file instead.

With `verify_audience=True`, AgentOS rejects tokens whose `aud` claim doesn't match the expected audience. That expected value defaults to the AgentOS `id`; set `audience` to override it when your provider mints a different value.

JWT claim names (`scopes`, `sub`) are configured on the JWT middleware itself, not on `AuthorizationConfig`. The defaults (`scopes` for the scopes claim, `sub` for the user id claim) match the tokens minted by the control plane.

For the full self-hosted setup including multi-issuer, see [Self-Hosted](/agent-os/security/authorization/self-hosted).

## Authorization

Every JWT carries a claim listing the caller's permissions (`scopes` by default; configurable via `scopes_claim` if your provider uses a different name, like WorkOS's `permissions`). Endpoints are gated on those scopes.

| Scope             | Grants                                                   |
| ----------------- | -------------------------------------------------------- |
| `agents:read`     | List agents and read their config                        |
| `agents:<id>:run` | Run a specific agent                                     |
| `agents:*:run`    | Run any agent (same pattern for teams, workflows)        |
| `agent_os:admin`  | Full access including session, memory, and trace queries |

The AgentOS control plane mints each token with the appropriate scopes. Scopes are bundled into roles and assigned to users in the control plane: the AgentOS control plane provides three default roles (owner, admin, member), and custom roles are available on Enterprise. Self-hosters define roles in their identity provider or backend. See the [scope reference](/agent-os/security/authorization/scopes#scope-reference) for the full scope list, [Default Roles](/agent-os/security/authorization/roles#default-roles) for what each grants, and [Custom Roles](/agent-os/security/authorization/roles#custom-roles) to compose your own.

## Request isolation

Every request gets a fresh copy of the agent, team, or workflow it's hitting. AgentOS calls `deep_copy()` on the registered component per request, so mutable state (session-scoped variables, tool execution context, run metadata) never bleeds between concurrent calls.

Heavy resources (the DB connection, the model client, MCP tool handles) are shared by reference; only the mutable per-run state is isolated. You get cheap concurrency without the footgun of two requests racing on the same in-memory agent instance.

This is on by default for every run endpoint. There's nothing to configure.

## User isolation

Per-user data isolation is **opt-in**. Authorization stays in force without it, but routes operate on the unscoped DB. A caller with `agents:my-agent:run` could read another user's sessions if they know the IDs. For multi-tenant deployments, turn it on:

```python theme={null}
from agno.os.config import AuthorizationConfig

agent_os = AgentOS(
    agents=[agent],
    db=db,
    authorization=True,
    authorization_config=AuthorizationConfig(
        verification_keys=[public_key],
        user_isolation=True,   # requires authorization=True; the user_id comes from the JWT sub
    ),
)
```

With `user_isolation=True`, every non-admin caller gets:

| Guarantee                | How                                                                                                                           |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| **No cross-user reads**  | The JWT `sub` is threaded as `user_id` on every user-scoped read (sessions, memory, traces). Callers only see their own rows. |
| **No cross-user writes** | `user_id` is coerced on every write, so a non-admin can't persist a session, memory, or trace attributed to another user.     |
| **Run ownership**        | Cancel, resume, and continue routes require `session_id` and verify the run belongs to the caller's session.                  |
| **WebSocket reconnect**  | Reconnecting to a workflow run requires `session_id` and `workflow_id`, then verifies the caller owns the run.                |

Admin callers (whoever holds the configured `admin_scope`, defaulting to `agent_os:admin`) bypass all of the above and see the full unscoped view: service accounts, internal tooling, the control plane.

Per-user isolation requires a database that records `user_id` (PostgreSQL recommended for production).

## Defaults

| Concern           | Default behavior                                                                                   |
| ----------------- | -------------------------------------------------------------------------------------------------- |
| Authentication    | `authorization=False`. Opt in with `authorization=True` and `JWT_VERIFICATION_KEY` for production. |
| Request isolation | On. Every run endpoint deep-copies the component.                                                  |
| User isolation    | **Off**. Opt in via `AuthorizationConfig(user_isolation=True)` for multi-tenant.                   |

See the [AuthorizationConfig reference](/reference/agent-os/authorization-config) for all configuration options and their defaults.
