A JWT (JSON Web Token) is a compact, self-contained token used to represent claims between two parties — most commonly between a client and a server for authentication. When a user logs in, the server creates a JWT and returns it to the client, which then sends it with every subsequent request. The server can verify and read the token without a database lookup.
The Three Parts of a JWT
A JWT is three Base64URL-encoded strings separated by dots. Here is a real JWT broken into its parts:
The Header
The header is a Base64URL-encoded JSON object containing the token type and the signing algorithm. Decoded, the header above is:
alg tells you which algorithm was used to sign the token — HS256 (HMAC-SHA256) is symmetric (same key to sign and verify), while RS256 (RSA-SHA256) is asymmetric (private key to sign, public key to verify). Never accept a token with "alg": "none" — this is a known attack vector.
The Payload
The payload contains the claims — statements about the subject (usually the user) and metadata. Decoded:
Standard registered claims include sub (subject), iss (issuer), exp (expiration time), iat (issued at), nbf (not before), and aud (audience). Custom claims — like name, role, or email — can be added freely.
The payload is readable by anyone. It is Base64URL-encoded, not encrypted. Do not put sensitive data in a JWT payload unless the token is encrypted (JWE). A JWT used for authentication should contain only what is needed — user ID, role, expiry.
The Signature
The signature is computed from the encoded header, encoded payload, and a secret key. For HS256: HMAC-SHA256(base64url(header) + "." + base64url(payload), secret). It proves the token was not tampered with — changing any character in the header or payload will produce a different signature, which the server will reject. But the signature does not encrypt the content.
Decoding a JWT Without a Library
Because the header and payload are just Base64URL-encoded JSON, you can decode them in any language without a JWT library — useful for debugging or inspecting tokens in environments where you cannot install packages.
Checking Token Expiry
The exp claim is a Unix timestamp. If the current time is past exp, the token is expired and should be rejected. The nbf (not before) claim is the opposite — the token is not valid before this timestamp.
JWT Security Considerations
Decoding a JWT is not the same as verifying it. Anyone can decode the payload — that is by design. Verification means checking the signature against the secret key, confirming the token was issued by who it claims, and checking that it has not expired. Always verify tokens on the server. Client-side decoding is for inspection only, not for making trust decisions.
The alg: none attack: some older JWT libraries would accept a token with the algorithm set to none, skipping signature verification entirely. Always check that your library rejects alg: none explicitly. Use the Hash Generator if you need to generate HMAC values manually for debugging.
Frequently Asked Questions
- Can anyone read the contents of a JWT?
- Yes — the header and payload are Base64URL-encoded, not encrypted. Anyone who intercepts the token can decode and read the claims. This is why JWTs should only be transmitted over HTTPS, and sensitive data should not be included in the payload. The signature proves the token is authentic and untampered, but it does not hide the contents.
- What is the difference between HS256 and RS256?
- HS256 (HMAC-SHA256) uses a single shared secret — the same key is used to both sign and verify the token. This is simpler but requires both the issuing service and the verifying service to know the secret. RS256 (RSA-SHA256) uses a key pair — the issuer signs with the private key and any verifier can check with the public key. RS256 is preferred in distributed systems where multiple services verify tokens but only one should issue them.
- How do I invalidate a JWT before it expires?
- JWTs are stateless by design — the server does not store them, so there is no built-in revocation mechanism. Common workarounds are: maintaining a blocklist of revoked token IDs (
jticlaim), using short expiry times (15 minutes) combined with refresh tokens, rotating the signing secret (invalidates all tokens at once), or storing the last password change time and rejecting tokens issued before it. - Where should I store a JWT in the browser?
- This is a genuine security trade-off.
localStorageis accessible to JavaScript, making it vulnerable to XSS attacks.httpOnlycookies are not accessible to JavaScript, protecting against XSS, but are vulnerable to CSRF (mitigated withSameSite=Strictor CSRF tokens). For most applications,httpOnlycookies withSameSite=Strictis the recommended approach. Avoid storing JWTs inlocalStorageif your application handles sensitive data.