Hacker DirectoryHacker Directory

WEB / WEB-14.S03

Token verification, purpose and key lifecycle

Separate token integrity evidence from API purpose and key-lifecycle acceptance.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Valid profile

  1. Read intent

    The client follows an owned API acceptance context for one known public note title.

    Atlas client
    No title; read intent
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  2. Request prepared

    The inert token alias and known read operation are prepared without actual token bytes or keys.

    Atlas client
    Read request ready; alias=normal
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  3. Verifier received

    The selected read request has reached the verifier. Its header and claims are not yet accepted identity or resource permission.

    Atlas client
    Read request ready; alias=normal
    Token verifier
    Request arrived; acceptance pending
    Public note store
    Public title stored; no delivery
  4. Local acceptance

    Trusted key/crypto declarations and issuer/audience/purpose/time profile pass before preparing resource read.

    Atlas client
    Read request ready; alias=normal
    Token verifier
    All gates accepted; resource read ready
    Public note store
    Public title stored; no delivery
  5. Read at store

    The known public-note read has arrived at the store; arrival does not yet create a response body.

    Atlas client
    Read request ready; alias=normal
    Token verifier
    All gates accepted; resource read ready
    Public note store
    Read arrived; title body pending
  6. Local public read

    The owned store locally selects the permitted public title projection without mutating the note.

    Atlas client
    Read request ready; alias=normal
    Token verifier
    All gates accepted; resource read ready
    Public note store
    Title response ready; Tea & Bread
  7. Title at verifier

    The public title response has arrived at the verifier; the fixed relay adds no further permission.

    Atlas client
    Read request ready; alias=normal
    Token verifier
    Public body arrived; client response ready
    Public note store
    Title response ready; Tea & Bread
  8. Client received

    The response has arrived and ordinary title data is available; this stage runs no DOM parsing or display.

    Atlas client
    Response arrived; title=Tea & Bread
    Token verifier
    Public body arrived; client response ready
    Public note store
    Title response ready; Tea & Bread

Wrong purpose

  1. Read intent

    The client follows an owned API acceptance context for one known public note title.

    Atlas client
    No title; read intent
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  2. Request prepared

    The inert token alias and known read operation are prepared without actual token bytes or keys.

    Atlas client
    Read request ready; alias=wrong-purpose
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  3. Verifier received

    The selected read request has reached the verifier. Its header and claims are not yet accepted identity or resource permission.

    Atlas client
    Read request ready; alias=wrong-purpose
    Token verifier
    Request arrived; acceptance pending
    Public note store
    Public title stored; no delivery
  4. Local key evidence

    The key is key-v2 from the configured issuer, with state=active and supplied signatureVerifiedDeclared=true. These positive conditions alone do not establish use for notes-read; the purpose decision remains pending.

    Atlas client
    Read request ready; alias=wrong-purpose
    Token verifier
    Configured key/crypto declared valid; profile pending
    Public note store
    Public title stored; no delivery
  5. Local profile gate

    The claims carry purpose=preview-render while the profile requires notes-read. The mismatch gives expectedAccepted=false. The verifier prepares no resource read; body=null and delivery=false remain.

    Atlas client
    Read request ready; alias=wrong-purpose
    Token verifier
    Acceptance denied: purpose; no body
    Public note store
    Public title stored; no delivery
  6. Denial prepared

    A harmless denial result is prepared without sending a read or mutation message to the resource store.

    Atlas client
    Read request ready; alias=wrong-purpose
    Token verifier
    Denial response ready; no resource body
    Public note store
    Public title stored; no delivery
  7. Denial at client

    The denial result has arrived with no protected note body and no preceding resource delivery.

    Atlas client
    Denial arrived; title absent
    Token verifier
    Denial response ready; no resource body
    Public note store
    Public title stored; no delivery

