WEB / WEB-14.03
JWT claims and token lifecycle
JWT claims are readable representation; trust comes from cryptographic and application validation. Access tokens, refresh tokens, and authenticated sessions do not share interchangeable lifetime decisions.
1. Core · Mechanism
Read every scenario without motion
Expected profile and resource
- Access alias ready
The client will present an access-token alias to the resource API; there are no secret bytes.
- Client
- Access alias; not refresh token
- Resource server
- No request
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Resource request arrived
The receiver obtained the encoded-token alias. Decoded claims remain tentative.
- Client
- Waiting
- Resource server
- Decoded tentative claims; not accepted
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Local cryptographic predicate passes
Allowed alg/key and supplied signatureValid are true; no real cryptography runs. Audience/time decisions remain necessary.
- Client
- Waiting
- Resource server
- Signature predicate passed; policy pending
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Profile and action policy
Issuer, audience, type, required claims, and time pass; the own-note action is separately allowed.
- Client
- Waiting
- Resource server
- ACCEPT: profile valid; action allowed
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Policy: profile valid; action allowed
- Decision reached the client
The client received the own-note representation permitted for subject-duru. After the signature predicate, issuer, audience, token type and time policy passed; the note action was separately allowed. This response grants nothing for another resource.
- Client
- Own note delivered
- Resource server
- Response: allowed
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Policy: profile valid; action allowed
Audience mismatch
- Access alias ready
The client will present an access-token alias to the resource API; there are no secret bytes.
- Client
- Access alias; not refresh token
- Resource server
- No request
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Resource request arrived
The receiver obtained the encoded-token alias. Decoded claims remain tentative.
- Client
- Waiting
- Resource server
- Decoded tentative claims; not accepted
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Local cryptographic predicate passes
Allowed alg/key and supplied signatureValid are true; no real cryptography runs. Audience/time decisions remain necessary.
- Client
- Waiting
- Resource server
- Signature predicate passed; policy pending
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Profile and action policy
Audience does not contain this resource server; a valid signature does not fix the intended use.
- Client
- Waiting
- Resource server
- REJECT: audience mismatch
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Policy: audience mismatch
- Decision reached the client
The client received a token rejection; no private representation was prepared. Although the signature predicate passed, audience did not include this resource server, so token acceptance stopped. Rejection does not mean the signature failed.
- Client
- Token rejected; no private representation
- Resource server
- Response: token invalid
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Policy: audience mismatch
Expiry boundary
- Access alias ready
The client will present an access-token alias to the resource API; there are no secret bytes.
- Client
- Access alias; not refresh token
- Resource server
- No request
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Resource request arrived
The receiver obtained the encoded-token alias. Decoded claims remain tentative.
- Client
- Waiting
- Resource server
- Decoded tentative claims; not accepted
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Local cryptographic predicate passes
Allowed alg/key and supplied signatureValid are true; no real cryptography runs. Audience/time decisions remain necessary.
- Client
- Waiting
- Resource server
- Signature predicate passed; policy pending
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Approved issuer/key/algorithm/profile
- Profile and action policy
Fixture clock 1200 == exp 1200; zero leeway rejects acceptance.
- Client
- Waiting
- Resource server
- REJECT: expiry: now == exp
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Policy: expiry: now == exp
- Decision reached the client
The client received a token rejection; no private representation was prepared. With now=1200, exp=1200 and leeway=0, the expiry boundary has been reached. Positive signature and audience predicates cannot change this time decision.
- Client
- Token rejected; no private representation
- Resource server
- Response: token invalid
- Authorization server
- Idle; no token endpoint call
- Trusted policy / family
- Policy: expiry: now == exp
Stale refresh reuse
- Rotation already completed
The AS already invalidated the old refresh and activated its successor. A stale client component still holds the old alias.
- Client
- Stale component: refresh-old
- Resource server
- Prior access alias not revalidated here
- Authorization server
- No refresh request yet
- Trusted policy / family
- Family: old invalid; current active
- Refresh request reached the AS
The client sends the old alias to the token endpoint, not to the resource server.
- Client
- Waiting for refresh result
- Resource server
- Prior access alias not revalidated here
- Authorization server
- Received: old refresh alias; validation pending
- Trusted policy / family
- Family: old invalid; current active
- Local reuse policy closes the family
The old alias is already invalid. The model revokes the family and issues no new access token; presenter attribution is not conclusive.
- Client
- Waiting for refresh result
- Resource server
- Prior access alias not revalidated here
- Authorization server
- Reuse detected; new access not issued
- Trusted policy / family
- Family revoked; no active successor
- Refresh failure reached the client
Fresh authorization is needed. This does not establish instant revocation of the prior access token at every API.
- Client
- Refresh rejected; fresh authorization needed
- Resource server
- Prior access alias not revalidated here
- Authorization server
- Response: refresh rejected
- Trusted policy / family
- Family revoked; no active successor
ACTORS · STATE · DATA FLOW
JWT is a claims representation
JWT is a family of compact representations carrying claims about a subject. The common signed JWS Compact form has three components: protected header, payload, and signature/MAC, represented with Base64url and separated by periods. The header carries cryptographic metadata such as alg; the payload carries claims.
This lesson focuses on a signed JWS Compact example. JWTs can also use encrypted JWE or nested representations; not every JWT fits a three-part/plaintext-payload description. A signed JWS does not encrypt its payload; signature integrity/authenticity and confidentiality are different needs.
HEADER_ALIAS.PAYLOAD_ALIAS.SIGNATURE_ALIAS is this lesson’s inert shape diagram, not a real JWT. The JSON artifact teaches decoded views. Decoding an external token proves nothing about which key and purpose make it trusted.
Which questions do claim names answer?
iss identifies the issuer, sub the subject, and aud the intended recipients. A subject can be unique within its issuer context; the same sub text across two issuers need not represent the same person. aud can be a string or an array, and recipients need the expected identifier in the audience.
exp marks the boundary after which acceptance is forbidden; nbf marks when acceptance can start. NumericDate values use seconds since the epoch. iat is issuance time, not expiry by itself. jti can support identification/correlation; it does not implement a revoke list or replay protection automatically.
RFC 7519 does not make registered claims mandatory for every application. A profile determines the required set. This fixture selects RFC 9068 for OAuth access tokens: iss, exp, aud, sub, client_id, iat, and jti are required, and typ declares the access-token type. Application-specific claims such as tenant need a producer/consumer contract.
Claims remain tentative after decoding
A parser can read JSON fields, but sub and scope are not yet trusted policy inputs. Cryptographic validation must use the right key, an acceptable algorithm, and every required cryptographic operation. Failure of any required validation rejects the token.
The server configuration supplies the allowed algorithm set; the token’s alg cannot expand it. A trusted configuration/discovery relationship establishes that the key belongs to the expected issuer. kid can select within an approved key set; token metadata does not establish key trust or instruct arbitrary remote fetching.
This access-token profile requires signatures and rejects none. Library verification and algorithm/key policy are both necessary. The model uses a supplied signatureValid boolean with no signature bytes or key material. It does not claim to execute RSA verification.
A valid signature does not establish the intended use
Even after cryptographic validation, enforce exact issuer, intended audience, required claims, token purpose, and time conditions. RFC 9068 requires typ at+jwt or application/at+jwt. Separate an ID token’s different purpose through access-token validation rules; sharing a signed-JWT format does not erase the type boundary.
The fixture expects issuer https://identity.atlas.example and audience https://api.atlas.example. In the wrong-audience branch, a valid signature does not make a token for another recipient acceptable. Resource actions additionally need scope and subject/resource policy.
The expiry condition is now < exp; now == exp is expired under this fixture’s zero-leeway policy. If nbf is present, now >= nbf is required. Actual clock-skew tolerance should be small and explicit; fixture values 1000/1200 are synthetic epoch seconds, not recommended TTLs or measured clock values.
Access and refresh tokens go to different recipients
An OAuth access token is a credential for a protected resource server. A refresh token is presented to the authorization server to obtain a new access token and is not sent to the resource API. Refresh-token issuance is optional, and its format need not be JWT.
Refresh must not create permission beyond the original grant; evaluate client binding, scope/resource boundaries, and validity. Sending a refresh token to the same endpoint as a longer-lived access token violates the purpose boundary.
RFC 9700 requires sender constraint or rotation to detect refresh-token replay for public clients. Rotation invalidates the previous refresh token when a new one is issued and retains the family relationship. Seeing the old token later signals risk without conclusively identifying who presented it; active-family revocation and fresh authorization may be required.
The fixture starts after rotation: refresh-old is invalid and refresh-current active. A stale client component presents the old alias; the AS detects reuse and revokes the family. It does not claim that an already issued access token instantly becomes invalid on every resource server.
Stateless validation does not imply a stateless lifecycle
A self-contained signed access token can let a resource server evaluate signature/claim policy without contacting the issuer on every request. That does not automatically propagate logout, revocation, or permission changes to every server.
Immediate revocation may require trusted state or backend coordination such as a denylist, session generation, or introspection. Short-lived access tokens and a protected refresh lifecycle offer another trade-off. JWT format alone does not select the correct approach.
Deleting a client token is local cleanup; refresh-family revocation at the AS and access-token acceptance at resource servers are separate outcomes. Key rotation is not automatically equivalent to a precise logout primitive for every user session. Design expiry, revocation, and current-authorization expectations together.
2. Red · Trust & Impact
Read every scenario without motion
Wrong audience
- The validation profile was selected
This fixture uses a signed JWS under the RFC 9068 OAuth access-token profile. The shape alias is not an actual encoded JWT; visible decoded claims are not acceptance.
- Token profile
- wrong-audience
- Validation gates
- Claims not evaluated
- Action boundary
- No action grant
- Key and signature boundaries were separated
Allowed RS256, typ at+jwt and the issuer-bound key source come from policy. This branch supplies signatureValid=true; it adds no claim that a cryptographic library ran.
- Token profile
- wrong-audience
- Validation gates
- Signature predicate=true
- Action boundary
- No action grant
- Recipient or purpose was checked
audienceValid=false fails expected audience https://api.atlas.example. True issuer and signature predicates do not authorize a token for another recipient here.
- Token profile
- wrong-audience
- Validation gates
- audienceValid=false
- Action boundary
- No action grant
- Valid time was not treated as sufficient
nowSeconds=1000 and expSeconds=1200 mean this row is not at expiry. Valid time cannot compensate for the failed audience gate.
- Token profile
- wrong-audience
- Validation gates
- Time valid; profile rejected
- Action boundary
- No action grant
- Token acceptance was withheld
tokenAccepted=false and actionAllowed=false remain. Readable sub=subject-duru is not promoted to trusted policy input; no credential generation or header alteration is shown.
- Token profile
- wrong-audience
- Validation gates
- Token accepted=false
- Action boundary
- Action allowed=false
- The authority scope was preserved
This negative case shows the boundary between a valid-signature predicate and recipient mismatch. It establishes no success or failure for another resource, issuer or token profile.
- Token profile
- wrong-audience
- Validation gates
- No profile acceptance
- Action boundary
- No trusted action context
Wrong purpose
- The validation profile was selected
This fixture uses a signed JWS under the RFC 9068 OAuth access-token profile. The shape alias is not an actual encoded JWT; visible decoded claims are not acceptance.
- Token profile
- wrong-purpose
- Validation gates
- Claims not evaluated
- Action boundary
- No action grant
- Key and signature boundaries were separated
Allowed RS256, typ at+jwt and the issuer-bound key source come from policy. This branch supplies signatureValid=true; it adds no claim that a cryptographic library ran.
- Token profile
- wrong-purpose
- Validation gates
- Signature predicate=true
- Action boundary
- No action grant
- Recipient or purpose was checked
purposeValid=false fails the selected access-token profile. Signature success for another token kind does not complete this use’s validation rules.
- Token profile
- wrong-purpose
- Validation gates
- purposeValid=false
- Action boundary
- No action grant
- Valid time was not treated as sufficient
nowSeconds=1000 and expSeconds=1200 mean this row is not at expiry. Valid time cannot compensate for the failed purpose gate.
- Token profile
- wrong-purpose
- Validation gates
- Time valid; profile rejected
- Action boundary
- No action grant
- Token acceptance was withheld
tokenAccepted=false and actionAllowed=false remain. Readable sub=subject-duru is not promoted to trusted policy input; no credential generation or header alteration is shown.
- Token profile
- wrong-purpose
- Validation gates
- Token accepted=false
- Action boundary
- Action allowed=false
- The authority scope was preserved
This negative case shows the boundary between a valid-signature predicate and purpose mismatch. It establishes no success or failure for another resource, issuer or token profile.
- Token profile
- wrong-purpose
- Validation gates
- No profile acceptance
- Action boundary
- No trusted action context
ASSUMPTION · BOUNDARY · CONTROL
Do not confuse readable with trusted claims
In a defensive review, distinguish decoded, cryptographically verified, profile-valid, and action-authorized states. Seeing sub=subject-duru is not identity acceptance. A valid signature does not establish audience/purpose/time acceptance.
The wrong-audience branch retains a positive signature condition but rejects the recipient. The expired branch keeps that condition while rejecting now == exp. Cryptographic correctness and appropriate use are separate questions. Neither rejected-token decision produces a private representation; a present or signed claim cannot open that boundary.
Observe independent lifecycle states
Browser cleanup, AS refresh-family state, and resource-server acceptance are not one variable. Do not infer another component’s current state from logout or refresh failure without evidence.
A refresh-reuse observation does not identify the actor conclusively; concurrency or stale-component behavior can also produce it. Defensive response policy handles the risk without claiming an unproven cause. The artifact preserves this distinction using nonsecret aliases.
3. Blue · Secure development & defense
Read every scenario without motion
Expiry equality
- The expiry record was selected
The expired row has true signature, issuer, audience and purpose predicates. Review focuses on why these positive fields do not automatically satisfy the time gate.
- Token observation
- case=expired
- Policy/lifecycle review
- Time not compared
- Acceptance conclusion
- Acceptance not examined
- NumericDate and clock policy were read
The policy uses clockLeewaySeconds=0. The row supplies nowSeconds=1200 and expSeconds=1200; these are fixture NumericDate values, not the current clock or a recommended lifetime.
- Token observation
- case=expired
- Policy/lifecycle review
- now=1200; exp=1200; leeway=0
- Acceptance conclusion
- Acceptance not examined
- The equality boundary was evaluated
Expiration requires now < exp. At equality the time gate fails; other true predicates do not override this condition.
- Token observation
- case=expired
- Policy/lifecycle review
- Time gate=false
- Acceptance conclusion
- Token accepted=false
- The action outcome was compared
The row also records actionAllowed=false. Token acceptance and resource authorization are distinct decisions; a rejected token does not establish trusted action context.
- Token observation
- case=expired
- Policy/lifecycle review
- Token accepted=false
- Acceptance conclusion
- Action allowed=false
- The integrity selector was read in scope
The selector searches for accepted=true with a false time or other validation gate. The main fixture gives count=0 and a separate positive fixture count=1; this is not actual JOSE verification output.
- Token observation
- case=expired
- Policy/lifecycle review
- Integrity count=0; positive=1
- Acceptance conclusion
- Expiry reason retained
- The time conclusion was not broadened
Expiry denial is known under this profile. A different leeway configuration, clock synchronization or actual cryptographic validity is not added without separate evidence.
- Token observation
- case=expired
- Policy/lifecycle review
- Known expiry at equality
- Acceptance conclusion
- Selected time policy only
Refresh family
- The refresh-lifecycle scope was selected
Correlation grant-ledger-a belongs to the refresh family. refresh-old and refresh-current are not actual tokens; the resource-server outcome for an earlier access alias is separately bounded.
- Token observation
- grant-ledger-a
- Policy/lifecycle review
- Before/after not examined
- Acceptance conclusion
- Resource-server state unknown
- Post-rotation state was read
beforeReuse records refresh-old as invalidated by prior rotation and refresh-current as active. A family relationship does not reveal browser or resource-server state.
- Token observation
- grant-ledger-a
- Policy/lifecycle review
- Old invalidated; current active
- Acceptance conclusion
- Resource-server state unknown
- The stale-presentation outcome was read
The artifact examines stalePresentation=refresh-old. afterReuse has refresh-current revoked and newAccessIssued=false; this policy observation does not attribute the event to an actor.
- Token observation
- grant-ledger-a
- Policy/lifecycle review
- Family revoked; no new access
- Acceptance conclusion
- Resource-server state unknown
- Refresh and access acceptance were separated
resourceServerObservation states that the previous access alias is not revalidated here. Family revocation does not guarantee instant rejection of an access token by every resource server.
- Token observation
- grant-ledger-a
- Policy/lifecycle review
- Refresh state known
- Acceptance conclusion
- UNKNOWN: previous access acceptance
- Cause attribution was withheld
A stale component, parallel legitimate refresh or response loss cannot be established from this family row. The reuse-policy outcome and the event cause require separate evidence.
- Token observation
- grant-ledger-a
- Policy/lifecycle review
- No new access issued
- Acceptance conclusion
- Actor/cause unknown
- Lifecycle evidence stayed with its owner
AS family state is known; browser cleanup and current resource-server acceptance remain UNKNOWN. Logout or client cleanup does not establish completion of the entire token lifecycle.
- Token observation
- grant-ledger-a
- Policy/lifecycle review
- Known: family revoked
- Acceptance conclusion
- UNKNOWN: downstream acceptance
IMPLEMENTATION · SECURITY CONTROL · VERIFICATION
Enforce library and profile boundaries together
Use an established JOSE/JWT library with approved algorithms and an issuer-bound trusted key set. Do not treat a decode helper as a verification API; validate required header/claim shape without turning arbitrary token metadata into trust configuration.
Configure expected issuer, audience, purpose, required claims, and time policy explicitly. Use mutually exclusive validation rules for different token types such as access and ID tokens. Complete resource authorization through trusted policy even after signature validation.
Use atomic rotation in refresh lifecycle
Handle refresh tokens only at the correct AS endpoint with protected transport/storage. Preserve client/grant binding and original scope. When choosing rotation, make invalidate-old, issue-new, and family updates one consistent boundary; do not accidentally create two active successors.
Apply family-revocation policy to stale reuse rather than issuing a fresh access grant. Define recovery for legitimate parallel refresh or response loss in the client/AS contract. Do not present arbitrary grace windows or TTL figures as sourced production advice.
Tie revocation expectations to product behavior
After logout, which access tokens may remain accepted, which store/denylist checks are needed, and when is the refresh family closed? Choosing JWT does not answer those questions. Evaluate failover and cache freshness against current-authorization expectations.
Audit correlation aliases, validation stages, reasons, and relevant issuer/audience/purpose categories rather than raw token strings. A supplied signatureValid teaching predicate and an actual cryptographic-library verification result are different kinds of evidence.
NOW APPLY IT TO THE CASE
Which independent boundaries do signature, profile and lifecycle establish?
The access-token profile requires issuer, recipient, purpose and time conditions together. Refresh-family state does not automatically explain another component’s current acceptance.
Separate decisions for verified actor, tenant relation and requested action in the case. Do not substitute authentication or proof success for object permission.
Open the related case file ↗READ THE EXAMPLE TOGETHER
JWT validation and refresh-lifecycle ledger
Select a field and examine what it supports and where the evidence ends.
Read the raw artifact and field explanations
{
"shapeAlias": "HEADER_ALIAS.PAYLOAD_ALIAS.SIGNATURE_ALIAS (not an encoded JWT)",
"header": {
"typ": "at+jwt",
"alg": "RS256",
"kid": "atlas-key-alias-1"
},
"claims": {
"iss": "https://identity.atlas.example",
"sub": "subject-duru",
"aud": [
"https://api.atlas.example"
],
"exp": 1200,
"nbf": 900,
"iat": 800,
"jti": "token-ledger-a",
"client_id": "atlas-reader",
"scope": "read:own-note"
},
"policy": {
"profile": "RFC 9068 OAuth access token, signed JWS example",
"expectedIssuer": "https://identity.atlas.example",
"expectedAudience": "https://api.atlas.example",
"allowedAlgorithms": [
"RS256"
],
"allowedTypes": [
"at+jwt",
"application/at+jwt"
],
"requiredClaims": [
"iss",
"exp",
"aud",
"sub",
"client_id",
"iat",
"jti"
],
"approvedKeySource": "configured issuer-bound key aliases; no remote fetch",
"clockLeewaySeconds": 0,
"nowSeconds": 1000,
"timeValuesAre": "synthetic NumericDate fixture, not current clock or recommended lifetime"
},
"validationCases": [
{
"case": "normal",
"signatureValid": true,
"issuerValid": true,
"audienceValid": true,
"purposeValid": true,
"nowSeconds": 1000,
"expSeconds": 1200,
"tokenAccepted": true,
"actionAllowed": true
},
{
"case": "wrong-audience",
"signatureValid": true,
"issuerValid": true,
"audienceValid": false,
"purposeValid": true,
"nowSeconds": 1000,
"expSeconds": 1200,
"tokenAccepted": false,
"actionAllowed": false
},
{
"case": "expired",
"signatureValid": true,
"issuerValid": true,
"audienceValid": true,
"purposeValid": true,
"nowSeconds": 1200,
"expSeconds": 1200,
"tokenAccepted": false,
"actionAllowed": false
},
{
"case": "bad-signature",
"signatureValid": false,
"issuerValid": true,
"audienceValid": true,
"purposeValid": true,
"nowSeconds": 1000,
"expSeconds": 1200,
"tokenAccepted": false,
"actionAllowed": false
},
{
"case": "wrong-purpose",
"signatureValid": true,
"issuerValid": true,
"audienceValid": true,
"purposeValid": false,
"nowSeconds": 1000,
"expSeconds": 1200,
"tokenAccepted": false,
"actionAllowed": false
}
],
"refreshFamily": {
"correlation": "grant-ledger-a",
"aliasesAre": "not actual refresh tokens",
"beforeReuse": {
"refresh-old": "invalidated by prior rotation",
"refresh-current": "active"
},
"stalePresentation": "refresh-old alias",
"afterReuse": {
"refresh-old": "invalid",
"refresh-current": "revoked",
"newAccessIssued": false
},
"resourceServerObservation": "previous access alias not revalidated here; immediate revocation not implied"
},
"selector": {
"xml": "<tokens><decision case=\"normal\" accepted=\"true\" signature=\"true\" issuer=\"true\" audience=\"true\" purpose=\"true\" time=\"true\"/><decision case=\"wrong-audience\" accepted=\"false\" signature=\"true\" issuer=\"true\" audience=\"false\" purpose=\"true\" time=\"true\"/><decision case=\"expired\" accepted=\"false\" signature=\"true\" issuer=\"true\" audience=\"true\" purpose=\"true\" time=\"false\"/><decision case=\"bad-signature\" accepted=\"false\" signature=\"false\" issuer=\"true\" audience=\"true\" purpose=\"true\" time=\"true\"/><decision case=\"wrong-purpose\" accepted=\"false\" signature=\"true\" issuer=\"true\" audience=\"true\" purpose=\"false\" time=\"true\"/></tokens>",
"xpath": "count(/tokens/decision[@accepted=\"true\" and (@signature!=\"true\" or @issuer!=\"true\" or @audience!=\"true\" or @purpose!=\"true\" or @time!=\"true\")])",
"expected": 0,
"positiveFixtureXml": "<tokens><decision accepted=\"true\" signature=\"true\" issuer=\"true\" audience=\"false\" purpose=\"true\" time=\"true\"/></tokens>",
"positiveExpected": 1
}
}- header / claims
- A decoded view; not trusted authorization input before verification.
- policy
- Issuer/audience/type/algorithm and required-claim choices come from the application’s approved profile.
- signatureValid / tokenAccepted / actionAllowed
- Cryptographic predicate, profile acceptance, and resource permission are separate states.
- refreshFamily
- AS refresh state, not automatic proof of resource-server access-token revocation.
There is no real JWT, signature, key, or refresh token. NumericDate times are synthetic and zero leeway is a fixture choice only. Signature predicates are supplied; no cryptographic library ran. RFC9068 is the selected profile, not a rule for every JWT.
Two reality checks
Decoding a JWT or obtaining a valid signature makes its claims authorized at this API.
Approved algorithm/key, issuer, audience, purpose, required claims, and time policy are needed; resource authorization remains separate.
Refresh-family revocation or Browser cleanup instantly invalidates issued JWT access tokens everywhere.
AS family state, client cleanup, and resource acceptance differ. Immediate revocation needs suitable trusted state/coordination; a locally validating server can behave differently until expiry.
YOUR TURN
Work through this story.
Predict the outcome, find the evidence boundary, and explain it in your own words.
Signature is true but audienceValid=false. Is this access token accepted?
- Yes; a valid signature is universal access to every API.
- No; the wrong-audience gate sets tokenAccepted=false.
Read the explained answer
No; the wrong-audience gate sets tokenAccepted=false. — A valid signature is an integrity/authenticity condition in issuer/key context; it does not create a grant for another resource server.
At now=exp=1200 with leeway=0, is acceptance expected?
Selected lesson artifact fields; teaching example
- validationCases[2]
- {"case":"expired","signatureValid":true,"issuerValid":true,"audienceValid":true,"purposeValid":true,"nowSeconds":1200,"expSeconds":1200,"tokenAccepted":false,"actionAllowed":false}
- policy.clockLeewaySeconds
- 0
- No; the selected expired branch rejects it.
- Yes; exp is an inclusive last-valid second.
Read the explained answer
No; the selected expired branch rejects it. — Acceptance requires being before expiration; equality with zero leeway closes the selected time gate.
The refresh family was revoked. Can immediate rejection of an earlier access alias by the resource server be inferred?
Read the explained answer
The selected family revokes the current refresh alias and leaves newAccessIssued=false. The earlier access alias is not revalidated here; resource-server revocation propagation/introspection/current policy need separate evidence. JTI or decoded claims alone are not a revocation mechanism.
□ I separated refresh-family state from access-token acceptance.
□ I left unobserved resource-server state unknown.
Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples
Sources and scope
- RFC 7515 — JSON Web Signature2 Terminology: JWS Signature and Base64url · 3.1 JWS Compact Serialization Overview
- RFC 7519 — JSON Web Token3 JSON Web Token Overview · 4.1 Registered Claim Names · 4.1.1 iss · 4.1.2 sub · 4.1.3 aud · 4.1.4 exp · 4.1.5 nbf · 4.1.6 iat · 4.1.7 jti · 7.2 Validating a JWT
- RFC 8725 — JWT Best Current Practices3.1 Perform Algorithm Verification · 3.3 Validate All Cryptographic Operations · 3.8 Validate Issuer and Subject · 3.9 Use and Validate Audience · 3.10 Do Not Trust Received Claims · 3.11 Use Explicit Typing · 3.12 Use Mutually Exclusive Validation Rules for Different Kinds of JWTs
- RFC 9068 — JWT Profile for OAuth 2.0 Access Tokens2.1 Header · 2.2 Data Structure · 4 Validating JWT Access Tokens
- RFC 6749 — OAuth 2.01.4 Access Token · 1.5 Refresh Token · 6 Refreshing an Access Token
- RFC 9700 — OAuth 2.0 Security Best Current Practice4.14.1 Refresh Token Protection: Discussion · 4.14.2 Refresh Token Protection: Recommendations
- RFC 7009 — OAuth Token Revocation3 Implementation Note
- OWASP — Authorization Cheat SheetValidate the Permissions on Every Request