# JWT decoder

URL: https://www.toolflux.app/en/jwt-decoder/
Stand: 2026-06-05

JSON Web Token (JWT) decoder with security checks. Flags what's off with your token: alg: none, expired, weak HS256 secret, PII in the payload.

A JWT (JSON Web Token) is a signed data packet in three parts: header, payload, signature. Reading one isn't a hack, since header and payload are base64url, not encryption. What's usually more interesting than the contents is what's wrong with the token: expired, `alg: none`, an email in the payload, a weak HS256 secret. You see those flags above the payload here, before you start digging through claims yourself.

## What's actually inside your JWT?

A signed JWT in JWS compact form has three segments separated by dots: header, payload, and signature. Header and payload are base64url-encoded JSON objects - readable without a key, broken out above with coloured underlines on the pasted token (cyan, violet, coral). Encrypted JWTs in JWE compact form have five segments; those aren't accepted here on purpose.

The header almost always carries the signing algorithm (`alg`), the token type (`typ`, almost always `JWT`), and optionally a key ID (`kid`) so the receiver can pick the matching public key from its JWKS. The payload carries the actual claims - the seven standard ones registered in RFC 7519 §4.1, plus one common extension:

- `sub` - the subject, usually a user ID. Stable across logins, not human-readable.
- `iss` - the issuer, who minted the token. Trust starts here.
- `aud` - the audience, who the token is for. Your API checks that it's in the list.
- `iat` / `exp` / `nbf` - timestamps in Unix seconds: issued at, expires at, not before.
- `jti` - a unique token ID, useful for revocation lists.
- `scope` - OAuth permissions, often space-separated. Not in RFC 7519 itself, but later registered in IANA's JWT Claims registry via RFC 8693.

These claims plus `azp` (an OIDC claim) and the common header fields (`alg`, `typ`, `kid`) get a small `?` chip - hover or tap for the one-sentence definition with its spec section. For `iat`, `exp`, and `nbf`, the Unix timestamp renders alongside a UTC datetime + relative phrase (`in 4m 12s` or `4m 12s ago`), so you don't have to switch tabs for the conversion.

## You don't need the secret to decode a JWT

The header and payload of a JWT are base64url-encoded, not encrypted. Reading them only needs the token itself - no secret, no public key. A key is only relevant if you want to verify the signature, i.e. confirm the token actually came from the issuer it claims and wasn't tampered with on the way.

Many decoders still ask for the key the moment you paste a token. That keeps the myth alive that you can't see the contents without a secret - and trains the reflex to drop a real key into a foreign input field.

Here it works the other way around: reading is the default view, signature verification sits below in a closed disclosure and stays that way until you actively open it. Only then does the key field appear. That matches the most common workflow: grab a token from a network tab or a log, peek at the contents, done. The signature is the server's concern, not yours.

If you do want to verify, `HS256/384/512` (shared secret), `RS256/384/512` and `PS256/384/512` (RSA with a PEM public key), and `ES256/384/512` (elliptic curve) all run through your browser's Web Crypto API. `alg: none` stays unsupported on purpose - the pitfall check above flags it, and verification would just be theatre.

## `alg: none` is not a joke - and your decoder doesn't warn you

`alg: none` is a valid JWT header value: no algorithm, no signature. The JWT spec allows it because integrity can, in some setups, be secured by the enclosing data structure (RFC 7519 §6). A library that accepts `alg: none` blindly lets every payload mutation slip through - the classic auth bypass.

The attack pattern is trivial:

1. Rewrite `alg` in the header from `RS256` to `none`.
2. Set `sub` in the payload to `admin` (or whichever claim the server uses for authorisation).
3. Drop the signature - third segment, empty string.
4. Send it. If the server accepts what the header claims rather than checking against an explicit allowlist, the token goes through.

RFC 8725 (the JWT BCP) has said the same thing since 2020: implementations should accept only algorithms on an explicit allowlist, never reading it from the header. The bug keeps coming back anyway. CVE-2026-23993 (HarbourJwt) is the latest example: the verification path accepted unrecognised `alg` values (including `none`) without filtering them. The same "unknown alg" bypass shape keeps showing up in different libraries year after year.

The status pill above marks `alg: none` in red the moment the header declares it - whether you want to verify or not, you see it. Seven of seven competitors we audited don't flag this; they show the decoded header and leave the interpretation to you. If you fished the token out of a log, that's a security check you'd otherwise miss.