Retired key

  1. Read intent

    The client follows an owned API acceptance context for one known public note title.

    Atlas client
    No title; read intent
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  2. Request prepared

    The inert token alias and known read operation are prepared without actual token bytes or keys.

    Atlas client
    Read request ready; alias=retired-key
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  3. Verifier received

    The selected read request has reached the verifier. Its header and claims are not yet accepted identity or resource permission.

    Atlas client
    Read request ready; alias=retired-key
    Token verifier
    Request arrived; acceptance pending
    Public note store
    Public title stored; no delivery
  4. Local key evidence

    Although key-v1 belongs to the trusted issuer, state=retired and verifyUntil=1700000090. The key is not permitted for acceptance at profile now=1700000100. This differs from reaching the token’s exp boundary.

    Atlas client
    Read request ready; alias=retired-key
    Token verifier
    key-v1 retired; acceptance denied
    Public note store
    Public title stored; no delivery
  5. Local profile gate

    The token satisfies its nbf/exp interval, but the prior key-policy rejection remains: reason=key-retired. The verifier prepares no resource read; body=null, delivery=false and mutation=false remain.

    Atlas client
    Read request ready; alias=retired-key
    Token verifier
    Acceptance denied: key-retired; no body
    Public note store
    Public title stored; no delivery
  6. Denial prepared

    A harmless denial result is prepared without sending a read or mutation message to the resource store.

    Atlas client
    Read request ready; alias=retired-key
    Token verifier
    Denial response ready; no resource body
    Public note store
    Public title stored; no delivery
  7. Denial at client

    The denial result has arrived with no protected note body and no preceding resource delivery.

    Atlas client
    Denial arrived; title absent
    Token verifier
    Denial response ready; no resource body
    Public note store
    Public title stored; no delivery

Key unavailable

  1. Read intent

    The client follows an owned API acceptance context for one known public note title.

    Atlas client
    No title; read intent
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  2. Request prepared

    The inert token alias and known read operation are prepared without actual token bytes or keys.

    Atlas client
    Read request ready; alias=unknown-key
    Token verifier
    No acceptance; configured keys
    Public note store
    Public title stored; no delivery
  3. Verifier received

    The selected read request has reached the verifier. Its header and claims are not yet accepted identity or resource permission.

    Atlas client
    Read request ready; alias=unknown-key
    Token verifier
    Request arrived; acceptance pending
    Public note store
    Public title stored; no delivery
  4. Local key evidence

    kid=key-unlisted was not found in the configured issuer key set. With signatureVerifiedDeclared=null, the signature is neither known valid nor invalid. No trusted-source refresh outcome is available, and an untrusted header URL is not a key source.

    Atlas client
    Read request ready; alias=unknown-key
    Token verifier
    Key unavailable; crypto evidence unknown
    Public note store
    Public title stored; no delivery
  5. Local profile gate

    Missing key-verification evidence stops acceptance with reason=key-unavailable. This known fail-closed decision does not prove forgery. No resource read is prepared; body=null, delivery=false and mutation=false remain.

    Atlas client
    Read request ready; alias=unknown-key
    Token verifier
    Acceptance denied: key-unavailable; no body
    Public note store
    Public title stored; no delivery
  6. Denial prepared

    A harmless denial result is prepared without sending a read or mutation message to the resource store.

    Atlas client
    Read request ready; alias=unknown-key
    Token verifier
    Denial response ready; no resource body
    Public note store
    Public title stored; no delivery
  7. Denial at client

    The denial result has arrived with no protected note body and no preceding resource delivery.

    Atlas client
    Denial arrived; title absent
    Token verifier
    Denial response ready; no resource body
    Public note store
    Public title stored; no delivery

ACTORS · STATE · DATA FLOW

Which protection does the token format carry?

Atlas gives Duru a notes-read access context for the public title Tea & Bread. JWT is the claims-bearing format; JWS provides integrity through a signature or MAC, while JWE uses authenticated encryption for confidentiality and integrity. JWS Compact has three parts and JWE Compact five; counting them is not validation. A signed payload is ordinarily readable: base64url is not encryption. Decrypting JWE does not by itself prove the expected issuer; key provenance and the profile still matter. Nested tokens require validation of every required outer and inner cryptographic operation. The artifact separates inert header and claims fields; signatureVerifiedDeclared is an assumed result, not a JWT string or parser output.

