# `JwtClaims`

A token's registered claims (RFC 7519 §4.1), once `crypto.verify_jwt` has checked its signature, times, issuer and audience: `aud` is a list, and `exp` always there.

[Security](types.md#security); not ordered; since Jadpo 0.1.0.

## Make one

Build one from its fields, `JwtClaims { aud: ...  exp: ... }`.

This operation gives one: `crypto.verify_jwt`.

## Fields

| Field | Type |
|---|---|
| `iss` | `Text?` |
| `sub` | `Text?` |
| `aud` | `List<Text>` |
| `exp` | [`Instant`](type-instant.md) |
| `nbf` | [`Instant`](type-instant.md)`?` |
| `iat` | [`Instant`](type-instant.md)`?` |
| `jti` | `Text?` |

## Text, JSON and storage

It has no single text form, so `text.from` and `text.parse` don't take it. JSON sends an object of its parts.

| JSON |
|---|
| `{"iss":"https://id.example.com","sub":"member-42","aud":["clinic"],"exp":"2026-10-18T10:00:00Z","nbf":"2026-10-18T09:00:00Z","iat":"2026-10-18T09:00:00Z","jti":"a1b2c3"}` |
| `{"iss":"https://id.example.com","aud":[],"exp":"2026-10-18T10:00:00Z"}` |
| `{"aud":[],"exp":"2026-10-18T10:00:00Z"}` |

It is stored in 7 columns, each named for the field, `_` and its suffix. This is `{"iss":"https://id.example.com","aud":[],"exp":"2026-10-18T10:00:00Z"}`:

| Column | Type | Holds |
|---|---|---|
| `<field>_iss` | `text` | `'https://id.example.com'` |
| `<field>_sub` | `text` | `NULL::text` |
| `<field>_aud` | `text[]` | `'{}'::text[]` |
| `<field>_exp` | `timestamptz(3)` | `'2026-10-18 10:00:00.000000+00'::timestamptz` |
| `<field>_nbf` | `timestamptz(3)` | `NULL::timestamptz` |
| `<field>_iat` | `timestamptz(3)` | `NULL::timestamptz` |
| `<field>_jti` | `text` | `NULL::text` |

## Shown to people

`text.format(value, locale:)` shows it to people in their locale.

With `locale: Locale.en_gb`:

| Value | Shown |
|---|---|
| `{"iss":"https://id.example.com","sub":"member-42","aud":["clinic"],"exp":"2026-10-18T10:00:00Z","nbf":"2026-10-18T09:00:00Z","iat":"2026-10-18T09:00:00Z","jti":"a1b2c3"}` | `member-42` |
| `{"iss":"https://id.example.com","aud":[],"exp":"2026-10-18T10:00:00Z"}` | `https://id.example.com` |
| `{"aud":[],"exp":"2026-10-18T10:00:00Z"}` | `2026-10-18T10:00:00Z` |

## Operations

- `crypto.verify_jwt(token: Jwt, keys: List<Jwk>, issuer: Text?, audience: Text?, as: Type?) -> JwtClaims`: The registered claims of `token` once it holds: signed by one of `keys` with that key's own algorithm (Ed25519, RS256 for an RSA key, ES256 for a P-256 key), never the one the token names, so `none` and every other algorithm fail `invalid_signature`; not expired, not used before its `nbf` and not issued later than now, against the clock with 30 seconds' leeway (`expired`, `not_yet_valid`); issued by `issuer` when one is given (`wrong_issuer`); and for `audience`, or for no audience when none is given (`wrong_audience`). A token that isn't one, or has no `exp`, is `malformed`. With `as:`, an object type of the application's, it gives instead the token's claims the type names, read as `json.read` reads it, a registered one as `JwtClaims` holds it, and fails `mismatch` when they aren't the type. It reads the clock, so it is called only in actions.

Like every standard type, it also has the contract's own operations: `text.format`, `json.read`, `json.write` and `==`. It isn't ordered, so `<` and sorting don't take it. [Every standard type meets one contract](../topics/every-standard-type-meets-one-contract.md) says how they work.