## Production token in a web tool? Four questions first

Jamie Tanna's argument against online tooling roughly goes: "We're teaching people to blindly trust arbitrary websites that they don't have any relationship with." For a JWT the risk is concrete: dropping a real production access token into a foreign browser tool is potentially a data leak. Either because the tool sends the token to a server (it happens), because the tool puts it in the URL (it happens), or because the browser history-syncs it to your other devices (it happens).

Three rules this decoder holds itself to, against exactly that:

1. Your token is not uploaded, not logged, not passed on.
2. The token never lands in the URL - not while typing, not on share. There's intentionally no "Share token" button.
3. If the token looks like production (email in the payload, UUID-shaped values, long `exp`, known issuer like `*.auth0.com` or `accounts.google.com`), opening the signature-verify disclosure shows a nudge: "Looks like a production token. Sure?". One click dismisses it - but the question got asked.

Four questions worth running through yourself before pasting a real token:

1. Do I actually need the production token? For local debugging, a test token from the dev issuer is usually enough.
2. Who runs the tool? Toolflux is a small solo project from Germany. Imprint and contact in the footer.
3. Is the token stored anywhere? Here, no - not in the URL, not in browser storage, not in server logs.
4. If something goes wrong: can I rotate the token? If the answer is "no, that's a long-lived refresh token with admin scope", that token doesn't belong in a web tool.

## Frequently Asked Questions

### What do sub, aud, iss mean in a JWT?

`sub` (Subject) is the ID of whoever the token is about, usually a user ID. `aud` (Audience) is who the token is for: your API server checks that it's in that list. `iss` (Issuer) is who minted the token, the auth server. Trust starts at `iss`. Each of these claims carries a `?` chip in the decoder with the definition and spec section.

### Do I need the secret to decode a JWT?

No. The header and payload are just base64url, not encryption. Reading them only needs the token itself. You need the secret or public key only to verify the signature.

### What is `alg: none` and why is it dangerous?

`alg: none` is a valid JWT header value: no algorithm, no signature. Servers that blindly trust whatever the header claims let arbitrary payload mutations slip through. The status pill above marks `alg: none` in red whenever the header declares it.

### Is my JWT stored anywhere?

No. Your token isn't uploaded, isn't persisted in the URL, isn't logged anywhere.

### What's the difference between HS256 and RS256?

HS256 (HMAC-SHA256) is symmetric: sender and receiver share the same secret. RS256 (RSA-SHA256) is asymmetric: only the issuer signs with their private key, anyone verifies with the public key. RS256 is the right choice as soon as more than one service has to accept the token.

### FAQ

**What do sub, aud, iss mean in a JWT?**

`sub` (Subject) is the ID of whoever the token is about, usually a user ID. `aud` (Audience) is who the token is for: your API server checks that it's in that list. `iss` (Issuer) is who minted the token, the auth server. Trust starts at `iss`. Each of these claims carries a `?` chip in the decoder with a one-sentence definition and the relevant spec section.

**Do I need the secret to decode a JWT?**

No. The header and payload are just base64url, not encryption - reading them only needs the token itself. You need the secret or public key when you want to verify the signature, i.e. to confirm the token actually came from the issuer it claims and hasn't been tampered with. That's why verification here is opt-in and closed by default - you don't have to upload a key just to read what's inside.

**What is `alg: none` and why is it dangerous?**

`alg: none` is a valid JWT header value: no algorithm, no signature. Servers that blindly trust whatever the header claims let arbitrary payload mutations slip through - the classic bypass. The status pill above marks `alg: none` in red the moment the header declares it. The fact that this bug still ships in production libraries in 2026 (CVE-2026-23993) is the reason for the check.

**Is my JWT stored anywhere?**

No. Your token isn't uploaded, isn't persisted in the URL, isn't logged anywhere. There's also intentionally no Share button that packs the token into a link: a JWT in a URL is a security footgun (logs, Referer headers, history sync across devices).

**What's the difference between HS256 and RS256?**

HS256 (HMAC-SHA256) is symmetric: sender and receiver share the same secret. Anyone with the secret can sign AND verify. RS256 (RSA-SHA256) is asymmetric: only the issuer signs with their private key, anyone verifies with the public key. RS256 is the right choice as soon as more than one service has to accept the token - otherwise every service has to know the signing secret.
