JWT debugging becomes much easier when you separate three tasks: decoding a token to inspect its contents, validating its claims, and verifying its signature. This guide explains how to use a JWT decoder online or a small JavaScript snippet safely, what each token section means, and how to trace common authentication errors without treating an unverified token as trusted data.
Overview
A JSON Web Token, usually called a JWT, is a compact string commonly used to carry authentication and authorization information between an application and an API. A typical token contains three dot-separated parts: a header, a payload, and a signature.
The header commonly identifies the token type and signing algorithm. The payload contains claims such as an issuer, subject, audience, expiration time, or application-specific values. The signature is used by the receiving service to determine whether the signed content has been altered and whether it was signed with a trusted key.
For debugging, the first two sections are the most immediately useful. A JWT decoder can turn their Base64URL-encoded text into readable JSON, helping you answer questions such as:
- Has the token expired?
- Does its issuer match the API configuration?
- Is the intended audience correct?
- Does the token contain the expected subject, scope, or roles?
- Is the token structurally complete?
Decoding is not the same as verification. Anyone who possesses a JWT can usually decode its header and payload. Readable claims do not prove that the token is authentic, unmodified, or safe to authorize. Use decoder tools with test or redacted tokens whenever possible, and never paste production credentials into an untrusted browser-based service.
Core framework
1. Confirm the token structure
Start by checking whether the value has exactly three non-empty sections separated by periods:
header.payload.signatureA missing section, an extra period, or a copied prefix such as Bearer can cause a parser to reject the value. When inspecting an HTTP request, remove the scheme before decoding:
const authorization = 'Bearer eyJ...';
const token = authorization.replace(/^Bearer\s+/i, '');This cleanup is useful for debugging, but it should not be used as a substitute for server-side validation.
2. Decode the header and payload
JWT sections use Base64URL rather than ordinary Base64. The encoding is designed for URLs and may omit padding characters. A lightweight JavaScript snippet can decode the JSON sections for local development:
function decodeJwtPart(part) {
const base64 = part.replace(/-/g, '+').replace(/_/g, '/');
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
const binary = atob(padded);
const bytes = Uint8Array.from(binary, char => char.charCodeAt(0));
return JSON.parse(new TextDecoder().decode(bytes));
}
function inspectJwt(token) {
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('Expected a JWT with three sections');
}
return {
header: decodeJwtPart(parts[0]),
payload: decodeJwtPart(parts[1]),
signature: parts[2]
};
}
const inspected = inspectJwt(token);
console.log(inspected.header);
console.log(inspected.payload);This example is intentionally an inspection helper. It does not verify the signature and should not decide whether a user is authenticated.
3. Read time-based claims correctly
JWT time claims such as exp, iat, and nbf are commonly represented as numeric timestamps in seconds, while JavaScript dates use milliseconds. Convert deliberately:
function getJwtStatus(payload, nowSeconds = Math.floor(Date.now() / 1000)) {
return {
expired: typeof payload.exp === 'number' && payload.exp <= nowSeconds,
notActiveYet: typeof payload.nbf === 'number' && payload.nbf > nowSeconds,
issuedInFuture: typeof payload.iat === 'number' && payload.iat > nowSeconds
};
}An expired token often produces a 401 response, but the exact behavior depends on the application and its authentication middleware. Clock differences between systems can also make a token appear slightly early or late, so inspect server logs and system time when the timestamps look reasonable but validation still fails.
4. Treat issuer, audience, and subject as separate checks
The iss claim identifies the service or authority that issued the token. The aud claim identifies the intended recipient, while sub usually identifies the user or principal. A token can be well formed and correctly signed yet rejected because it was issued for a different API or environment.
Compare these values with the configuration expected by the service that receives the token. Check spelling, casing, URL formatting, and whether a development token is being sent to a staging or production endpoint. Also inspect scope or role claims, but confirm how the application actually maps them to permissions.
Practical examples
Diagnosing a 401 response
When an API returns 401 Unauthorized, use a repeatable sequence rather than changing several settings at once:
- Confirm that the request includes an Authorization header.
- Check that the value uses the expected scheme, commonly
Bearer. - Decode the token locally and inspect its expiration, issuer, and audience.
- Compare the token's algorithm and key identifier with the server's verification configuration.
- Check whether the API expects a different identity provider, tenant, or environment.
- Review server-side validation errors and logs without exposing the full token.
If the token is expired, obtain a fresh token through the normal authentication flow. Editing the payload will only invalidate the signature; it will not produce a legitimate replacement.
Diagnosing a 403 response
A 403 Forbidden response often means the server recognized the identity but did not authorize the requested action. First confirm that authentication succeeded. Then inspect the claims used for authorization, such as scopes, roles, permissions, or tenant identifiers. A token may contain a role under a different claim name than the application expects, or it may represent a user who is authenticated but lacks access to the selected resource.
Do not assume that adding a role in a decoded token will test the permission system. Since the modified token will not have a valid signature, a properly configured server should reject it before authorization.
Using browser-based tools safely
A JWT decoder online can be convenient for a short, non-sensitive test token, especially when you need to quickly inspect JSON formatting. For real credentials, prefer local tools or a small script in a controlled development environment. Redact signatures, personal data, refresh tokens, and claims that reveal internal identifiers before sharing a screenshot or bug report.
For related payload inspection tasks, the site's guide to online JSON tools for developers can help with formatting and validating decoded data. The broader JavaScript developer tools toolkit also provides context for choosing browser-based utilities during debugging.
Common mistakes
- Confusing decoding with verification: Decoding reveals content; it does not establish trust.
- Using milliseconds for JWT timestamps: Multiplying or comparing time values incorrectly can make valid tokens appear expired.
- Ignoring the audience: A token from the right login system may still be intended for another API.
- Copying the Bearer prefix into a decoder: Strip transport syntax before parsing the token.
- Logging complete tokens: Access tokens can grant access while valid. Log a short identifier or redacted sections instead.
- Trusting client-side claims: UI code may read claims to display a state, but authorization must be enforced by the server.
- Changing the algorithm casually: The header's algorithm is an input to verification policy, not a value the client should blindly accept.
- Testing only the happy path: Include expired, malformed, wrong-audience, and insufficient-scope cases in authentication tests.
When writing JavaScript authentication code, keep token parsing separate from application authorization. That separation makes it easier to test each failure mode and reduces the chance that display logic becomes a security decision.
When to revisit
Revisit this debugging checklist whenever an identity provider changes its token format, signing keys, issuer URL, audience values, claim names, or token lifetime. It is also worth reviewing after an API gateway, authentication library, framework, or deployment environment changes. These updates can alter where validation occurs and which error reaches the client.
Make the process actionable by maintaining a small non-production test set: a valid token, an expired token, a token with the wrong audience, a malformed token, and a token without the required scope. Document which response each case should produce, and run those checks after changes to authentication middleware. Keep the examples free of real user data, rotate any credential that was accidentally exposed, and use server-side verification as the final source of trust.
For broader package and tooling decisions, see the guide on evaluating npm packages before adding them to a JavaScript project. The goal is not to find a decoder that makes every token look readable; it is to build a clear, repeatable path from token structure to verified identity and authorized access.