InfraHub
Back to Blog
Security
Security Analyst

JWT Authentication Debugging: Decode, Verify, and Fix Token Issues

Learn JWT structure, how claims work, why tokens expire, and how to debug common JWT authentication failures in your applications.

JWT Authentication Debugging: Decode, Verify, and Fix Token Issues

JSON Web Tokens are the backbone of modern API authentication. They're compact, self-contained, and stateless. They're also a frequent source of cryptic auth failures. Understanding their structure is the fastest path from "401 Unauthorized" to a working application.

What Is a JWT?

A JSON Web Token (JWT) is a compact, URL-safe token format defined in RFC 7519. It consists of three Base64URL-encoded parts separated by dots:

header.payload.signature

A real token looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Header

The header specifies the token type and signing algorithm:

{
  "alg": "HS256",
  "typ": "JWT"
}

Common algorithms: HS256 (HMAC-SHA256), RS256 (RSA-SHA256), ES256 (ECDSA-SHA256).

Payload (Claims)

The payload contains claims — statements about the subject:

{
  "sub": "1234567890",
  "name": "Alice",
  "email": "alice@example.com",
  "roles": ["admin"],
  "iat": 1516239022,
  "exp": 1516242622,
  "iss": "https://auth.example.com",
  "aud": "https://api.example.com"
}

Signature

The signature verifies the token hasn't been tampered with:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

Critical: The payload is Base64URL-encoded, not encrypted. Anyone can decode and read the payload. Never put sensitive data (passwords, credit card numbers, PII beyond what's necessary) in a JWT.

Registered Claims (RFC 7519)

Claim Full Name Description
iss Issuer Who issued the token
sub Subject Who the token is about (usually user ID)
aud Audience Who the token is intended for
exp Expiration Unix timestamp after which the token is invalid
nbf Not Before Unix timestamp before which the token is invalid
iat Issued At Unix timestamp when the token was issued
jti JWT ID Unique identifier for this token (prevents replay)

Common JWT Authentication Failures

1. Token Expired (exp in the past)

{ "error": "jwt expired" }

Check the exp claim. Convert the Unix timestamp:

new Date(1516242622 * 1000).toISOString()
// "2018-01-18T02:30:22.000Z"

Short expiration windows (15 minutes) are a security best practice but require a refresh token flow to maintain user sessions.

2. Algorithm Confusion Attack

If your server accepts both RS256 and HS256, an attacker can take the RS256 public key (which is public) and use it as the HMAC secret to forge tokens signed with HS256.

Fix: Always specify the allowed algorithm explicitly, never accept none as an algorithm:

jwt.verify(token, publicKey, { algorithms: ['RS256'] });

3. Audience Mismatch

A token issued for https://api-v1.example.com should not be accepted by https://api-v2.example.com. Validate the aud claim:

jwt.verify(token, secret, { audience: 'https://api-v2.example.com' });

4. Clock Skew

Tokens validated across servers with different system clocks can fail due to exp or nbf being milliseconds off. Most JWT libraries have a clockTolerance option (typically ±30 seconds).

5. Signature Verification Failure

A "invalid signature" error means either:

  • The wrong secret/key is being used
  • The token was modified after signing
  • The Base64URL encoding was corrupted (watch out for URL-decoded + and / characters)

JWT Security Best Practices

  1. Use short expiration times (15–60 minutes for access tokens).
  2. Use refresh tokens stored in httpOnly cookies, not localStorage.
  3. Validate iss, aud, and exp on every request.
  4. Use RS256 or ES256 for distributed systems where multiple services verify tokens — avoids sharing a secret.
  5. Never put sensitive data in the payload — it's readable by anyone with the token.
  6. Implement token revocation for high-security flows (maintain a token blocklist or use short expiration).

Debug JWTs in Your Browser

The JWT Debugger on InfraHub decodes any JWT instantly, shows the header, payload, and claims in a readable format, checks expiration status, and validates the signature if you provide the secret or public key. It runs entirely in your browser — your tokens are never transmitted to any server.

Paste in a token from your auth flow, a test environment, or an API documentation example to verify its structure before writing validation code.

Share Feedback

We read every message