What this decoder shows
JWT Decoder inspects a compact three-segment string: it Base64URL-decodes the first two segments, JSON.parse’s them, pretty-prints the objects, and displays the third segment as-is. It does not verify signatures, does not take a secret or public key, and does not decide whether a token should be trusted.
The Encoded JWT field starts empty. Decode token is disabled while the trimmed textarea is empty. Load example fills a three-segment sample and decodes it immediately. Clear empties the textarea, the active token, and the Copied label.
JWT structure: header, payload, and signature
decodeJwt trims the string and splits on “.”. If parts.length is not 3, it throws “A JWT must contain exactly 3 parts separated by dots.” Typical five-part encrypted tokens therefore fail this check. There is no JWE or nested-JWT path.
- Header
- First segment: Base64URL → UTF-8 text → JSON.parse. Shown in the Header card as JSON.stringify(header, null, 2).
- Payload
- Second segment: same decode and parse. Shown as Payload / Claims with the same 2-space stringify.
- Signature
- Third segment: not passed through atob or JSON.parse. If the segment is empty, hasSignature is false and the UI says “No signature segment found.” Otherwise the raw segment is shown and the UI says it is present.
- Type label
- tokenType is header.typ, or the string “JWT” if typ is missing. That default is a label, not a detected token class.
What Base64URL decoding means here
base64UrlToBase64 replaces - with + and _ with /. If the length modulo 4 is 2 it appends “==”; if 3, “=”; if 0, it leaves the string; any other remainder throws “Invalid Base64URL string.” decodeBase64Unicode then calls atob and TextDecoder().decode. Only the header and payload segments use this path.
This is not the generic Base64 tool: there is no URL-safe checkbox, no encode mode, and the signature is not decoded. Padding restore applies only inside this Base64URL helper.
How the tool handles malformed tokens
- Empty trimmed input: decodeJwt returns no error and no objects. The error banner is shown only when decoded.error is set and activeToken is non-empty.
- Wrong segment count: “A JWT must contain exactly 3 parts separated by dots.”
- Bad Base64URL padding (remainder 1): “Invalid Base64URL string.”
- atob or JSON.parse failures: error.message, or “Invalid JWT.” if message is missing. Header and payload are cleared.
- Whitespace: only the whole token is trimmed. Spaces or line breaks inside a segment are not stripped the way Base64Tool strips decode input.
There is no implemented character ceiling in JwtDecoder.jsx. Header and payload cards stay hidden until header and payload both parse without error.
Reading common claims
Token summary appears after a successful decode. It reads specific properties; it does not validate them.
- Algorithm
- header.alg, or “—” if missing. Not checked against the signature.
- Type
- tokenType as above (typ or “JWT”).
- Subject / Issuer / Audience
- payload.sub, iss, and aud via formatClaimValue: empty/null/undefined → “—”; objects → JSON.stringify; otherwise String(value).
- Other claims
- Visible in the payload JSON card (for example nbf or custom fields). The summary does not list them and does not enforce nbf.
Time-based claims and date conversion
formatDateFromUnix treats iat and exp as Unix seconds: it requires a finite number, then new Date(value * 1000) and date.toLocaleString(). Non-numeric values show “—”.
getTokenStatus uses only exp. Missing exp → “No expiry claim.” Non-finite exp → “Invalid token.” exp < Math.floor(Date.now() / 1000) → “Expired.” Otherwise → “Active.” iat is displayed but not used for status. nbf is not read. Active/Expired is a local clock comparison of a decoded number, not proof that a real issuer accepted the token.
What decoding does not verify
The sidebar Verification details text states: this decoder shows header and payload JSON; it does not verify signatures or prove that a token is trusted. There is no key field, HMAC, RSA/ECDSA check, or JWKS lookup in JwtDecoder.jsx.
- Authenticity and integrity: a forged header/payload can still decode.
- Issuer and audience: iss and aud are displayed; they are not compared to an expected value.
- Expiration as a security decision: Expired/Active does not mean the token is valid or invalid at an API.
- Authorization or session trust: claims are text after JSON.parse, not permissions.
- Revocation, key rotation, or algorithm confusion defenses: not implemented.
- Server-side verification: this page is not a substitute for verifying the token with the issuer’s key in your backend.
The signature panel copy says verification is optional and requires a key. That key step is not implemented here; showing the third segment is not verification.
Handling sensitive tokens responsibly
The amber sidebar says: do not paste production secrets, private keys or sensitive tokens unless you understand the risk. A JWT often carries session or identity data. Decoding reveals whatever the header and payload contain to this browser tab.
Avoid pasting production credentials, customer tokens, or private keys into a shared, logged, or untrusted environment. Clear the fields when you finish. Copy writes JSON to the clipboard when the API allows it; that clipboard is outside this component.
Privacy and local-processing boundaries
JwtDecoder.jsx decodes in the browser. It does not call trackToolEvent and does not POST the token to a verification or decode API. That is not a confidentiality guarantee: the rest of the page can still load site assets and advertising; the privacy policy describes first-party analytics for event metadata (not designed to store tool contents); clipboard use, extensions, and shared devices remain outside this component.
Local atob and JSON.parse do not encrypt the token or isolate it from the browser environment. Decoding is not safe handling of secrets.
When to use Base64 or JSON Formatter instead
- JSON Formatter — to pretty-print JSON text that is not wrapped as a three-segment JWT, or to re-format copied header/payload JSON with indent 2/4/8. This decoder already stringifies with indent 2 and does not parse arbitrary JSON documents.
- Base64 Encoder/Decoder — for generic text Base64/URL-safe encode or decode. It does not split JWT segments or JSON.parse header/payload.
- Timestamp Converter — if you copy a raw numeric iat, exp, or other Unix value from the payload JSON and need seconds/milliseconds conversion. This decoder already formats iat and exp with toLocaleString() from Unix seconds; it does not open the converter automatically.
Output-review checklist
- Treat header and payload as readable JSON only, never as a verified token.
- If Status is Active, remember it only means numeric exp >= now in this browser.
- If Status is Expired or No expiry claim, that still does not prove the signature or issuer.
- Confirm you needed three segments; two- or five-part strings fail the split check.
- Do not paste production or customer tokens on a shared device; use Clear when done.
- If Copy does not change to Copied, the clipboard API may have been denied; there is no download fallback.