JWT safety

Inspect JWT claims without treating them as proof

JWT decoding is useful when an integration fails, but a decoded payload is only text until the issuer, audience, time claims, and signature are verified by the receiving system.

Content updated: · Maintainer and corrections

Diagnose a rejected test token

For the example below, exp: 1735689600 converts to 2025-01-01T00:00:00.000Z. At exactly that instant, the token is expired under the exp rule unless the receiving system applies its permitted clock-skew allowance. Enter 1735689600 as seconds in the timestamp tool; multiplying by 1,000 is needed only for an API that takes milliseconds.

If an API still rejects an unexpired test token, compare aud with the intended API, not merely the frontend name. Compare iss with the configured issuer exactly. Then examine the verifier logs for signature and allowed-algorithm failures. A readable payload cannot settle those checks.

Diagnose a rejected test token
CaseInterpretation and next step
expCurrent time must be before expiration, subject to the verifier's explicit skew policy.
nbfA future not-before time can explain rejection even if exp is later.
iatIssued-at describes issuance time; it is not a substitute for exp.

Use local inspection for diagnosis

Compare a non-production token's header and claims with the integration contract. Typical checks are the expected issuer, audience, expiration unit, and whether a required claim is actually present.

Reproduce the time check

NumericDate values are seconds since the Unix epoch. Convert the exact value before assuming a clock-skew problem; milliseconds are a frequent integration mistake.

Never paste a production bearer token

Use a token minted for a local or disposable test account. Decoding does not verify a signature, and an inspector must never become an authorization decision or a place to share credentials.

{"iss":"https://issuer.example","aud":"web","exp":1735689600}

Data provenance

Sources