Uvlio

Command Palette

Search for a command to run...

Back to articles
Technical Article

JWTs Explained: Decoding Claims Without Confusing Them With Validation

A JSON Web Token is easy to inspect because much of it is readable after Base64URL decoding. That readability is useful during debugging, but it also creates a common mistake: people decode a token and assume it is valid. A JWT should be read in two layers. The first layer explains what the token says. The second layer decides whether the application should trust it.
Uvlio editorial team by limitcool2026-05-177 min read
Topic coverDeveloperJWTClaims

JWTs Explained: Decoding Claims Without Confusing Them With Validation

A practical explanation of JWT structure, claims, Base64URL encoding, signatures, and why decoding a token is not the same as trusting it.

Guide subject preview
header.payload.signature
"aud":"app-web"
check exp / iat / nbf
Tool stack
JWT Decoder
Reading focus
1Decode parts
2Read claims
3Validate trust

Original workflow visual

JWTs Explained: Decoding Claims Without Confusing Them With Validation

This original Uvlio visual summarizes the practical path from input inspection to output review for this workflow.
1

Decode parts

Review before moving forward

2

Read claims

Review before moving forward

3

Validate trust

Review before moving forward

Maintainer and review note
Maintained by limitcool. Use it to understand the technical model, processing boundaries, privacy risks, and verifiable behavior.
The three visible parts

Most JWTs are written as three dot-separated segments: header, payload, and signature. The header usually states the token type and signing algorithm. The payload contains claims such as subject, issuer, audience, expiration, and application-specific fields. The signature protects the header and payload from modification when verified with the correct key. The first two parts are encoded, not encrypted. Anyone with the token can usually read them.

Claims are statements, not proof

A claim is a statement inside the payload. The subject might identify a user, the audience might identify an API, and the expiration might define when the token should stop working. Those statements are useful only if the token is valid for the current application. An attacker can write a payload that says anything. Trust comes from signature verification, issuer checks, audience checks, time checks, and application authorization rules.

Expiration bugs are often unit and clock bugs

The exp, iat, and nbf claims are usually NumericDate values measured in seconds. Developers often compare them with millisecond timestamps from JavaScript, which creates dates far in the future or past. Clock skew also matters when servers disagree by a small amount. When debugging an expired or not-yet-valid token, convert the claim to UTC first, then compare it with server time and the policy your application actually enforces.

Audience and issuer prevent cross-use

The issuer tells you who created the token, and the audience tells you who the token is meant for. These checks stop a token from one environment or service being accepted by another. A token issued for staging should not work in production. A token issued for a profile API should not automatically work against a billing API. Many real JWT bugs are not cryptographic failures; they are missing issuer or audience checks.

Algorithms and keys must be controlled by the verifier

The verifier should not blindly trust whatever algorithm appears in the header. Application configuration should define allowed algorithms and keys. Public key rotation, key identifiers, and JSON Web Key Sets can make verification flexible, but they also add failure modes when caches are stale or keys are misconfigured. Decoding the header can help explain a failure, but the verifier must still enforce its own rules.

A JWT is often a bearer credential

Many JWTs are bearer tokens: whoever has the token can use it until it expires or is revoked by the surrounding system. That means raw tokens should not be pasted into tickets, analytics, screenshots, chat logs, or unnecessary online tools. For debugging, use a synthetic token or redact the signature and sensitive claims when possible. If a real token was exposed, treat it like any other leaked credential.

A practical debugging order

Start by decoding the header and payload locally. Read iss, aud, sub, exp, iat, nbf, roles, scopes, and environment markers. Convert times to UTC. Then check whether the rejecting service expects the same issuer, audience, algorithm, key, and clock tolerance. If those are correct, move to authorization logic: the token may be valid but still lack permission. This order keeps token debugging from becoming a random search through backend code.

Common Questions

Does decoding a JWT prove it is valid?

No. Decoding only reveals the header and payload. Validation requires signature, issuer, audience, time, and policy checks.

Are JWT payloads private?

Usually no. Standard JWT payloads are encoded, not encrypted, so sensitive data should not be placed there casually.

Why does my token work in one service but not another?

Common causes are audience mismatch, issuer mismatch, key rotation, clock skew, missing scopes, or environment confusion.

What should I redact before sharing a JWT in a bug report?

Avoid sharing the raw token. If a teammate only needs claim structure, provide a decoded payload with subject, email, token ID, signature, and environment-specific identifiers replaced by placeholders. Keep claim names, time values, issuer, audience, and scope shape if those details are relevant to the failure.

Why can a valid JWT still fail authorization?

Validation answers whether the token should be trusted. Authorization answers whether the trusted subject can perform a specific action. A token can have a correct signature and still lack the required role, scope, tenant membership, account state, or resource-level permission.

Should tokens be stored in logs?

Raw bearer tokens should not be logged because possession can be enough to use them. Log a token ID, request ID, or redacted claim summary instead.