Hacker DirectoryHacker Directory

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.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Expected profile and resource

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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

  1. 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
  2. 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
  3. 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
  4. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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?

  1. Yes; a valid signature is universal access to every API.
  2. 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
  1. No; the selected expired branch rejects it.
  2. 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