//Tools  ·  /tools/jwt/about

← Back to the tool

About JSON Web Tokens.

A practical explainer for anyone who has ever copied a JWT out of a log line, an Authorization: Bearer header, or a cookie and wondered what its claims actually say — and what they don’t.

1. What is a JWT?

A JSON Web Token, or JWT, is a compact, URL-safe way to represent a small set of claims about a subject — typically a user, a service account, or a session — so that those claims can be passed safely across HTTP headers, URL fragments, or cookies. The format is three base64url-encoded segments separated by dots, and by far the most common variant on the open web is the signed flavor (JWS): a header that names a signing algorithm, a payload that contains the claims as JSON, and a signature computed by the issuer over the first two parts.

JWTs exist because stateless authentication needs a token that a recipient can trust without consulting a session store. With a valid signature and the issuer’s key, any service that receives a JWT can independently confirm both who issued it and that the claims have not been tampered with — useful when the caller and the recipient are different services, owned by different teams, possibly inside different organisations. That property is why almost every OAuth 2.0 / OpenID Connect stack on the modern web ends up emitting JWTs as ID tokens, access tokens, or both.

2. A brief history

The earliest drafts of what became JWT circulated through the IETF starting in 2010 and 2011, with contributors from Microsoft, Ping Identity, Google, and the wider OAuth working group. The format always came as a small bundle of companion specifications: JWS (the signature container), JWE (the encryption container), JWK (the key representation), and JWA (the algorithm registry). Together, the bundle was formally published in May 2015 as a family of RFCs — RFC 7515 (JWS), 7516 (JWE), 7517 (JWK), 7518 (JWA), and RFC 7519[1] for JWT itself.

Mainstream adoption rode the wave of OAuth 2.0 and OpenID Connect from roughly 2014 to 2016. By the late 2010s, JWTs had become the default token format for nearly every B2B SaaS auth product, most consumer identity providers, and a long tail of internal microservice meshes. The format has its critics — the implementation history is full of footguns — but its place in the ecosystem is now firmly load-bearing, and the chance that a backend engineer can spend a year on the modern web without encountering one is essentially zero.

3. Anatomy of a JWT

A compact JWS — the case this tool targets — is a string with exactly three segments separated by two dots:header.payload.signature. Each segment is encoded with base64url: standard base64 per RFC 4648 §5 with two character swaps (+ becomes -, / becomes _) and trailing = padding stripped. The two character swaps are what make the encoding safe to drop into URLs and HTTP headers without further escaping.

Segment 1, the header, is a JSON object whose only required field is alg — the algorithm used to compute the signature. In practice the header also commonly carries typ (set to JWT) and kid (a key identifier that tells a verifier which key from a JWKS to use). Segment 2, the payload, is another JSON object whose top-level fields are called claims. Segment 3 is the raw signature bytes, base64url-encoded. The signature is computed over the literal string base64url(header) + "." + base64url(payload) using the algorithm declared in the header — which is both the specification’s elegance and its most notorious source of security bugs.

4. Algorithms (and the alg: none story)

The JWS family supports two broad categories of signing algorithm. Symmetric algorithms compute an HMAC with a shared secret: HS256, HS384, and HS512 wrap HMAC around SHA-256, SHA-384, and SHA-512 respectively. Asymmetric algorithms sign with a private key and verify with a public one: RS256, RS384, RS512 (RSA-PKCS1-v1_5), PS256 / PS384 / PS512 (RSA-PSS), ES256 / ES384 / ES512 (ECDSA over the NIST curves), and EdDSA (Ed25519 or Ed448). For most modern stacks the right default is RS256, ES256, or EdDSA — symmetric HS-family signing is fine inside a closed service mesh but invites key-distribution headaches across trust boundaries.

The specification also names the literal alg: none algorithm, which means “there is no signature, the signature segment is empty.” The original intent was to support cases where transport-level integrity was already guaranteed, but its presence in the spec produced the canonical JWT exploit: an attacker takes a real signed token, swaps the header for {"alg":"none"}, empties the signature segment, and tampers the payload — and a naive verifier that trusts the header’s alg field unconditionally accepts it. Several mainstream libraries shipped that bug through 2014–2015, and the resulting wave of advisories[2] permanently changed how JWT libraries are written: never trust the header to pick an algorithm; always require that the expected algorithm match what the verifier was configured for.

5. Standard claims (RFC 7519 §4.1)

The spec reserves a small set of registered claim names and defines their semantics. They are optional but pervasive in real tokens:

  • iss — issuer, a string identifying who minted the token.
  • sub — subject, the principal the token is about (a user id, a service account, etc.).
  • aud — audience, the intended recipient or recipients. A verifier that is not in the audience must reject the token.
  • exp — expiry, the moment after which the token is no longer valid.
  • nbf — not before, the moment before which the token is not yet valid.
  • iat — issued at, the moment the token was issued.
  • jti — JWT ID, a unique identifier for the token (often a UUID), useful for revocation lists and replay protection.