The verifier owns the acceptance profile

The verifier does not let alg freely choose its cryptography: the application binds allowed algorithms and key usage to the operation. kid selects within a trusted issuer key set and is not a trust anchor. Atlas requires issuer, an issuer-subject pair, audience, typ and notes-read purpose. aud can be one string or an array of strings, matched against the intended recipient. Here purpose is an application-specific claim and atlas-notes+jwt an illustrative profile type, not universal JWT requirements. Registered claims are not all mandatory in every application; this profile explicitly lists requiredClaims. An API access context, ID token and differently purposed signed object need distinct acceptance rules. Object/action authorization still follows accepted identity.

Time and key lifecycle are separate decisions

NumericDate counts UTC epoch seconds, unlike JavaScript milliseconds. With zero skew, this fixture requires now >= nbf and now < exp; equality at exp rejects acceptance. iat describes issuance and is not itself not-before or logout enforcement; small skew and maximum age belong to the deployment profile. An active signing key and an older verification key may overlap under explicit policy. Atlas key-v1 is beyond verifyUntil while key-v2 remains active. An unavailable trusted-source refresh for an unknown kid gives no acceptance evidence and does not automatically prove forgery. Rotation, emergency revocation, cache propagation and session logout are separate lifecycle events. Preserve a configured issuer-key binding rather than downloading keys from arbitrary header URLs.

2. Red · Trust & Impact

Read every scenario without motion

Key retired

  1. The token-profile scope was selected

    This case is evaluated under notes-read purpose, atlas-notes+jwt type and atlas-notes-api recipient policy. Decoded claims alone are not trusted context.

    Token profile
    case=retired-key
    Key/purpose gate
    Key/profile not examined
    Resource boundary
    No resource body
  2. The signature predicate was read separately

    signatureVerifiedDeclared=true is supplied. This positive predicate does not automatically complete trusted-key lifecycle or operation-purpose gates.

    Token profile
    case=retired-key
    Key/purpose gate
    Declared signature=true
    Resource boundary
    No resource body
  3. The key-state or purpose boundary was applied

    kid=key-v1 is bound to the configured issuer, but has state=retired and verifyUntil=1700000090. Verification use is not permitted at profile now=1700000100.

    Token profile
    case=retired-key
    Key/purpose gate
    Key retired at current profile time
    Resource boundary
    No resource body
  4. Time and other gates were kept distinct

    exp=1700000300 and nbf=1700000000 satisfy this profile time. Valid token time does not restore permission to use a retired key.

    Token profile
    case=retired-key
    Key/purpose gate
    Time condition valid
    Resource boundary
    No resource body
  5. Delivery and mutation were withheld

    expectedAccepted=false, expectedBody=null, expectedResourceDelivery=false and expectedMutation=false remain. Crypto success does not establish trusted action context for this note.

    Token profile
    case=retired-key
    Key/purpose gate
    Reason: key-retired
    Resource boundary
    Body=null; delivery=false; mutation=false
  6. Key provenance remained distinct from hints

    kid is a selection hint, not a trust anchor. Retirement policies can differ, but this configured denial is not relabelled as issuer compromise or an expired JWT.

    Token profile
    case=retired-key
    Key/purpose gate
    Configured denial known
    Resource boundary
    No trusted resource dispatch

