JWT Decoder
Runs locallyDecode a JSON Web Token, check its expiry and verify its signature — in your browser.
Token
Paste the token, a whole Authorization header or a quoted string — the extras are removed.
Header
{"alg": "HS256","typ": "JWT","kid": "demo-2026"}Payload
{"iss": "https://auth.example.com/","sub": "user_8f3a21","aud": "api.example.com","name": "Ada Lovelace","scope": "read:orders write:orders","iat": 1767225600,"nbf": 1767225600,"exp": 2556057600,"jti": "b7e1c0d2-3f4a-4c5b-9e6f-1a2b3c4d5e6f"}Claims
What each field means. Times are shown in UTC; JWT times are seconds since 1970.
| Claim | Value | Meaning |
|---|---|---|
| alg · header | HS256 | Algorithm. How the signature was made; "none" means unsigned |
| typ · header | JWT | Type. Media type of the token, usually JWT or at+jwt |
| kid · header | demo-2026 | Key ID. Which key in the issuer’s JWKS signed this token |
| iss | https://auth.example.com/ | Issuer. Who created and signed the token — usually the identity provider’s URL |
| sub | user_8f3a21 | Subject. Whom the token is about: a user or client ID, unique within the issuer |
| aud | api.example.com | Audience. Which API or app the token is meant for; a resource server must reject other audiences |
| name | Ada Lovelace | Name. The user’s full name |
| scope | read:orders write:orders | Scope. Space-separated permissions granted (OAuth 2.0) |
| iat | 1767225600 2026-01-01T00:00:00Z | Issued at. When the token was created |
| nbf | 1767225600 2026-01-01T00:00:00Z | Not before. Before this instant the token must be rejected |
| exp | 2556057600 2050-12-31T00:00:00Z | Expiration time. After this instant the token must be rejected |
| jti | b7e1c0d2-3f4a-4c5b-9e6f-1a2b3c4d5e6f | JWT ID. A unique ID, used to stop the same token being replayed |
Verify the signature
Algorithm from the header: HS256 · kid "demo-2026". Verified with Web Crypto in this tab; the key never leaves it.
Decoding a token is not the same as trusting it
A JSON Web Token (RFC 7519) is three Base64URL segments joined by dots: a header naming the algorithm, a payload of claims, and a signature over the first two. The header and payload are only encoded, so anyone holding the token can read them — never put secrets in a payload. Decoding shows what the issuer claimed; the signature is what proves the issuer actually said it and that nobody changed a byte since.
Verification here runs on Web Crypto without leaving the page. For HS256, HS384 and HS512 the key is a shared secret; tick the Base64 box when your provider hands the secret out encoded, as Auth0 and many others do. For RS, PS and ES algorithms paste the public key as a PEM, a certificate, a JWK, or the whole JWKS from the issuer’s /.well-known/jwks.json, and the key is chosen by the token’s kid. The page never fetches a JWKS itself — a token can name a jku URL, and a verifier that follows it lets the attacker choose the key. To make a token to test against, sign one with the JWT encoder.
Time claims are seconds since 1970. The countdown uses your device’s clock, and a token whose nbf is a few seconds in the future usually means the issuer’s clock is ahead, not that the token is bad; most libraries accept 30 to 60 seconds of leeway. A 13-digit exp is a common bug — milliseconds written where seconds belong — and is flagged, because it makes a token look valid for more than 50,000 years. The Timestamp Converter shows any claim in other time zones.
Token (HS256, secret "your-256-bit-secret")
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Decoded
Header {"alg":"HS256","typ":"JWT"}
Payload {"sub":"1234567890","name":"John Doe",
"iat":1516239022}
iat 2018-01-18T01:30:22Z
exp none — this token never expires
Signature verified with the secretThe well-known example token. Change one character of the payload and verification fails, while decoding still works — which is exactly why decoding alone proves nothing.
Where people get caught
Trusting a decoded token
Any client can build a token with admin: true. A server must verify the signature and check exp, nbf, iss and aud before using a single claim.
alg: none and algorithm confusion
Reject unsigned tokens, and fix the expected algorithm per key. Libraries that let the token pick HS256 with an RSA public key as the "secret" have been exploited.
A Base64-encoded secret used as text
If the provider shows the secret Base64-encoded, the signing key is the decoded bytes. Using the string itself produces a different signature and a confusing "invalid signature".
exp in milliseconds
Date.now() is milliseconds; JWT NumericDate is seconds. An exp of 1767229200000 means the year 57,971, so the token effectively never expires.
Pasting the whole header
Authorization: Bearer eyJ… is not a token. This page strips the prefix and quotes and says so; your own code needs to do the same before verifying.
About JSON Web Tokens
How it works in 4 steps · 4 common use cases · 5 questions answered
About JSON Web Tokens
How it works in 4 steps · 4 common use cases · 5 questions answered
How it works
- 1.Paste a token or an Authorization header; prefixes, quotes and line breaks are removed.
- 2.Read the header and payload, with each claim explained and times converted.
- 3.Watch the expiry countdown and the nbf and iat checks.
- 4.Paste a secret, PEM, JWK or JWKS to verify the signature locally.
Common use cases
- •Finding out why an API returns 401 for a token
- •Checking the scopes, audience and expiry an identity provider issued
- •Verifying a webhook or service-to-service token against a public key
- •Debugging clock skew between an issuer and an API
FAQ
Is it safe to paste a production token here?
The token, secret and keys stay in this tab: decoding and verification run locally with Web Crypto and nothing is sent over the network. A token is still a credential, so treat the screen like any other place it appears.
Can this page verify a JWT signature?
Yes, for HS256/384/512 with a secret (plain or Base64), RS256/384/512 and PS256/384/512 with an RSA key, ES256/384/512 with an EC key, and EdDSA where the browser supports Ed25519.
Why is my token valid here but rejected by the server?
The server also checks the audience, the issuer, the expected algorithm and the key. A mismatch in aud or iss, a rotated key or a clock-skew nbf are the usual causes.
Why won’t it fetch the JWKS for me?
Fetching would send a request from this page, and a token-supplied jku URL is exactly what an attacker controls. Open the issuer’s jwks.json yourself and paste it.
Can it decode an encrypted token (JWE)?
No. A JWE has five parts and its payload is encrypted for the recipient. The page recognises one and shows the header, which is not encrypted.