What's Actually Inside a JWT (And Why Decoding Isn't the Same as Verifying)

5 min read

A JWT's payload is readable by anyone — the security comes from the signature, not from secrecy.

A JSON Web Token has three parts separated by dots: a header describing the signing algorithm, a payload containing claims (like a user ID or expiry time), and a signature. The header and payload are just Base64Url-encoded JSON — not encrypted — so decoding them requires no secret key at all, only reversing a standard, publicly known encoding.

This is an important and frequently misunderstood point: anyone can decode a JWT's contents and read the payload, including a token they intercepted but were never meant to have. The signature is what makes a JWT trustworthy, not secrecy of its contents — it proves the payload has not been tampered with since the issuing server signed it.

Decoding a JWT (reading what is inside) and verifying a JWT (confirming the signature is valid and the token has not been forged or expired) are two entirely different operations. A tool that decodes a JWT for inspection is not performing any security check — verification requires the actual signing secret or public key, which a client-side decoding tool never has access to.

1. The Anatomy of a JSON Web Token (RFC 7519)

A JSON Web Token (JWT) is an open, industry-standard RFC 7519 method for securely representing claims between two parties in modern stateless client-server architectures. JWTs are ubiquitous in Single-Page Applications (SPAs) and microservices for user authentication and authorization. A JWT appears to the naked eye as an opaque, continuous alphanumeric string separated by two periods (`.`): `aaaaa.bbbbb.ccccc` These periods separate three distinct structural components: 1. **Header (Algorithm & Token Type)** 2. **Payload (Data Claims)** 3. **Signature (Cryptographic Verification Proof)**

2. Base64Url Encoding: Decoding is NOT Verifying

The most dangerous misconception among novice software developers is assuming that a JWT is 'encrypted' or secret. **A standard JWT is merely encoded, NOT encrypted.** - The Header and Payload are plain JSON objects transformed using **Base64Url** encoding—a binary-to-text scheme that replaces URL-unfriendly characters (`+` and `/`) with `-` and `_` and strips trailing padding (`=`). - Anyone with access to the token (or visiting sites like jwt.io) can decode the payload in browser JavaScript using `atob()` and read every user claim, role, and email address. - Never place confidential secrets—such as raw database passwords, private encryption keys, or sensitive credit card data—inside a standard JWT payload.

3. Standard Claims and Expiration Timing

JWT payloads carry reserved claims defined by RFC 7519: - **`sub` (Subject):** The unique identifier of the user (e.g. `"sub": "user_98721"`). - **`iat` (Issued At):** Unix epoch timestamp when the token was created. - **`exp` (Expiration Time):** Unix timestamp after which the token must be rejected. - **`iss` (Issuer):** The authentication server that minted the token (e.g. `auth0.com`). - **`aud` (Audience):** The recipient backend resource server for which the token is intended. Custom claims (such as `"role": "admin"` or `"tenant_id": "corp_12"`) can be freely appended for authorization checks.

4. The Cryptographic Signature and The 'None' Algorithm Attack

The third component—the **Signature**—is what provides security and tamper-resistance: `Signature = HMAC-SHA256( Base64Url(Header) + "." + Base64Url(Payload), SecretKey )` When a backend server receives a JWT, it recalculates the cryptographic signature using its secret private key. If an attacker modifies even a single character in the payload (such as altering `"role": "user"` to `"role": "admin"`), the signatures will not match, and the request is immediately rejected with a 401 Unauthorized status. **The Infamous 'None' Algorithm Attack:** Early vulnerable JWT libraries allowed attackers to craft headers containing `"alg": "none"`, tricking naive servers into accepting unsigned modified payloads without verifying the signature. Modern production libraries explicitly disallow the `none` algorithm.

5. Frequently Asked Questions on JWTs

What is the difference between JWS and JWE? A standard signed token is a JSON Web Signature (JWS)—data is readable by anyone but tamper-proof. A JSON Web Encryption (JWE) token encrypts the payload so that only holders of the private decryption key can view its contents. ### Where should JWTs be stored in browser applications? Storing tokens in browser `localStorage` exposes them to Cross-Site Scripting (XSS) theft. Storing tokens inside secure `httpOnly`, `SameSite=Strict` cookies protects them from client-side script access. ### How do you revoke a stateless JWT before its expiration date? Because JWTs are stateless, instant revocation requires either short expiration lifespans (15 minutes) paired with rotating refresh tokens, or maintaining a distributed Redis token-denylist.

6. Strategic Takeaways

Remember that JWT payloads are fully readable by clients. Always enforce cryptographic signature verification on the server, keep access token lifespans short, and never store passwords or sensitive keys in token claims.