What the token actually contains
A JWT is three Base64url-encoded chunks separated by dots: a header, a payload, and a signature. The header and payload are readable by anyone — they're not encrypted, just encoded. The signature is what proves authenticity, and it requires the server's secret key to verify.
When an auth bug goes dark — a 401 that shouldn't be happening, a user who can't access a resource they should have permission for — the first step is reading what's actually in the token. I wrote about this in Reading JWT Tokens Without a Library — you can decode any token in 10 seconds with just a browser.
Fields to check when debugging auth
| Field | What it means | What to check |
|---|---|---|
| exp | Expiry time (Unix timestamp) | Is it in the past? Compare to Date.now() / 1000 |
| iat | Issued at time | Is it suspiciously old or in the future? |
| iss | Issuer | Does it match the expected auth server? |
| aud | Audience | Does it include your API/service? |
| sub | Subject (user ID) | Is it the correct user? |
| scope / roles | Permissions granted | Does it include the required scope for this endpoint? |
| alg (header) | Signing algorithm | Is it RS256 or HS256? Never "none" |
What this tool does NOT do
- Signature verificationThis tool decodes the header and payload — it does not verify the signature. You need the server's public key (for RS256) or shared secret (for HS256) to verify authenticity. Never trust a JWT's claims without verifying the signature on the server.
- JWE (encrypted tokens)JWE tokens are encrypted, not just signed. They look like 5-part strings (4 dots). This tool decodes JWS (signed) tokens only — JWE will not decode meaningfully.
The decoder runs entirely in your browser. Your tokens — which may contain user IDs, scopes, and session data — never leave your device.
JWT standard claims — what each field means
The JWT spec (RFC 7519) defines a set of registered claim names. You'll see these in the decoded payload — here's what each one means:
| Claim | Full name | Meaning | Type |
|---|---|---|---|
| iss | Issuer | Who issued the token (e.g., "https://auth.example.com") | String |
| sub | Subject | The user or entity the token is about (e.g., user ID) | String |
| aud | Audience | Who the token is intended for — your API should verify this matches | String or array |
| exp | Expiration time | Unix timestamp (seconds) after which the token is invalid | Number |
| iat | Issued at | Unix timestamp when the token was created | Number |
| nbf | Not before | Unix timestamp before which the token must not be accepted | Number |
| jti | JWT ID | Unique identifier for this specific token — used to prevent replay attacks | String |
| scope / scp | Scope (OAuth 2.0) | Space-separated list of permissions granted to the token | String |
| roles / groups | Custom (not in RFC) | User roles or group memberships — added by auth providers like Auth0, Okta | Array |
Timestamps (exp, iat, nbf) are Unix epoch seconds — divide by 1000 to convert to JavaScript Date milliseconds, or paste into a Unix timestamp converter.