Wrong purpose

  1. The token-profile scope was selected

    This case is evaluated under notes-read purpose, atlas-notes+jwt type and atlas-notes-api recipient policy. Decoded claims alone are not trusted context.

    Token profile
    case=wrong-purpose
    Key/purpose gate
    Key/profile not examined
    Resource boundary
    No resource body
  2. The signature predicate was read separately

    signatureVerifiedDeclared=true is supplied. This positive predicate does not automatically complete trusted-key lifecycle or operation-purpose gates.

    Token profile
    case=wrong-purpose
    Key/purpose gate
    Declared signature=true
    Resource boundary
    No resource body
  3. The key-state or purpose boundary was applied

    The claims carry purpose=preview-render while policy requires notes-read. Matching issuer and audience do not satisfy this different-use purpose gate.

    Token profile
    case=wrong-purpose
    Key/purpose gate
    Purpose preview-render ≠ notes-read
    Resource boundary
    No resource body
  4. Time and other gates were kept distinct

    exp=1700000300 and nbf=1700000000 satisfy this profile time. Valid token time does not turn the wrong purpose into the right one.

    Token profile
    case=wrong-purpose
    Key/purpose gate
    Time condition valid
    Resource boundary
    No resource body
  5. Delivery and mutation were withheld

    expectedAccepted=false, expectedBody=null, expectedResourceDelivery=false and expectedMutation=false remain. Crypto success does not establish trusted action context for this note.

    Token profile
    case=wrong-purpose
    Key/purpose gate
    Reason: purpose
    Resource boundary
    Body=null; delivery=false; mutation=false
  6. Key provenance remained distinct from hints

    Signed claims are unsuitable for this purpose. JWE encryption also does not remove recipient/purpose acceptance rules; protection format is not an action grant.

    Token profile
    case=wrong-purpose
    Key/purpose gate
    Configured denial known
    Resource boundary
    No trusted resource dispatch

ASSUMPTION · BOUNDARY · CONTROL

Overinterpreting cryptographic success

Normal signature success says the supplied bytes verified under a configured key; it does not establish suitability for this recipient and operation. The broken assumption is that every signed claim set from a trusted issuer means the same thing in every consumer. Confusion requires distinct token purposes/profiles and a consumer failing to separate audience, type or required values. Impact is misinterpreted identity or access scope, not automatically universal account compromise. The wrong-purpose row retains signatureVerifiedDeclared=true yet rejects API acceptance, with no resource delivery or title body. Mutually exclusive profiles and separate object authorization preserve the boundary; encryption alone does not correct purpose confusion.

Mistaking key selection for trust provenance

Normal rollout may retain an older verification key for a defined interval; key-v1 does not necessarily mean an expired JWT. The broken assumption is that finding kid proves issuer ownership and continued permitted use. Stale caches, rollover delay or trusted-key-service outage can be benign; the unknown row therefore records key-unavailable separately. The retired row has valid time claims but fails application key policy. Controls bind trusted provenance, algorithm/use and retirement or revocation propagation. Impact is limited to the context accepted by that consumer; there is no key material or bearer token in the artifact.

3. Blue · Secure development & defense

Read every scenario without motion

Key unavailable

  1. The unknown-key observation was selected

    The header carries kid=key-unlisted, absent from the configured key list. Missing key metadata and an invalid-signature outcome are different evidence.

    Verification observation
    case=unknown-key
    Key/lifecycle review
    Key lookup not examined
    Evidence conclusion
    No acceptance conclusion
  2. The trusted-provenance boundary was read

    The approved source is the configured issuer-bound key-alias set. An untrusted header URL is not fetched; kid text does not independently establish issuer/key trust.

    Verification observation
    kid=key-unlisted
    Key/lifecycle review
    Configured key not found
    Evidence conclusion
    No acceptance conclusion
  3. The crypto outcome remained unknown

    signatureVerifiedDeclared=null supports neither a valid nor invalid signature conclusion. A stale cache, rollout or trusted-service outage is not established as the cause of this gap.

    Verification observation
    case=unknown-key
    Key/lifecycle review
    Signature outcome=null
    Evidence conclusion
    UNKNOWN: crypto verification
  4. Acceptance stayed closed on missing evidence

    The reason is key-unavailable with expectedAccepted=false. This fail-closed decision produces no body, mutation or resource delivery; an unavailable key is not forgery evidence.

    Verification observation
    case=unknown-key
    Key/lifecycle review
    Reason: key-unavailable
    Evidence conclusion
    No body/mutation/delivery
  5. Refresh evidence was requested separately

    The lifecycle selects bounded trusted-source refresh, but this scene performs no networking. Acceptance is not assumed before key-source and freshness outcomes are available.

    Verification observation
    case=unknown-key
    Key/lifecycle review
    Trusted freshness required
    Evidence conclusion
    UNKNOWN: refreshed key outcome
  6. The unknown/invalid distinction was preserved

    The known outcome is no acceptance; signature validity remains unknown. Key rotation, revocation and trusted provenance are explained using their own evidence.

    Verification observation
    Known: key unavailable
    Key/lifecycle review
    UNKNOWN: signature validity
    Evidence conclusion
    Known: delivery withheld

