JWT anatomy
A JWT is three Base64URL-encoded segments joined by dots:- header — algorithm and key metadata. NEAR Auth’s guards expect
alg: "RS256"and use thekid(key id) to identify which signing key was used. - payload — the JSON claims describing the user and the request (see below).
- signature — an RSA signature over the ASCII string
header.payload(the first two segments, dots included, exactly as they appear in the token).
Tokens are capped at 7168 bytes (7 KB). A JWT larger than that is rejected before any cryptography runs, which bounds gas usage and guards against oversized-input abuse.
The claims
The guard deserializes the payload into a small, strict set of standard claims:| Claim | Type | Meaning |
|---|---|---|
sub | string | Subject — the stable, unique identifier for the authenticated user. Must be non-empty; it becomes the identity component of the MPC derivation path. |
iss | string | Issuer — the identity provider that minted the token. Must match the issuer the guard is configured for. |
exp | number | Expiry, as a UNIX timestamp in seconds. The token is rejected once the current block time passes it. |
nbf | number (optional) | Not-before, as a UNIX timestamp in seconds. The token is rejected while the current block time is still below it. |
aud | string or array | Audience — who the token is intended for. Used by the Auth0 guard (see custom claims). |
The fatxn claim
Alongside the standard claims, NEAR Auth relies on one custom claim:
The encoded transaction or delegate action the user is authorizing, embedded into the JWT at login-to-sign time.
fatxn claim is what binds a token to a specific action. When a user requests a signature, the provider asks the identity provider to mint a JWT whose fatxn claim is the Borsh-encoded transaction (or delegate action) payload. Because fatxn is signed as part of the token, an attacker cannot swap in a different transaction after the fact — the RSA signature would no longer verify.
The guard compares fatxn against the sign_payload passed into the on-chain call. Only if they are byte-for-byte identical does verification proceed, guaranteeing that the transaction the MPC network signs is exactly the one the identity provider attested to.
On-chain verification
Guards implement a sharedJwtGuard trait whose internal_verify routine runs the full check in two phases: first the RSA signature, then the claims. If either phase fails, the whole verification fails and no signature is ever produced.
Signature verification (RS256)
Recompute
header.payload, then verify the RSA signature against every configured public key. If any key validates the signature, the token’s signature is accepted.RS256 signature check
RS256 means RSASSA-PKCS1-v1_5 with SHA-256. The guard verifies it from raw key components — the modulus n and exponent e — using 2048-bit keys:
- Hash the signed message with SHA-256.
- Interpret the signature,
n, andeas big integers at 2048-bit precision. - Reject the signature up front unless it is strictly less than the modulus
n. - Recover the padded message by computing
signature^e mod nvia square-and-multiply modular exponentiation. - Validate the recovered bytes against the PKCS#1 v1.5 layout —
0x00 || 0x01 || PS || 0x00 || DigestInfo || Hash, wherePSis at least eight0xFFbytes — and confirm the trailing hash equals the SHA-256 digest from step 1.
The public key struct
Each configured key is stored as its raw big-endian RSA components:Standard claim checks
Once the signature is trusted, the guard validates the claims against the current block time (block_timestamp_ms converted to seconds):
- Expiry — fails with
Token expiredifexpis at or before now. - Not-before — fails with
Token not yet validifnbfis in the future. - Issuer — fails with
Invalid issuerifissdoes not match the guard’s configured issuer.
sub claim, which the NEAR Auth contract uses to derive the user’s MPC key.
Guard-specific custom claims
Standard claim checks are common to all guards, but each guard also enforces its own custom claims before the standard ones. For the Auth0 guard this means:aud— the token’s audience must contain the guard’s own account id, so a token minted for a different audience can’t be replayed against it.fatxn— the embedded transaction payload must exactly equal thesign_payloadsupplied to the on-chain call.
fatxn binding, however, is universal: a guard never authorizes a transaction it wasn’t explicitly presented with.
Next steps
Auth0
How Auth0 issues the JWTs NEAR Auth verifies, and which login methods it supports.
Auth0 guard
The on-chain contract that runs this verification and manages its RSA keys.