JWT Decoder
How it works
Paste any JWT, the long xxx.yyy.zzz string you find in Authorization: Bearer headers, OAuth responses, cookies or Firebase auth tokens. The tool splits it into its three parts (header, payload, signature), Base64URL-decodes the first two into JSON, and lays them out side-by-side with syntax highlighting. The third part is shown as-is, because a signature is binary, not decodable text.
The header tells you which algorithm signed the token (alg) and sometimes which key was used (kid). Common values: HS256 (HMAC-SHA-256, symmetric), RS256 (RSA-SHA-256, asymmetric, the default for OAuth / OpenID Connect), ES256 (ECDSA P-256, what Apple and modern issuers prefer), and the infamous "none", which means unsigned and should always be rejected.
The payload carries the claims, the actual data inside the token. Standard claims the tool highlights: • `iss`: issuer (which service minted the token) • `sub`: subject (usually the user ID) • `aud`: audience (which service is allowed to consume it) • `exp`: expiration, a Unix timestamp; the tool converts it to a human date and an "expires in 23 minutes" badge, or "EXPIRED 2 hours ago" if it is past • `iat`: issued-at timestamp • `nbf`: not-before (the token is not valid before this time) • `jti`: a unique token ID, used to revoke individual tokens
Everything else is a custom claim (roles, permissions, tenant ID, email). The tool preserves the exact JSON key order and types, so you can eyeball what your auth server is actually emitting.
Security note worth taking seriously: do NOT paste a production JWT into an online tool you don't control. JWTs can contain PII (email, user ID), session identifiers and access scopes. Even a read-only tool that sends the token to a server has just received, logged and possibly stored your credentials. This tool decodes inside the page, which you can confirm in DevTools → Network by watching no request fire, but for production debugging prefer the `jwt` CLI or a local IDE extension where you can.
Frequently Asked Questions
Is it safe to paste my JWT here?
- Safer than most online decoders, because this one runs entirely in your browser: pasting and decoding fire no network request, which you can confirm in DevTools → Network. That said, for production tokens carrying an active session identifier the safest route is always a local CLI or your IDE's JWT extension. Any valid token pasted into any web page is a potential credential leak if the page or a browser extension is compromised.
Does it verify the signature?
- No, this tool only decodes. Signature verification needs the signing secret (HS256) or the issuer's public key (RS256 / ES256), and neither belongs in a random web tool. Decoding is one-way reading; verification is a separate step, best done server-side or with a dedicated library (jose, jsonwebtoken, pyjwt).
What claims are shown?
- Every claim in the payload, with special highlighting for the standard registered ones: iss (issuer), sub (subject), aud (audience), exp (expiration, rendered as a human date plus an expires-in badge), iat (issued-at), nbf (not-before), jti (token ID). Custom claims (roles, tenant, email, scopes) are listed below in their original order.
Why does my token look like it has three parts separated by dots?
- Because a JWT has exactly three parts: header.payload.signature, each Base64URL-encoded. The header and payload decode to JSON; the signature is a binary MAC or signature that cannot be decoded to readable text. If your token has more or fewer dots it is not a standard JWT, possibly a JWE (the encrypted variant, which this tool does not support).
My JWT has "alg":"none", is that dangerous?
- Yes, historically a major vulnerability. alg:none means the token is unsigned, and any application that accepts it blindly will trust forged tokens. Modern libraries reject alg:none by default. Seeing it in a token from a production service is a red flag: the issuer should never emit unsigned tokens.
What's the difference between HS256 and RS256?
- HS256 is symmetric (HMAC with a shared secret): fast, but anyone who can verify can also sign. RS256 is asymmetric (an RSA key pair): the issuer signs with the private key and consumers verify with the public key. OAuth and OpenID Connect use RS256 (or ES256) precisely because the public key can be distributed safely. Use HS256 only for internal same-service tokens where a shared secret makes sense.
How do I check if my JWT is expired?
- Read the badge: green "expires in 23 minutes" while the token is still valid, red "EXPIRED 2 hours ago" once it is not. The badge comes from the `exp` claim, a Unix timestamp in seconds, and the token is expired when `exp < Date.now() / 1000`. A token with no `exp` claim never expires, which is usually a misconfiguration since most issuers should set one.
Can I decode a JWT in Node, Python, or with curl?
- Yes, in one line each. Node: `Buffer.from(token.split('.')[1], 'base64url').toString()`. Python: `import base64,json; json.loads(base64.urlsafe_b64decode(token.split('.')[1] + '=='))`. Bash: `echo $TOKEN | cut -d. -f2 | base64 -d | jq` (add `=` padding if needed). All three decode the payload only; for verification use jose (Node), pyjwt (Python) or a CLI like jwt-cli.
What's the difference between JWT and JWE?
- A JWT (JSON Web Token, RFC 7519) is signed: the payload is Base64-encoded and readable by anyone, since signing only proves who issued it. A JWE (JSON Web Encryption, RFC 7516) is encrypted: the payload is unreadable without the decryption key. JWTs have three parts (header.payload.signature), JWEs have five (header.encryptedKey.iv.ciphertext.tag). This tool decodes JWTs only; a JWE needs the recipient's private key and cannot be opened by a generic web tool.
JWT signing algorithms: what to use and when
The `alg` field in a JWT header tells you how the token was signed. Quick reference for the algorithms you'll encounter in production tokens.
| alg | Type | Key material | Typical use | Notes |
|---|---|---|---|---|
| HS256 | HMAC-SHA-256 (symmetric) | Shared secret ≥ 32 bytes | Internal same-service tokens, single-tenant apps | Fast. Anyone who can verify can also sign, so never distribute the secret |
| HS384 | HMAC-SHA-384 (symmetric) | Shared secret ≥ 48 bytes | Same as HS256 with stronger hash | Rare. HS256 is sufficient for almost all cases |
| HS512 | HMAC-SHA-512 (symmetric) | Shared secret ≥ 64 bytes | High-security symmetric tokens | Rare |
| RS256 | RSA-PKCS1-v1_5 + SHA-256 | RSA key pair (2048-bit minimum) | OAuth 2.0, OpenID Connect, public-facing APIs | Default for Auth0, Okta, AWS Cognito, Firebase. Public key can be shared via JWKS |
| RS384 | RSA-PKCS1-v1_5 + SHA-384 | RSA 2048+ | As RS256 with stronger hash | Low adoption |
| RS512 | RSA-PKCS1-v1_5 + SHA-512 | RSA 2048+ | As RS256 with stronger hash | Low adoption |
| ES256 | ECDSA P-256 + SHA-256 | EC P-256 key pair | Modern OAuth, mobile apps, Apple Sign In | Smaller signatures (64 bytes vs RSA's 256+). What most modern issuers prefer |
| ES384 | ECDSA P-384 + SHA-384 | EC P-384 key pair | High-security ECDSA | Low adoption |
| ES512 | ECDSA P-521 + SHA-512 | EC P-521 key pair | Very-high-security ECDSA | Low adoption |
| EdDSA | Ed25519 (or Ed448) | 32-byte Ed25519 key | Newer alternative to ECDSA: fast, constant-time | Growing adoption. Supported by jose, jsonwebtoken (with plugins), pyjwt 2.6+ |
| PS256 | RSA-PSS + SHA-256 | RSA 2048+ | More secure RSA padding than RS256 | Recommended over RS256 by some specs (FAPI). Verify your library supports it |
| none | (unsigned) | (none) | Debugging only | NEVER accept in production: historical major vulnerability (CVE-2015-9235). Reject explicitly |
Defined by RFC 7518 (JOSE / JWA). Most identity providers default to RS256; modern stacks (Apple, Firebase, newer Auth0 apps) increasingly use ES256 or EdDSA for smaller tokens and better mobile performance.