Logout scope

  1. The lifecycle-policy record was selected

    logoutMeansAutomaticJWTRevocation=false describes a format limitation. Even with browser cleanup or session-logout observations, all JWT acceptance states do not have one owner.

    Verification observation
    Lifecycle policy
    Key/lifecycle review
    Revocation path not examined
    Evidence conclusion
    Current acceptance unknown
  2. The key-overlap policy was read

    oldKeyOverlapIsConfiguredNotUniversal=true. How long an old key remains usable for verification during rotation is configuration, not a property inferred from token formatting.

    Verification observation
    Lifecycle policy
    Key/lifecycle review
    Overlap configured
    Evidence conclusion
    Current acceptance unknown
  3. Emergency revocation was kept separate

    compromiseRequiresExplicitRevocationPolicy=true marks the need for separate emergency-revocation design. Routine overlap is not automatic incident response.

    Verification observation
    Lifecycle policy
    Key/lifecycle review
    Explicit revoke policy required
    Evidence conclusion
    Current acceptance unknown
  4. JWS and confidentiality were separated

    The JWS Compact ledger has confidentiality=false. A signature can contribute to an integrity/authenticity profile, but guarantees neither unreadable payloads nor logout revocation.

    Verification observation
    JWS confidentiality=false
    Key/lifecycle review
    Protection and lifecycle separate
    Evidence conclusion
    Current acceptance unknown
  5. JWE was not broadened into issuer proof

    JWE carries authenticated encryption, while issuer identity remains profile-bound. Encryption does not complete key-source, purpose or current-revocation observations.

    Verification observation
    JWE protection class known
    Key/lifecycle review
    Issuer profile remains required
    Evidence conclusion
    UNKNOWN: current revocation
  6. The lifecycle conclusion preserved unknown state

    This artifact shows policy boundaries with actualRevocationService=false. Neither an active key nor a logout statement establishes that every consumer currently accepts or rejects the token.

    Verification observation
    Known: lifecycle policy boundaries
    Key/lifecycle review
    No runtime revocation observation
    Evidence conclusion
    UNKNOWN: consumer acceptance

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Separate the verifier decision into fields

Observe profile ID, configured issuer, audience and purpose/type matches, subject mapping, key alias/state, cryptographic result and time-gate reason alongside accepted or denied. Keep raw tokens, personal payloads and private keys out of logs. A decoded observation is not positive verification; an upstream 401 or cache miss alone is not key-compromise evidence. Baseline uses an active key, intended recipient/purpose and bounded time. Separate unknown key, invalid signature and retired key, correlating trusted-source freshness and refresh outcome. Here denial visibly lacks a resource message and title body; no service audit schema or JWT library result is invented.

Exercise rotation and acceptance regression together

Manage acceptance-policy versions with issuer-key provenance. Routine rotation defines old-key verification overlap, maximum token lifetime and bounded cache refresh; emergency revocation needs a separate decision. Signature-valid wrong audience/purpose, now=exp, now=nbf and retired key require distinct regression cases. Fail-closed handling of unavailable evidence differs from attack attribution. The library must support the profile’s unknown-header, critical-extension and nested-crypto rules. A signature primitive working on public note bytes does not verify JWT/JWS acceptance rules or a JWKS lifecycle. Session logout requires separate server state or revocation control; JWT format alone supplies no distributed revocation.

NOW APPLY IT TO THE CASE

How do signatures, key lifecycle and purpose bind together?

The note-read profile requires approved issuer, key state and purpose together. An unknown-key outcome establishes neither forgery nor current revocation.

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

