# Tokens and claims

Look up each token a project issues, its lifetime, signing algorithm and claims, and how to validate one.

A project issues several kinds of token. Each is signed with one of the project's keys, published at `https://acme.authfi.io/.well-known/jwks.json`.

## Token types

A claim is a named value inside a token. A JWT is a signed token whose claims any holder can read and any service can verify.

| Token                                     | Issued to                               | Signed with         | Header `typ`    | Lifetime                      |
| ----------------------------------------- | --------------------------------------- | ------------------- | --------------- | ----------------------------- |
| [ID token](#id-token)                     | An application, after a person signs in | `RS256`             | `JWT`           | 15 minutes by default         |
| [Access token](#access-token)             | An application or a service             | `RS256`             | `at+jwt`        | 15 minutes by default         |
| [Refresh token](#refresh-token)           | An application, after a person signs in | Not a JWT           |                 | 30 days by default, used once |
| [Agent access token](#agent-access-token) | An AI agent, after it signs in          | `EdDSA`             | `at+jwt`        | 15 minutes by default         |
| [Transaction token](#transaction-token)   | An agent, for one action                | `EdDSA`             | `txn_token+jwt` | 60 seconds                    |
| [Client assertion](#client-assertion)     | Made by the agent itself, to sign in    | The agent's own key | `JWT`           | At most 5 minutes, used once  |

The ID token has the same lifetime as the access token.

## ID token

Tells an application who signed in. It is returned by the [token endpoint](/reference/oauth#token) when the scope includes `openid`. See [Add sign-in to an app](/quickstart/sign-in).

```json
{
  "iss": "https://acme.authfi.io",
  "sub": "ada@acme.com",
  "aud": "<your client ID>",
  "email": "ada@acme.com",
  "email_verified": true,
  "name": "Ada Lovelace",
  "groups": ["engineering"],
  "roles": ["developer"],
  "auth_time": 1760000000,
  "iat": 1760000000,
  "exp": 1760000900
}
```

| Claim            | Description                                                                      |
| ---------------- | -------------------------------------------------------------------------------- |
| `iss`            | The project's issuer                                                             |
| `sub`            | The person's identifier in this project                                          |
| `aud`            | The client ID the token was issued to                                            |
| `exp`, `iat`     | When it expires, and when it was issued                                          |
| `auth_time`      | When the person last signed in                                                   |
| `nonce`          | The `nonce` your application sent, unchanged                                     |
| `email`          | The person's email                                                               |
| `email_verified` | Whether the email is verified                                                    |
| `name`           | The person's name                                                                |
| `groups`         | The groups assigned to the application, or shared with it, that the person is in |
| `roles`          | The roles the person holds                                                       |

The ID token carries `email`, `email_verified`, `name`, `groups` and `roles` whatever scopes you request. Scopes decide what [userinfo](/reference/oauth#userinfo) returns. See [Scopes](/reference/oauth#scopes).

`groups` lists only the groups assigned to the application or shared with it, not every group the person belongs to. See [Application types](/applications/types).

## Access token

Lets an application or a service call an API. It follows the JWT access token profile (RFC 9068). See [Sessions](/applications/sessions) and [Service to service](/applications/machine-to-machine).

| Claim                            | Description                                                                                       |
| -------------------------------- | ------------------------------------------------------------------------------------------------- |
| `iss`                            | The project's issuer                                                                              |
| `sub`                            | The person, or for a service its own client ID                                                    |
| `aud`                            | A list. For a person, the client ID it was issued to. For a service, the API named in `resource`. |
| `client_id`                      | The application the token was issued to                                                           |
| `scope`                          | The scopes granted, space-separated. For a service, the scopes it asked for, unchanged.           |
| `roles`, `permissions`, `groups` | What the person holds                                                                             |
| `sid`                            | The session the token came from                                                                   |
| `amr`, `acr`                     | How the person signed in. See [`acr` and `amr` values](#acr-and-amr-values).                      |
| `auth_time`                      | When the person last signed in                                                                    |
| `jti`                            | A unique identifier for this token                                                                |
| `iat`, `exp`                     | When it was issued, and when it expires                                                           |

`sid`, `amr` and `acr` describe a person's sign-in. A service's token has none of them, because nobody signed in.

An access token carries no name or email. Read those from the ID token or from [userinfo](/reference/oauth#userinfo).

### `acr` and `amr` values

`acr` is the sign-in strength reached. An application asks for one with `acr_values` at the [authorization endpoint](/reference/oauth#authorize). Several values, separated by spaces, mean any of these, or stronger. See [Sessions](/applications/sessions).

| `acr`                        | The person                                  |
| ---------------------------- | ------------------------------------------- |
| `urn:authfi:acr:1fa`         | Signed in                                   |
| `urn:authfi:acr:mfa`         | Completed multi-factor authentication (MFA) |
| `urn:authfi:acr:passkey`     | Signed in with a passkey                    |
| `urn:authfi:acr:passkey_mfa` | Signed in with a passkey and completed MFA  |

`amr` is the list of methods the person used.

| `amr` value | Method                                                 |
| ----------- | ------------------------------------------------------ |
| `pwd`       | A password                                             |
| `otp`       | A one-time code                                        |
| `mfa`       | More than one method                                   |
| `hwk`       | A key held in hardware, such as a passkey              |
| `user`      | The passkey verified the person (a PIN or biometric)   |
| `fed`       | [Another identity provider](/connections/sign-in-with) |

### Custom claims

A claim that a webhook action returns is added to the token under `custom`. See [Actions at sign-in](/extend/actions).

```json
{ "sub": "ada@acme.com", "custom": { "plan": "enterprise", "cost_center": "R-204" } }
```

## Refresh token

Gets new tokens without another sign-in, at the [token endpoint](/reference/oauth#refresh-token-grant). It is not a JWT. See [Refresh tokens](/applications/sessions#keep-a-person-signed-in).

| Property  | Value                                                                                                                |
| --------- | -------------------------------------------------------------------------------------------------------------------- |
| Lifetime  | 30 days by default                                                                                                   |
| Use       | Once. Every use returns a new refresh token.                                                                         |
| Issued to | Every application that holds a secret. An application without one, when the project allows it, which is the default. |
| Reuse     | Presenting a used refresh token revokes every refresh token from the same sign-in and returns `invalid_grant`        |
| Revoke    | At the [revocation endpoint](/reference/oauth#revoke-a-token)                                                        |

## Agent access token

Lets an AI agent call a service. It is returned by the agent token endpoint, `/apis/regional/v1/ai/auth/token`, and follows RFC 9068. An agent has no refresh token: when the access token expires, it signs a new client assertion. See [Give an AI agent an identity](/quickstart/agent) and [Accept agents at your own API](/agents/verify).

| Claim               | Description                                                                         |
| ------------------- | ----------------------------------------------------------------------------------- |
| `iss`               | The project's issuer                                                                |
| `sub`               | The agent, or the person it is acting for                                           |
| `client_id`         | The agent ID, `agt_…`. Always the agent.                                            |
| `aud`               | Where the token may be used, from `resource` in the request                         |
| `agent_type`        | `delegated` or `autonomous`                                                         |
| `owner_user_id`     | The person who answers for the agent, when it has an owner                          |
| `approval_group_id` | The group that answers for the agent, when it has one                               |
| `act`               | Present when the agent acts for a person. `act.sub` is the agent, as `agent:agt_…`. |
| `capabilities`      | The names of the capabilities the agent was granted                                 |
| `scope`             | The same grants, as a space-separated string                                        |
| `roles`, `groups`   | The agent's own roles and groups, when it has any                                   |
| `tenant_id`         | The project's internal identifier                                                   |
| `jti`               | A unique identifier for this token                                                  |
| `iat`, `exp`        | When it was issued, and when it expires                                             |

`capabilities` holds names only. AuthFI keeps the limits on each capability and checks them when the agent acts. See [Limit what an agent may do](/agents/limits).

A token requested with no `resource` is issued for the project's MCP gateway.

`act` tells the two cases apart.

| Token              | `sub`      | `act.sub`     | Meaning                                                         |
| ------------------ | ---------- | ------------- | --------------------------------------------------------------- |
| Agent on its own   | `agt_…`    | Absent        | The agent acts as itself                                        |
| Agent for a person | The person | `agent:agt_…` | The agent acts for that person, who [approved it](/agents/ciba) |

In a token issued after a person approves through CIBA, `scope` is what the person approved, and `exp` is 15 minutes after issue.

## Transaction token

Binds an agent to one action and its details. The agent exchanges its access token for one before a sensitive action. See [Ask before a sensitive action](/agents/approvals).

```json
{
  "typ": "txn_token+jwt",
  "sub": "agt_7f3a9c21",
  "txn": "0b7d6a52-…",
  "azd": {
    "action": "transfer:accounts",
    "resource": "account:4821",
    "context": { "amount": 1200, "currency": "USD", "account_type": "business" }
  }
}
```

| Claim               | Description                                         |
| ------------------- | --------------------------------------------------- |
| `iss`, `aud`, `exp` | As for the agent access token                       |
| `typ`               | `txn_token+jwt`                                     |
| `sub`               | The same agent as the access token                  |
| `txn`               | The identifier of this transaction                  |
| `azd.action`        | The action the token is for                         |
| `azd.resource`      | What the action is done to                          |
| `azd.context`       | The details of the action, fixed when it was issued |

## Client assertion

Proves an agent's identity at the agent token endpoint. The agent creates and signs it with its own private key. This is `private_key_jwt` (RFC 7523). See [Give an AI agent an identity](/quickstart/agent).

| Part            | Value                               |
| --------------- | ----------------------------------- |
| Header `alg`    | `EdDSA`                             |
| Header `kid`    | The key ID registered for the agent |
| `iss` and `sub` | The agent ID                        |
| `aud`           | The token endpoint's full URL       |
| `jti`           | A new random value every time       |
| `exp`           | At most 5 minutes after `iat`       |

An assertion is accepted once. Build a new one for every request.

## Other signed tokens

A project signs these tokens for one purpose each. The page that uses each token lists its claims.

| Token               | Signed with        | Lifetime                | Used in                               |
| ------------------- | ------------------ | ----------------------- | ------------------------------------- |
| Webhook action call | `RS256`            | `exp` within 60 seconds | [Actions at sign-in](/extend/actions) |
| Cloud role token    | `RS256`            |                         | [Cloud roles](/access/cloud)          |
| Revocation event    | The project's keys |                         | [Stop an agent](/agents/revocation)   |

## Checking a token

A service validates a JWT itself, without a call to AuthFI. Most JWT libraries make these checks when you configure them with the issuer, the keys URL, the audience and the algorithm.

1. Verify the signature with the key whose `kid` matches the token's header.
2. Check that `iss` is the issuer of the project you trust.
3. Check that `aud` names this service. Reject a token issued for another service.
4. Check that `exp` is in the future.
5. For a bearer token, check that the header `typ` is `at+jwt`. An ID token has `typ: JWT` and is not a bearer credential.

| Setting   | ID token                 | Access token                                                                 | Agent access token       |
| --------- | ------------------------ | ---------------------------------------------------------------------------- | ------------------------ |
| Issuer    | `https://acme.authfi.io` | `https://acme.authfi.io`                                                     | `https://acme.authfi.io` |
| Keys      | `/.well-known/jwks.json` | `/.well-known/jwks.json`                                                     | `/.well-known/jwks.json` |
| Audience  | Your client ID           | The client ID it was issued to. For a service's token, your API's client ID. | Your API's client ID     |
| Algorithm | `RS256`                  | `RS256`                                                                      | `EdDSA`                  |
| `typ`     | `JWT`                    | `at+jwt`                                                                     | `at+jwt`                 |

For a transaction token, also compare `azd` with the request in front of you, and reject any difference. See [Accept agents at your own API](/agents/verify).

**Keys.** Find the key by `kid`. Cache the key set, and fetch it again when you meet a `kid` you do not know. See [Keys and secrets](/security/keys).

**Revocation.** A signature check accepts a token until it expires. To learn sooner that an agent was revoked, subscribe to [revocation events](/agents/revocation). An application with a secret can also ask the [introspection endpoint](/reference/oauth#check-a-token).

**Sign-in strength.** Before a sensitive action, read `acr` from the token and deny the action if it is weaker than you need.
