jti (JWT ID) Claim in a JWT
jti is a unique identifier for one token. It exists so a verifier can tell two tokens apart, which is what makes single use tokens and revocation lists possible without inspecting the rest of the payload.
At a glance
- Claim
- jti (JWT ID)
- Defined in
- RFC 7519 section 4.1.7
- Value type
- String, case sensitive, compared as an opaque value
- Required
- Optional in RFC 7519. Required in an RFC 9068 JWT access token.
- Sample value
- 9a8f2c14-0b3d-4e77-9c2a-5f1e8d0b7a63
- In the decoder
- Explained by name, listed under Registered claims as JWT ID with its raw value.
In detail
RFC 7519 asks for a value assigned so that the probability of the same identifier being given to a different token is negligibly small, and says a collision across issuers must also be guarded against if the token may reach a consumer that accepts several issuers. A version 4 UUID or a long random string satisfies both. A counter satisfies neither and quietly publishes your issuance rate.
The claim only becomes useful when something stores it. Replay detection means recording every jti seen and rejecting a repeat. A revocation list means recording the identifiers you want to refuse. Both reintroduce server side state to a design chosen partly to avoid it, which is the honest cost, and both only need to keep a record until the token's own exp has passed.
That bounded storage is the detail people get wrong in both directions. Discarding records too early opens the replay window again. Keeping them forever turns a list that should shrink into one that grows without limit. Tie the retention to exp plus whatever clock leeway you allow, and the working set stays proportional to how many tokens are alive at once.
The decoder lists jti under Registered claims as JWT ID and prints the value as it appears. It cannot tell you whether that identifier has been used before, because it has no record of anything: the token is decoded in your browser and nothing is uploaded, so there is no server side history for it to compare against.
Examples
{
"jti": "9a8f2c14-0b3d-4e77-9c2a-5f1e8d0b7a63",
"sub": "5f8d0c1e-9a2b-4c3d-8e7f-1a2b3c4d5e6f",
"iat": 1735689600,
"exp": 1735690200
}Ten minute lifetime, so the replay record for this jti can be dropped ten minutes after issuance.
Things that catch people out
- Sequential or timestamp based identifiers are guessable and leak volume. Use a random value with enough entropy that a collision is not worth thinking about.
- A revocation list with no expiry policy grows forever. Purge each entry once the token's own exp has passed, since the token is refused on exp after that anyway.
- Assuming jti alone stops replay. Nothing is checked unless your code writes the identifier down and looks it up on the next request.
Try it: JWT Decoder
Paste a token to decode the header and payload, read the claims and expiry, and verify the signature in your browser. Nothing is uploaded.
JWT Decoder
LOCAL · Browser-OnlyDecode and inspect JSON Web Tokens without sending them to a server
Related JWT claims and algorithms
All jwt claims and algorithms →exp (Expiration Time) Claim in a JWT
The exp claim sets when a JWT stops being accepted. NumericDate format, clock skew allowances, and the four expiry states the decoder shows.
iss (Issuer) Claim in a JWT
The iss claim names who issued a token. What goes in it, why validators compare it byte for byte, and how the decoder displays it.
cnf (Confirmation) Claim in a JWT
The cnf claim binds a token to a key its holder must prove. How mTLS and DPoP use it, why it stops replay of a stolen token, and decoder limits.
azp (Authorized Party) Claim in a JWT
The azp claim names the client an ID token was issued to. When OpenID Connect requires it, how it differs from aud, and what the decoder shows.
nonce Claim in an OpenID Connect ID Token
The nonce claim binds an ID token to one authentication request. How the value round trips, why it must be compared, and how the decoder shows it.
auth_time Claim in an OpenID Connect ID Token
The auth_time claim records when the end user actually authenticated, not when the token was issued. How max_age uses it and how the decoder shows it.
Tools for this
- JWT Decoder Free online JWT decoder - decode and inspect JSON Web Tokens without sending them to a server