Token acceptance ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "profile": {
    "issuer": "https://identity.example.org",
    "audience": "atlas-notes-api",
    "purpose": "notes-read",
    "typ": "atlas-notes+jwt",
    "allowedAlgorithms": [
      "RS256"
    ],
    "subject": "Duru",
    "now": 1700000100,
    "clockSkewSeconds": 0,
    "requiredClaims": [
      "iss",
      "sub",
      "aud",
      "exp",
      "nbf",
      "iat",
      "purpose"
    ],
    "actualJWT": false,
    "actualJWKS": false,
    "actualNetwork": false,
    "actualRevocationService": false,
    "publicNoteReadPermissionDeclared": true,
    "noteId": "note-A"
  },
  "formatLedger": [
    {
      "format": "JWT",
      "content": "claims set",
      "protection": "selected JWS or JWE profile; not implied by decode"
    },
    {
      "format": "JWS Compact",
      "parts": [
        "protected-header",
        "payload",
        "signature-or-MAC"
      ],
      "confidentiality": false
    },
    {
      "format": "JWE Compact",
      "parts": [
        "protected-header",
        "encrypted-key",
        "iv",
        "ciphertext",
        "authentication-tag"
      ],
      "protection": "authenticated encryption; issuer identity still profile-bound"
    }
  ],
  "keys": [
    {
      "kid": "key-v2",
      "issuer": "https://identity.example.org",
      "algorithm": "RS256",
      "use": "sig",
      "state": "active",
      "verifyUntil": 1700000600
    },
    {
      "kid": "key-v1",
      "issuer": "https://identity.example.org",
      "algorithm": "RS256",
      "use": "sig",
      "state": "retired",
      "verifyUntil": 1700000090
    }
  ],
  "rows": [
    {
      "case": "normal",
      "header": {
        "typ": "atlas-notes+jwt",
        "alg": "RS256",
        "kid": "key-v2"
      },
      "claims": {
        "iss": "https://identity.example.org",
        "sub": "Duru",
        "aud": [
          "atlas-notes-api"
        ],
        "exp": 1700000300,
        "nbf": 1700000000,
        "iat": 1700000000,
        "purpose": "notes-read"
      },
      "signatureVerifiedDeclared": true,
      "expectedAccepted": true,
      "reason": "accepted",
      "expectedBody": {
        "title": "Tea & Bread"
      },
      "expectedResourceDelivery": true,
      "expectedMutation": false
    },
    {
      "case": "wrong-purpose",
      "header": {
        "typ": "atlas-notes+jwt",
        "alg": "RS256",
        "kid": "key-v2"
      },
      "claims": {
        "iss": "https://identity.example.org",
        "sub": "Duru",
        "aud": [
          "atlas-notes-api"
        ],
        "exp": 1700000300,
        "nbf": 1700000000,
        "iat": 1700000000,
        "purpose": "preview-render"
      },
      "signatureVerifiedDeclared": true,
      "expectedAccepted": false,
      "reason": "purpose",
      "expectedBody": null,
      "expectedResourceDelivery": false,
      "expectedMutation": false
    },
    {
      "case": "retired-key",
      "header": {
        "typ": "atlas-notes+jwt",
        "alg": "RS256",
        "kid": "key-v1"
      },
      "claims": {
        "iss": "https://identity.example.org",
        "sub": "Duru",
        "aud": [
          "atlas-notes-api"
        ],
        "exp": 1700000300,
        "nbf": 1700000000,
        "iat": 1700000000,
        "purpose": "notes-read"
      },
      "signatureVerifiedDeclared": true,
      "expectedAccepted": false,
      "reason": "key-retired",
      "expectedBody": null,
      "expectedResourceDelivery": false,
      "expectedMutation": false
    },
    {
      "case": "unknown-key",
      "header": {
        "typ": "atlas-notes+jwt",
        "alg": "RS256",
        "kid": "key-unlisted"
      },
      "claims": {
        "iss": "https://identity.example.org",
        "sub": "Duru",
        "aud": [
          "atlas-notes-api"
        ],
        "exp": 1700000300,
        "nbf": 1700000000,
        "iat": 1700000000,
        "purpose": "notes-read"
      },
      "signatureVerifiedDeclared": null,
      "expectedAccepted": false,
      "reason": "key-unavailable",
      "expectedBody": null,
      "expectedResourceDelivery": false,
      "expectedMutation": false
    }
  ],
  "timeRows": [
    {
      "now": 1700000000,
      "nbf": 1700000000,
      "exp": 1700000300,
      "expected": true
    },
    {
      "now": 1699999999,
      "nbf": 1700000000,
      "exp": 1700000300,
      "expected": false
    },
    {
      "now": 1700000300,
      "nbf": 1700000000,
      "exp": 1700000300,
      "expected": false
    }
  ],
  "lifecycle": {
    "signingKey": "key-v2",
    "oldKeyOverlapIsConfiguredNotUniversal": true,
    "unknownKeyRefresh": "bounded trusted-source refresh; unavailable is no acceptance evidence",
    "logoutMeansAutomaticJWTRevocation": false,
    "compromiseRequiresExplicitRevocationPolicy": true,
    "untrustedHeaderURLFetched": false
  },
  "boundaries": {
    "decodedClaimsTrusted": false,
    "signatureGrantsAllActions": false,
    "jweProvesIssuerByItself": false,
    "kidIsTrustAnchor": false,
    "unavailableKeyProvesForgery": false
  },
  "audienceRows": [
    {
      "aud": [
        "atlas-notes-api"
      ],
      "expected": true
    },
    {
      "aud": "atlas-notes-api",
      "expected": true
    },
    {
      "aud": [
        "book-preview"
      ],
      "expected": false
    },
    {
      "aud": [],
      "expected": false
    }
  ]
}
formatLedger
Claims versus cryptographic container.
profile
Consumer-owned acceptance requirements.
keys
Trusted aliases, use and lifecycle without secrets.
rows
Declared crypto outcome and independent acceptance decisions.
timeRows
Zero-skew NumericDate equality boundaries.

