How a JWT works
A JSON Web Token (RFC 7519) is three pieces of text joined by dots. The header says how the token is signed, the payload holds the claims, such as who the user is and when the token expires, and the signature proves that both were made by someone with the key. Header and payload are JSON written in Base64url, a variant of Base64 that is safe in URLs.
header.payload.signature
header = base64url(UTF-8 JSON, e.g. {"alg":"HS256","typ":"JWT"})
payload = base64url(UTF-8 JSON with the claims)
signature = base64url(HMAC-SHA256(secret, header + "." + payload)) Base64url is an encoding, not encryption: anyone who has a token can read its payload, exactly as this page does, without any key. So never put passwords or other secrets in a JWT. What the key protects is the signature: change one character of the payload and the signature no longer matches.
With HS256, HS384 and HS512 the issuer and the server share one secret and sign with HMAC; this page can check those when you enter the secret. RS256, ES256 and the other public-key algorithms are signed with a private key and checked with the issuer's public key, which a server usually gets from a JWKS URL. Decoding a token is not the same as trusting it: a server must check the signature, the expiry and the audience.
Example
Take jwt.io's well-known example, eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c. The first part decodes to the header {"alg":"HS256","typ":"JWT"}: an HMAC-SHA256 signature. The second decodes to the payload {"sub":"1234567890","name":"John Doe","iat":1516239022}. Its iat of 1516239022 means it was issued on 18 January 2018 at 01:30:22 UTC, and with no exp it never expires. The last part is the 32-byte HMAC-SHA256 of the first two parts and the dot between them; with the secret your-256-bit-secret it matches, so the signature is valid.
The registered claims
| Claim | What it means |
|---|---|
iss · Issuer | Who made and signed the token, usually the login service's URL. |
sub · Subject | Who the token is about, usually a user ID. |
aud · Audience | Who the token is meant for. A server should reject a token whose audience is not itself. |
exp · Expires at | After this moment the token must be rejected. Seconds since 1 January 1970 UTC. |
nbf · Not before | Before this moment the token must be rejected. Seconds since 1 January 1970 UTC. |
iat · Issued at | When the token was made. Seconds since 1 January 1970 UTC. |
jti · Token ID | A unique ID for this token, so a server can block it or notice it being used twice. |
Questions
Is it safe to paste my JWT here?
Yes. The token is decoded by JavaScript in your browser, and the signature is checked with your browser's built-in WebCrypto. Neither the token nor the secret is sent to a server or stored. Still treat a live token like a password: anyone who has it can use it until it expires.
Can a JWT be decoded without the secret?
Yes. The header and payload are only Base64url-encoded JSON, so anyone can read them without a key. The secret or public key is needed only to check that the signature is genuine. That is why a JWT must not contain passwords or other secrets.
How do I check if a JWT is expired?
Look at the exp claim: it is the expiry time in seconds since 1 January 1970 UTC. If the current time is past it, the token has expired. This page reads exp and nbf for you and shows the result as a date and as “expired 3 days ago” or “expires in 2 hours”.
What is the difference between HS256 and RS256?
HS256 signs with HMAC and one shared secret: whoever can check a token can also make one. RS256 signs with an RSA private key and is checked with the public key, so services can verify tokens without being able to issue them. RS256 or ES256 is the usual choice when several services check the same tokens.
Why is my JWT signature invalid?
The secret is wrong, it is Base64-encoded and needs the “Base64-encoded” option, or the token was changed after signing. Copying a token can also add or drop characters. If the token uses RS256, ES256 or another public-key algorithm, it cannot be checked with a secret at all: it needs the issuer's public key.