JWT Decoder
Decode any JWT token to see its header, payload, and signature. Stays in your browser โ tokens are never sent anywhere.
What a JWT actually is
A JSON Web Token (JWT) is three Base64URL-encoded parts joined by dots: header.payload.signature. The header and payload are JSON objects that anyone can decode โ they're not encrypted, just encoded. The signature proves the token wasn't tampered with, and verifying it requires the secret/key.
Common header fields
algโ algorithm: HS256 (HMAC-SHA256), RS256 (RSA-SHA256), ES256 (ECDSA), or none.typโ always "JWT".kidโ key ID, when the verifier needs to know which key to use.
Common payload claims
issโ issuer (who created the token)subโ subject (who the token is about โ usually a user ID)audโ audience (who the token is for)expโ expiration time (Unix timestamp)iatโ issued at (Unix timestamp)nbfโ not before (Unix timestamp โ token invalid before this time)jtiโ JWT ID (unique identifier, prevents replay)
Anyone with a JWT can read its contents. Never put sensitive data in the payload โ passwords, PII, secrets. The signature only proves authenticity, not confidentiality. If you need confidentiality, use JWE (JSON Web Encryption) instead.
What JWTs actually are
A JSON Web Token is a compact way to represent claims that are cryptographically signed. It's used everywhere in modern web authentication: OAuth flows, single-sign-on tokens, API keys for services, session tokens for single-page apps. Once you know how they work, you can debug a whole class of authentication problems that would otherwise be opaque.
A JWT is three parts separated by dots. Each part is Base64URL-encoded. The first part is a header (JSON describing which algorithm signed the token). The second part is a payload (JSON containing the actual claims โ who the user is, when the token expires, what permissions they have). The third part is a signature that proves the token wasn't tampered with, computed over the first two parts using a secret key or a private key.
Anyone with a JWT can read what's inside it. The signature prevents modification but does not encrypt the content. If you're storing sensitive data in a JWT payload, that data is visible to anyone who intercepts the token. This is a source of persistent confusion โ Base64 is not encryption.
Reading a JWT: what to look for
The header
Most importantly, the header specifies the signing algorithm. The alg field tells you what algorithm was used to sign the token. Common values are RS256 (RSA with SHA-256), ES256 (ECDSA with SHA-256), HS256 (HMAC with SHA-256), and PS256 (RSA-PSS with SHA-256). The kid field, if present, identifies which key was used to sign โ the receiver looks this up to find the matching public key for verification.
An alg value of "none" is a red flag. It literally means no signature, and there's a whole class of JWT vulnerabilities where a receiver naively accepts an unsigned token. Any JWT with alg:none should be rejected out of hand in production code. Some libraries handle this correctly; some don't.
The payload
The payload is a JSON object containing "claims" โ key-value pairs describing the token holder or the token itself. There's a standard set of claim names defined in RFC 7519, plus whatever custom claims the issuer chose to include.
Standard claims worth knowing:
- iss โ issuer. The service that generated the token. Verifying this matches your expected issuer prevents cross-service token confusion.
- sub โ subject. Usually the user ID or entity the token represents.
- aud โ audience. Who the token is intended for. If your service receives a token whose aud is a different service, reject it.
- exp โ expiration time, as a Unix timestamp. If the current time is past this, the token is expired.
- nbf โ "not before" time. The token isn't valid until this timestamp.
- iat โ issued at. When the token was created.
- jti โ JWT ID. A unique identifier for the token, used for revocation and replay protection.
Custom claims vary by issuer. OAuth access tokens often include scope (what permissions), preferred_username (human-readable name), email, and various roles or groups. Some services put a lot in the token; some put very little.
The signature
The signature is the third part of the token. It's the output of running the signing algorithm over the concatenation of the header and payload (with a dot between them). Verifying the signature requires the public key (for asymmetric algorithms) or the shared secret (for symmetric algorithms). The signature check is what makes JWTs trustworthy โ a modified token fails signature verification.
This decoder tool doesn't verify signatures. It only shows you what's inside. Signature verification requires access to the signing key, which isn't something a general-purpose tool can have. If you need to verify a JWT signature, use a library in your programming language of choice with the appropriate public key.
Common JWT debugging scenarios
Token rejected as invalid but you can't tell why
Decode the token here first. Check exp โ is it in the past? Check aud โ does it match your service? Check iss โ is it the expected issuer? Any of these mismatches will cause rejection.
Also check the alg field. Some services rotate signing algorithms; some libraries validate against a specific algorithm and reject others. A token signed with RS256 rejected by a service expecting HS256 will fail signature verification silently.
User is logged in but missing permissions
Decode the access token to see what scopes or roles it actually contains. Sometimes a login flow succeeds but the resulting token doesn't include the permissions the user should have โ usually a bug in the authorization server's claim mapping.
Token appears to work in one environment but not another
Check the aud field. If your dev environment issues tokens with aud=example-dev and your staging environment expects aud=example-staging, tokens don't cross environments. This is intentional (prevents accidentally using dev tokens in prod) but confusing when you're not expecting it.
Session times out sooner than expected
The exp claim is authoritative for when the token expires. If your service is enforcing shorter timeouts than the exp value, something is layered on top โ a separate session store, a refresh token flow with shorter TTL, or a middleware doing its own timeout logic.
What NOT to put in a JWT
Because JWT payloads are readable by anyone with the token, they should never contain:
- Passwords, obviously.
- Payment card details.
- Social security numbers, government IDs, or similar highly sensitive personal data.
- Full user profile data with contact info, address, etc. โ even if the token is only sent over HTTPS, it might be logged, cached, or leaked through various side channels.
- Any secret that shouldn't be visible to the user themselves. Users can decode their own tokens easily.
JWTs should contain identifiers and claims about permissions, not the full data those identifiers refer to. The service using the token should look up any sensitive data from a secure store using the identifiers in the token.
Common vulnerabilities and how to avoid them
Accepting alg:none
Some JWT libraries in early versions would accept a token with alg:none and skip signature verification entirely. This is completely broken behavior but historically shipped in production libraries. Modern libraries reject alg:none by default; older versions might not. If you're implementing JWT verification, always specify the allowed algorithms explicitly rather than trusting the header.
Algorithm confusion (RS256 vs HS256)
A specific vulnerability where an attacker changes a token's algorithm from RS256 (asymmetric) to HS256 (symmetric), then signs a modified payload using the RSA public key as the HMAC secret. A naive receiver that uses the algorithm from the header for verification will accept it. The fix is to always specify the expected algorithm when verifying, not read it from the header.
Missing signature verification
Simply not checking the signature. Rare in modern frameworks but has appeared in custom code. Any JWT that isn't signature-verified is effectively a client-controlled data blob โ the user can change any claim.
Trusting a JWT after a user's permissions changed
JWTs are stateless by design. Once issued, they're valid until they expire, regardless of what happens to the user's account. If a user's admin role is revoked, tokens issued before revocation still claim admin until they expire naturally. High-security systems mitigate this with short token lifetimes and refresh flows, or with an explicit revocation list checked on every request.
Long-lived tokens without expiration
A JWT without an exp claim, or with a very distant exp, becomes a de facto permanent credential. If it leaks, you have no revocation mechanism short of rotating the signing key (which invalidates every other token too). Short token lifetimes with refresh flows are the standard defense.
Related tools
If you're working with JWTs, you're often also working with Base64 encoding โ the pieces of a JWT are Base64URL-encoded. The Base64 Encode/Decode tool handles both standard and URL-safe variants.
If you need to inspect the TLS certificate of the service issuing a JWT (to check the expected signing key), the SSL/TLS Cert Inspector pulls certificate details for any HTTPS URL.
Privacy of decoded tokens
This tool decodes JWTs entirely in your browser. Nothing is sent to any server. The token you paste stays local. This is important because production JWTs contain real user identifiers and shouldn't be transmitted to third-party services. You can verify this by opening browser developer tools before pasting a token and watching that no network requests are made when you click Decode.
