# Tokens and claims

> Access, ID and refresh tokens, their claims and lifetimes, scopes and the claims they add, signing keys and algorithms.

Source: https://9in.io/docs/reference/tokens/

## Access tokens

For your API. By default a JWT ([RFC 9068](https://www.rfc-editor.org/rfc/rfc9068)):

```json
// header
{ "alg": "RS256", "kid": "k_8f2a…", "typ": "at+jwt" }
// payload
{
  "iss": "https://acme.9in.io",
  "sub": "usr_8k2…",              // the user, or the client ID for client-credentials tokens
  "aud": "https://api.acme.com",  // the API (the client ID when no audience was requested)
  "client_id": "app_7f3k2m9q",
  "scope": "openid profile orders:read",
  "iat": 1794200000,
  "exp": 1794203600,
  "jti": "…",
  "sid": "…",                     // the session (absent for client credentials)
  "auth_time": 1794199000
}
```

**Opaque access tokens** (`9in_at_…` followed by random characters) carry nothing: check them with [introspection](https://9in.io/docs/reference/endpoints/#post-oauth-introspect). Choose the format per tenant (**Tokens & Keys**), per API, or per application for tokens without an API.

Access tokens don't include profile claims or `amr`; get those from the ID token or `/oauth/userinfo`.

## ID tokens

For your app: who signed in and how. A JWT with `typ: JWT`, always signed with the tenant's **default key** (asymmetric).

| Claim | |
|---|---|
| `iss`, `sub`, `aud` (your client ID), `azp`, `iat`, `exp` | |
| `auth_time` | When the user signed in (unix seconds) |
| `nonce` | From the authorization request |
| `sid` | The session id |
| `amr` | How the user signed in: `pwd`, `otp`, `social`, `fed` (enterprise), `link` (login link), and `mfa` after a second factor |
| `at_hash` | Hash of the access token issued with it (OIDC) |
| Profile claims | Depending on the scopes (below) |
| Custom fields | Fields marked "include as a claim", with the `profile` scope |

## Refresh tokens

Opaque, issued with `offline_access` when the application has the refresh token grant (and the API allows refresh tokens). They **rotate**: every use returns a new refresh token. Using one that was already used revokes the whole family (`token.reuse_detected` in the activity log). They stop working when their session ends, the user is blocked or deleted, or they're revoked.

## Lifetimes

| | Default | Range | Set per |
|---|---|---|---|
| Access token | 1 hour | 1 minute – 24 hours | Tenant, API, application |
| ID token | 1 hour | 1 minute – 24 hours | Tenant, application |
| Refresh token | 30 days | 5 minutes – 1 year | Tenant, application |
| Authorization code | 60 seconds | 10 seconds – 10 minutes | Tenant |
| Session | 30 days (sliding) | 5 minutes – 1 year | Tenant |

For access tokens, an application's lifetime wins over its API's, which wins over the tenant's.

## Scopes and claims

| Scope | Gives |
|---|---|
| `openid` | An ID token; `sub` |
| `profile` | `name`, `given_name`, `middle_name`, `family_name`, `nickname`, `preferred_username`, `picture`, `website`, `gender`, `locale`, `zoneinfo`, `birthdate`, and custom-field claims |
| `email` | `email`, `email_verified` |
| `phone` | `phone_number`, `phone_number_verified` |
| `offline_access` | A refresh token |
| Your API's scopes | Listed in the access token's `scope` |

Claims with no value are left out.

## Signing keys

Each tenant has a key library (**Tokens & Keys**):

- **Algorithms:** RS256, RS384, RS512, PS256, PS384, PS512 (RSA 2048 or 4096 bits), ES256, ES384, EdDSA (Ed25519), and HS256, HS384, HS512 (shared secrets).
- **Generate** keys, or **import** your own: a PKCS#8 PEM, a private JWK, or an HMAC secret.
- The **default key** (asymmetric) signs ID tokens and every access token whose API or application doesn't choose another key. New tenants start with an RS256 key.
- **Per API or application:** any key from the library, so an API can use ES256 or a shared HMAC secret.
- **Rotation:** add a key, make it the default, then **retire** the old one. Retired keys stop signing but stay in the JWKS until you delete them, so tokens signed earlier keep verifying.
- **HMAC keys** are never published; the secret is shown once when it's created. A token signed with one is only accepted for the API or application that uses that key.

Verifiers must use the algorithm of the key named by `kid`, not a fixed list: an API's key can be ES256 or EdDSA.