The three time-bearing claims — exp, nbf, and iat — use the NumericDate convention: seconds since the Unix epoch, as a JSON number. Any other field that does not collide with a registered name is a custom (or “private”) claim — most production tokens also carry an email, a list of scopes, role or group memberships, and tenant identifiers. The IANA JWT claim registry coordinates the names of widely-shared custom claims so that two issuers don’t accidentally collide.

6. Decode vs. verify — the central caveat

This is the point the warning banner on the tool page exists to hammer home, and it is worth reading slowly. Decoding a JWT only requires a base64url codec; it reveals what the token says. Verifying a JWT requires the issuer’s signing key — a shared secret for HMAC algorithms, or a public key (typically fetched from a JWKS URL the issuer publishes) for asymmetric ones — and proves the token was issued by that party and not modified in transit. A decoded payload is a claim; a verified payload is a fact. Until the signature has been checked against the right key, the token’s iss, sub, aud, and every other field are unverified strings. Treating them as authoritative for any trust decision is how production incidents happen.

7. Common attacks and pitfalls

JWT-related vulnerabilities cluster around a handful of recurring patterns. The OWASP Cheat Sheet Series enumerates them comprehensively[2]; the short list every implementer should know is:

  • alg: none injection — discussed above. Defence: never let the header pick the verification algorithm.
  • HS / RS algorithm confusion — an attacker rewrites the header to HS256 and signs the token using the RSA public key as if it were an HMAC secret; a verifier that asks “is this HMAC?” and reaches for the configured RSA public key as the secret accepts it. Defence: pin the expected algorithm and the expected key together.
  • kid injection — the kid header field is supposed to point a verifier at the right key, but if the lookup naively uses it as a path or SQL parameter without sanitisation, an attacker can substitute an arbitrary file or row as the key.
  • spoofed jku / x5u URLs — the JWS header allows pointing at a remote JWKS or X.509 chain; naively fetching the URL the token names lets the token bring its own key.
  • PII in payload — JWT payloads are not encrypted by default. Anything the token holder can see, every downstream that sees the token can also see — including logs, referers, and browser history.
  • long-lived tokens — without short expiry windows and a revocation strategy, a leaked JWT is a leaked credential for its entire lifetime.

8. When to reach for this tool

This decoder is built for the situations where a developer needs to see what a token says, knows perfectly well that it does not need to verify it in a browser tab, and would prefer not to paste a possibly-still-valid bearer credential into a third-party website. Concretely: debugging your own application’s tokens; inspecting a token your auth library has already validated server-side; comparing claim values across two issuers or two environments; reading a token pulled out of your own application logs; understanding why a verifier you control rejected an incoming token; teaching someone what a JWT actually is. It is always paired with a separate, key-aware verification step before any trust decision is made.

9. When not to reach for this tool

Never as a substitute for signature verification with a real JWT library on the server — reach for jose in JavaScript, PyJWT in Python, jjwt in Java, or golang-jwt in Go, configure it with the algorithm and key you expect, and call its verification entry point. Never use a browser decoder for production trust decisions. Never “trust” the iss or sub of a token before checking the signature against the issuer’s key. If you find yourself wanting a verification step from this tool, the right move is to walk over to a server with a real library; the right move is not to add a key field to this page.

10. About this tool (the privacy footnote)

This decoder runs entirely in your browser. There is no key field, no JWKS URL field, and no “verify” button by design — exposing verification in a browser tool is the wrong shape of product, because it would invite users to paste both a token and a signing secret into the same web page. The implementation uses only the platform: native atob with a URL-safe replacement step for base64url decoding, the built-in TextDecoder for UTF-8, JSON.parse, and Intl.RelativeTimeFormat for the “2 hours ago” annotations. No analytics SDK on this route ever reads the token field. Once the page has loaded, the tool works offline.

11. References

  1. Jones, M., Bradley, J., & Sakimura, N. (May 2015). JSON Web Token (JWT). IETF RFC 7519. https://www.rfc-editor.org/rfc/rfc7519 — the canonical specification for JWT, including the three base64url-encoded-segment structure, the NumericDate convention, the registered claim names enumerated in §4.1, and the publication date.
  2. OWASP Foundation. JSON Web Token for Java Cheat Sheet (OWASP Cheat Sheet Series). https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html — authoritative collection of the JWT-specific attack patterns this explainer references, including the alg: none family, HS/RS algorithm confusion, kid injection, and the “verify, don’t just decode” guidance the warning banner is built around.

Related tools