What's inside a JWT, and why decoding isn't the same as verifying
A JSON Web Token (JWT, pronounced "jot") is a compact, self-contained way to represent claims — statements about a user or entity — that can be verified and trusted because it's digitally signed. JWTs are the backbone of most modern authentication systems: log in once, receive a JWT, and send that token with every subsequent request instead of re-authenticating each time.
A JWT is not encrypted by default — it's signed, not sealed. Anyone who has the token can read its contents. What the signature guarantees is that the contents haven't been tampered with since the server issued it, not that they're secret.
A JWT is three Base64url-encoded segments joined by dots: header.payload.signature.
HS256, RS256) and token typesub (subject/user ID), iat (issued at), exp (expiration), plus any custom claims the application adds, like roles or permissionsBecause the header and payload are just Base64url-encoded JSON — not encrypted — you can decode and read them without knowing the signing secret. Verifying the signature is a separate step that does require the secret or public key.
These are two different operations that are easy to conflate:
A common mistake is decoding a JWT, seeing valid-looking claims, and assuming the token is legitimate. Without verification, anyone can craft a JWT with any payload they like — the signature is what separates "these claims look right" from "these claims are actually trustworthy."
iss — issuer, who created the tokensub — subject, usually the user IDaud — audience, who the token is intended forexp — expiration time, as a Unix timestampiat — issued-at timenbf — "not before," the token isn't valid until this timeApplications commonly add custom claims on top of these — a role, a tenant_id for multi-tenant systems, or a list of granted scopes.
The signing algorithm in the header determines how verification works:
Most large-scale identity providers (Auth0, Okta, Firebase Auth) default to RS256 for exactly this reason — it lets many services verify tokens without ever holding the signing secret.
The exp claim exists because JWTs can't be revoked the way a server-side session can — once issued, a JWT remains technically valid until it expires, even if the user's access should have been revoked. Short expiration times (minutes, not days) combined with a refresh token mechanism is the standard mitigation: the short-lived JWT limits the damage window if a token is somehow leaked, while the refresh token (usually stored more securely) issues new JWTs as needed.
When an API call fails with an auth error, decoding the JWT you're sending is often the fastest way to diagnose it. Common issues a quick decode reveals:
exp is in the past)aud doesn't match the API you're calling)Can I read a JWT's contents without the secret key? Yes — decoding the header and payload requires no secret, since they're only Base64url-encoded, not encrypted.
Is it safe to paste a JWT into an online decoder? Only if the tool processes it client-side. A JWT often contains user IDs, roles, and other claims you may not want logged on someone else's server, even though it isn't a password.
Why does my decoded JWT look valid but the API still rejects it? Decoding only shows you the claims — it doesn't confirm the signature is valid. An expired, tampered, or wrong-audience token can still decode into readable JSON while failing verification.
Use the DataBench JWT Decoder to inspect the header and payload of any JSON Web Token, check its expiration, and optionally verify HS256 signatures — entirely in your browser, with nothing sent to a server.