Fixed header/claim/key aliases and acceptance truth table; not a JWT parser, signature-verification or JWKS-fetch result. Time uses zero skew and an explicitly selected application key policy.

Two reality checks

A valid signature makes the token valid at every API.

Audience, purpose/profile, time and key policy still require acceptance.

An unknown kid proves token forgery.

Unavailable provenance prevents acceptance while root cause remains separate.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

Signature verification is supplied as true, but purpose=preview-render while notes-read is required. Is the note body released?

  1. No. The reason is purpose and expectedBody=null.
  2. Yes. A signature approves every purpose and action.
Read the explained answer

No. The reason is purpose and expectedBody=null. — This profile requires purpose matching. A valid signature does not convert a token for another purpose into a notes-read grant.

The key is unavailable and signatureVerifiedDeclared=null. Does this record prove the token is a forgery?

Selected lesson artifact fields; teaching example

rows[3].header.kid
key-unlisted
rows[3].signatureVerifiedDeclared
null
rows[3].reason
key-unavailable
rows[3].expectedResourceDelivery
false
boundaries.unavailableKeyProvesForgery
false
  1. Yes. An unknown kid is always proven forgery.
  2. No. Acceptance is withheld, but forgery is not proven.
Read the explained answer

No. Acceptance is withheld, but forgery is not proven. — key-unavailable withholds delivery. unavailableKeyProvesForgery=false; the signature result is unknown.

JWS Compact provides no confidentiality, while JWE issuer identity remains profile-bound. What separate decisions are needed for key rotation and logout?

Read the explained answer

Decoding claims does not establish trusted identity. JWS provides signature/MAC protection, not confidentiality; JWE uses authenticated encryption but issuer acceptance still requires profile conditions. Key state and verifyUntil follow the configured lifecycle policy, and old-key overlap is not universal. Logout does not automatically revoke every JWT. Current revocation and resource policy remain separate. This ledger executes no real JWT/JWKS or revocation service.

□ I separated decoding, signature and encryption roles.

□ I did not infer universal revocation from rotation or logout.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope