Cryonel

JWT debugging guide

Debug JSON Web Tokens in layers: compact structure, header policy, cryptographic verification, time claims, and application claims. Decoding is useful evidence, but it is never proof that a token should be trusted.

1. Confirm the compact structure

A signed JWT normally contains three dot-separated Base64URL segments: header, payload, and signature. Extra spaces, a leading Bearer prefix, copied quotation marks, or a missing segment produces “jwt malformed” or “not enough segments.” Extract only the token value and confirm that it has exactly two dots before investigating keys.

2. Decode without trusting

Use the JWT Decoder to inspect the header and payload locally. Check whether both segments decode to JSON and whether the claims resemble the expected environment. Anyone can create readable header and payload segments. Do not authorize a request merely because the token decodes or contains an administrator role.

3. Enforce an algorithm policy

Read alg from the header, but do not let that untrusted value choose any algorithm automatically. The verifier should allow only algorithms configured for the issuer. HS256 uses one shared secret for signing and verification. RS256 and ES256 use a private signing key and a public verification key. Supplying an RSA public key to an HMAC verifier, or accepting alg: none, is a configuration or security failure.

4. Select the correct verification key

For rotating asymmetric keys, the JWT header usually contains a kid. Match it to a key in the issuer’s trusted JWKS. If no key matches, refresh the cached JWKS once and retry; do not try every unrelated key indefinitely. Confirm the JWKS belongs to the configured issuer, the key type matches the algorithm, and certificate or PEM conversion preserved the complete key material.

5. Diagnose signature failures

A signature failure means the signing input, key, or algorithm does not match. Common causes include using a secret from another environment, modifying the token, verifying the wrong issuer, accidental whitespace, and confusing standard Base64 with Base64URL. Compare a known-good token from the same issuer. Never “fix” the problem by disabling verification.

6. Validate time claims with a bounded tolerance

exp rejects tokens after expiration, nbf rejects them before activation, and iat helps detect implausible issuance times. These values are normally Unix seconds, not milliseconds. Keep server clocks synchronized and allow only a small, documented skew for network and clock differences. A large tolerance silently extends token lifetime.

7. Validate issuer, audience, and application rules

Cryptographically valid does not mean valid for this API. Require the expected iss, verify that aud includes your service, and apply tenant, scope, nonce, or token-type rules relevant to the flow. Distinguish an access token from an ID token. Return a generic authentication error to the caller while keeping a safe internal reason code for diagnosis.

Production checklist

  1. Remove the transport prefix and validate the three-segment structure.
  2. Decode the header and payload without granting trust.
  3. Require a configured algorithm and trusted issuer.
  4. Resolve the exact key, then verify the signature.
  5. Validate time, issuer, audience, and application-specific claims.
  6. Test malformed, wrong-key, expired, and wrong-audience tokens.

Frequently Asked Questions

Does decoding a JWT prove it is valid?

No. Header and payload are only Base64URL-encoded; trust requires signature and claim validation.

Why does a JWT work locally but fail in production?

Common causes are different issuer, audience, key set, accepted algorithm, or server clock configuration.

Is it safe to paste a production JWT into a debugger?

Treat tokens as credentials. Prefer local processing, redact them from logs and screenshots, and use short-lived test tokens when possible.

Related Tools and Guides