JWT Decoder β Debug Authentication Errors by Inspecting Token Claims
Learn how to use a JWT decoder to debug 401 errors, understand the header, payload, and signature sections, check expiry and audience claims, and identify security issues like alg:none attacks.
By sadiqbd Β· June 7, 2026
A 401 error doesn't always mean the token is wrong β sometimes it means you're reading the wrong token
An API returns 401 Unauthorized. The developer checks the code β the token is being sent. They check the expiry β it looks fine in the logs. They spend 45 minutes debugging before realising: the token they're checking in the logs is a different token from what's actually being sent to that endpoint. A JWT decoder would have resolved this in 30 seconds.
JWT debugging is about seeing what's actually in the token versus what you think is in the token. The decoder closes that gap.
What Every JWT Contains
A JWT has three Base64URL-encoded sections separated by dots:
[header].[payload].[signature]
The header
Always tells you the algorithm and token type:
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-2024-06"
}
alg: The signing algorithm.HS256= HMAC-SHA256 (symmetric, shared secret).RS256= RSA-SHA256 (asymmetric, public/private key pair).ES256= ECDSA (more compact than RSA).typ: Almost always "JWT"kid: Key ID β which specific key was used to sign this token. Relevant when a service rotates keys.
The payload (claims)
Contains the actual information about the user and token:
{
"iss": "https://auth.example.com",
"sub": "user_42",
"aud": ["api.example.com", "admin.example.com"],
"exp": 1718003600,
"iat": 1718000000,
"nbf": 1718000000,
"jti": "abc123-unique-id",
"email": "user@example.com",
"roles": ["admin", "editor"],
"tenant_id": "org_789"
}
The standard claims:
iss(issuer): Who created the tokensub(subject): The user or entity the token is aboutaud(audience): Who the token is intended for β an API should reject tokens with a non-matching audienceexp(expiration): Unix timestamp β token is invalid after thisiat(issued at): When the token was creatednbf(not before): Token is invalid before this timestampjti(JWT ID): Unique identifier for this specific token
Custom claims (email, roles, tenant_id) are application-specific additions.
The signature
Computed from the header + payload + secret/private key. Cannot be decoded without the secret. The decoder shows the raw signature bytes but cannot verify or reverse them without the signing key.
How to Use the JWT Decoder on sadiqbd.com
- Paste the JWT β the complete token string (all three sections including dots)
- Decode β header, payload, and signature are shown separately
- Check expiry β the tool converts
expto a readable date and shows whether it's expired - Review claims β confirm audience, roles, user ID, and any custom claims
Debugging Authentication Problems with JWT Decoder
Problem: 401 on a valid-looking token
Decode the token being sent. Check:
-
Is
expin the past? The most common cause. The decoder shows the expiry time in human-readable form βexp: 1718003600β June 10, 2024 at 11:13 AM UTC. If current time is past this, the token is expired. -
Does
audmatch the API? If the token has"aud": "payments.example.com"but you're callingusers.example.com, the users API should (and often does) reject the token. Check what audience your API requires. -
Is
isscorrect? Some APIs validate the issuer. A token from a staging environment sent to production will fail if the issuers differ. -
Are required custom claims present? Your API may require
tenant_idor specific roles. Check whether they're in the payload.
Problem: User can't access an admin page despite being an admin
Decode the token actually being used (from the request headers in the browser's Network tab). Check the roles claim. If "roles": ["viewer"] instead of "roles": ["admin"], the token was issued before the role was updated. The user needs to log out and back in to get a new token with the updated role.
Problem: Suspicious-looking JWT
A token header shows "alg": "none". This is a known vulnerability in some JWT libraries β the none algorithm means no signature verification. If your system accepts this, it means any payload would be accepted without verification. This is a critical security issue.
Another red flag: "alg": "HS256" when you expect "alg": "RS256". An attacker might be trying an algorithm confusion attack β sending an RS256-signed token but claiming HS256 (which would make the library try to verify using the public key as an HMAC secret, potentially succeeding if the library is vulnerable).
JWT Expiry and Token Lifecycle
Understanding token lifecycle is essential for understanding JWT-related errors:
Short-lived access token (5 min β 1 hour): The token you send to APIs. Short expiry limits damage if compromised.
Refresh token (days to weeks): A long-lived token used to get new access tokens without re-authentication. Usually stored more securely (HttpOnly cookie vs. localStorage).
Token rotation: When a refresh token is used, a new refresh token is issued and the old one is invalidated. This prevents infinite token reuse.
The JWT decoder shows access token lifetime (from iat to exp). If the expiry is 1 year, the access token has a security problem β it should be much shorter.
What the Decoder Cannot Tell You
Whether the signature is valid. Verifying the signature requires the signing key β which only the server that issued the token has. The decoder shows the claims; it cannot confirm whether the token is genuine without the key.
Whether the token has been revoked. JWTs are stateless β once issued, they're valid until expiry unless you maintain a token blocklist. The decoder shows the claims but not whether the issuer has invalidated the specific token.
Whether the custom claims are accurate. If a token says "role": "admin", that's what the issuing system said at issue time. Whether it's still true (maybe the admin status was revoked) depends on whether the system re-validates claims on each request.
Security Reminders
Production tokens are sensitive. A valid, unexpired JWT for your production system should be treated like a password. Don't paste it into tools you don't trust. For debugging production issues, use tokens that will expire quickly or have already expired.
The decoder confirms what you think is there. The primary use case is verifying that the token contains the expected claims β not necessarily breaking security, but ensuring your application logic is working as intended.
Frequently Asked Questions
Can I use the decoder to modify a JWT and make it work differently? No β modifying the payload would invalidate the signature. Unless you have the signing secret, you can't create a valid modified JWT.
What if the JWT has no expiry (exp claim)?
Tokens without expiry are problematic β they're valid indefinitely unless explicitly revoked. Well-designed systems always include expiry. A missing exp in a production token should be flagged as an implementation issue.
Why are some JWT payloads larger than others? More claims = larger payload. Some systems embed extensive user profile data in the JWT to avoid database lookups. This is a trade-off between token size and authentication performance.
Is the JWT Decoder free? Yes β completely free, no sign-up required.
JWT debugging without a decoder is guesswork. With a decoder, the problem is almost always visible within 30 seconds of pasting the token. For any authentication issue involving JWTs, it's the first tool to reach for.
Try the JWT Decoder free at sadiqbd.com β paste any JWT and inspect its header, payload, and expiry instantly.