JWT Debugger

Decode, verify and debug JSON Web Tokens.

Encoded Token

Header

// Header

Payload

// Payload

JSON Web Tokens: structure, claims, algorithms and the mistakes that get exploited

A JSON Web Token (JWT, RFC 7519) is three base64url segments joined by dots: header.payload.signature. The header names the signing algorithm and usually a key id, the payload is a JSON object of claims, and the signature covers the first two segments exactly as they appear in the token. The encoding is base64url with the padding removed (RFC 7515), which is why a token starts with eyJ: that is {" encoded. The signed form is technically a JWS; an encrypted JWT (JWE, RFC 7516) has five segments and cannot be read without the key.

The registered claims

ClaimMeaningNotes
issIssuerWho minted the token, usually a URL. Verify it matches the server you trust.
subSubjectThe user or client the token is about.
audAudienceWhich API the token is for. A token for service A must be rejected by service B.
expExpirySeconds since the Unix epoch, not milliseconds. Reject after this time.
nbfNot beforeReject before this time.
iatIssued atUseful for detecting tokens minted before a password change.
jtiJWT idUnique id, the hook for revocation lists.

The three time claims are NumericDate values in seconds. Paste one into the epoch converter to read it; a 13-digit value there means someone used Date.now() without dividing by 1000, and every verifier will treat the token as valid for about 50,000 years.

What this debugger does, and what it does not

The page uses the jwt-decode library to split the token and decode the header and payload, then pretty-prints both as JSON. The input turns green when the token has the right shape and red with "Invalid JWT Token" when it does not: a missing segment, a segment that is not valid base64url, or a payload that is not JSON. A JWE will also be rejected because it has five segments.

Decoding is not verifying

Anyone can decode a JWT; the payload is only encoded, never hidden. This tool does not check the signature or the expiry, and it never asks for your secret or private key. Verification belongs on the server, in a library that pins the expected algorithm and validates exp, nbf, iss and aud.

Algorithms

HS256 is HMAC with SHA-256 over a shared secret. It is symmetric: every service that can verify a token can also mint one, so the secret must be long (at least 32 random bytes) and shared with as few parties as possible. Short, guessable secrets can be recovered offline from a single captured token with tools like hashcat. RS256 is RSA (PKCS#1 v1.5) with SHA-256, PS256 is RSA-PSS, ES256 is ECDSA on the P-256 curve, and EdDSA uses Ed25519 (RFC 8037). These are asymmetric: the issuer signs with a private key and verifiers only need the public key, typically fetched from a JWKS endpoint such as /.well-known/jwks.json and selected by the header's kid. Use an asymmetric algorithm whenever more than one service verifies tokens.

Attacks worth knowing by name

  • alg none. A header of {"alg":"none"} declares an unsigned token. Early libraries accepted it, letting anyone forge any payload. Modern libraries refuse it, but only if you do not pass it in your allowed-algorithms list.
  • Algorithm confusion. If a verifier accepts both RS256 and HS256 and uses "whatever the header says", an attacker can take the public RSA key, use it as an HMAC secret, and mint HS256 tokens the server accepts. Pin one algorithm per key.
  • Weak HMAC secrets. hashcat mode 16500 brute-forces HS256 secrets from a captured token. Use random secrets, not words.
  • Missing claim checks. Verifying the signature but not exp or aud means old tokens live forever and a token for one API opens another.
  • Untrusted kid, jku and x5u. These header fields tell the verifier where the key is. If they are used to build a file path, a SQL query or an outbound HTTP request without validation, the attacker chooses the key.
  • Replay after logout. A signed token stays valid until exp. Keep access tokens short-lived (minutes), rotate refresh tokens, and keep a denylist keyed by jti for forced logout.

RFC 8725 (JSON Web Token Best Current Practices) collects the defences for all of these and is short enough to read in one sitting. The JWT security guide on this site walks through the same points with code.

Storage and lifetime in the browser

A token in localStorage can be read by any script that runs on the page, including an injected one, so an XSS bug becomes a stolen session. An HttpOnly, Secure, SameSite=Lax or Strict cookie cannot be read by JavaScript, at the cost of needing CSRF defences for state-changing requests. Cookies are also capped at about 4 KB and proxies limit total header size (often 8 KB), which is a reason to keep claims small and put roles in a lookup rather than in the token. Allow a small clock skew (30 to 60 seconds) when checking exp and nbf; two servers with drifting clocks is a common cause of "token expired" the moment it is issued.

Command-line equivalents

# Decode the payload (no verification)
node -e "console.log(JSON.parse(Buffer.from(process.argv[1].split('.')[1],'base64url')))" "$TOKEN"
python3 -c "import jwt,sys; print(jwt.decode(sys.argv[1], options={'verify_signature': False}))" "$TOKEN"   # pip install PyJWT

# Recompute an HS256 signature by hand and compare it with the third segment
printf '%s' "$HEADER.$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64 | tr '+/' '-_' | tr -d '='

How to use

  1. Paste the whole token (the string with two dots, usually starting with eyJ) into the Encoded Token box.
  2. The Header and Payload panels decode immediately and show pretty-printed JSON; the box turns green for a well-formed token and red for a malformed one.
  3. Read the standard claims: iss, sub, aud, exp, nbf, iat and jti, plus any custom claims such as roles or scopes.
  4. Convert exp or iat with the Epoch Converter to see the real date, and check the alg and kid fields in the header against what your server expects.
  5. To verify the signature, use your server-side library with the secret or public key. This page intentionally never asks for keys.

Common use cases

  • Diagnosing a 401 response by checking whether the token is expired, has the wrong audience, or lacks a required scope.
  • Inspecting what an API gateway or identity provider (Auth0, Keycloak, Cognito, Firebase) actually puts in its tokens.
  • Confirming that exp is in seconds and not milliseconds after a change to token-issuing code.
  • Reading the kid header to work out which key in a JWKS document should verify a token.
  • Reviewing a token during a security assessment for secrets, personal data or an alg of none.
  • Teaching new team members the difference between a decoded token and a verified one.

Frequently asked questions

Is it safe to paste a real JWT here?

Decoding runs in your browser and the token is not sent anywhere by this page. A valid token is still a credential, so treat it like a password: prefer a token from a test environment, and if you must inspect a production token, revoke it or let it expire afterwards.

Does this tool verify the signature?

No. Verification needs the shared secret or the public key, and a secret should never be typed into any web page. Use a server-side library such as jsonwebtoken or jose (Node), PyJWT (Python), java-jwt or Nimbus (Java), and pin the allowed algorithms explicitly.

What does "alg": "none" mean and why is it dangerous?

It declares that the token has no signature. If a verifier honours it, anyone can write any payload and be trusted. Reject it outright and only pass the algorithms you actually use to your library.

What should never go into a JWT payload?

Anything secret or sensitive: passwords, API keys, card numbers, personal identifiers like Aadhaar or PAN. The payload is base64url text that anyone holding the token can read. Store a user id and look the rest up server-side.

Why does my server say the token is expired when the time looks right?

Three usual causes: exp was written in milliseconds or in local time instead of Unix seconds, the issuing and verifying servers have drifting clocks and no skew allowance, or the token is being compared against a timezone-converted value. exp must be UTC seconds since 1970.

How do I log a user out if tokens are stateless?

You cannot un-sign a token. Keep access tokens short-lived, use a refresh token that can be revoked server-side, and for immediate logout keep a small denylist of jti values until their exp passes.

HS256 or RS256, which should I choose?

HS256 is fine when a single service both issues and verifies tokens and the secret is 32 or more random bytes. Once other services or third parties need to verify tokens, use RS256, ES256 or EdDSA so the private key stays with the issuer and everyone else only holds the public key.

Why does the debugger reject my token as invalid?

It needs exactly three dot-separated base64url segments with a JSON header and payload. Common causes of rejection are a copied token with the "Bearer " prefix still attached, whitespace or line breaks inside it, a truncated copy, or a five-segment encrypted JWE.

Advertisement