All posts
3 min read

How to Debug a JWT Token Step by Step

A practical guide to reading, validating, and troubleshooting JSON Web Tokens when authentication breaks.

JWT Authentication API Debugging Developer Tools
Manoj Kumar
Manoj Kumar
Senior full-stack developer with 14 years in PHP, Laravel, WordPress,...

Every developer working with modern APIs will eventually stare at a JWT token wondering why authentication is failing. The token looks like a wall of random characters. The API returns 401. The logs say "token expired" or "invalid signature" or, worse, nothing at all.

Here is how to debug a JWT systematically, from decoding the payload to catching the most common mistakes.

What a JWT actually contains

A JWT is three Base64-encoded sections separated by dots. The first section is the header (algorithm and token type). The second is the payload — the actual claims like user ID, roles, and expiration. The third is the signature, which proves the token was not tampered with.

When you paste a JWT into a decoder, you are reading the header and payload. You do not need the secret key for this. The payload is not encrypted, just encoded. Anyone with the token can read it.

Step 1: Decode the token

Paste the full token (all three dot-separated sections) into a JWT decoder. Check these fields immediately:

The exp claim is the expiration time as a Unix timestamp. If this time has passed, the token is expired. This is the single most common cause of 401 errors. Use a timestamp converter to turn the number into a readable date if needed.

The iat (issued at) and nbf (not before) claims define when the token becomes valid. If your server clock is wrong, a freshly-issued token might appear to be from the future.

The iss (issuer) and aud (audience) claims must match what your API expects. A token issued by auth.staging.example.com will fail validation against auth.example.com.

Step 2: Check the algorithm

The header contains an alg field. Common values are HS256 (HMAC with SHA-256, symmetric), RS256 (RSA with SHA-256, asymmetric), and ES256 (ECDSA, asymmetric).

If your API expects RS256 but the token header says HS256, validation will fail. This mismatch can happen when switching between auth providers or environments. It can also be an attack vector: the "alg: none" attack tricks some libraries into skipping signature verification entirely. Check this field deliberately, not as an afterthought.

Step 3: Verify the claims

Look at the custom claims in the payload. These are application-specific fields like role, permissions, tenant_id, or scope. Common issues include the user having role: viewer when the endpoint requires role: admin, the scope field missing a required permission, and a tenant_id that does not match the resource being accessed.

Step 4: Check token transmission

How is the token being sent? The standard is the Authorization: Bearer <token> header. Common mistakes include sending it as a query parameter (some APIs reject this), including the word "Bearer" twice, having whitespace or newlines in the token (happens when copying from logs), and the token being URL-encoded when it should not be.

Step 5: Clock skew

If the token looks valid but authentication still fails, check the server time. A 5-minute clock difference between the auth server and the API server can cause tokens to appear expired or not-yet-valid. Most JWT libraries have a clockTolerance or leeway setting for this. It is worth configuring even when you do not think you need it.

Step 6: Signature verification

If the payload looks correct and everything else checks out, the issue might be the signature. This means the secret key or public key used to verify the token does not match the one used to sign it. This happens after key rotation, when using different keys per environment, or when the JWKS (JSON Web Key Set) endpoint is cached and stale.

Quick debugging checklist

  • Check exp claim against current time
  • Verify iss and aud match your configuration
  • Confirm the alg header matches what your API expects
  • Inspect custom claims for correct roles and permissions
  • Verify the Authorization header format is correct
  • Check server clock synchronization
  • Confirm the signing key matches between issuer and verifier

Tools

Share this post
Manoj Kumar
Manoj Kumar

Senior full-stack developer with 14 years in PHP, Laravel, WordPress, and AWS. Building products under the FluxWillow umbrella.