Understanding JSON Web Tokens: Developer Tutorial
JSON Web Tokens are the most widely used authentication mechanism for modern web APIs. This tutorial walks through JWT fundamentals, implementation patterns, security considerations, and common mistakes — with practical advice you can apply immediately.
JWT Fundamentals
A JWT is a compact, URL-safe token consisting of three Base64url-encoded parts separated by dots: the header, the payload, and the signature. The header specifies the signing algorithm and token type. The payload carries claims — key-value pairs containing the authentication data. The signature ensures the token has not been tampered with.
The critical point many developers miss: JWTs are signed, not encrypted. Anyone with the token can decode and read the header and payload. The signature only proves the token was created by someone with the signing key and has not been modified. Do not store sensitive information like passwords, credit card numbers, or personal data in JWT payloads.
Creating JWTs
In Node.js, the jsonwebtoken package is the most popular library for creating and verifying JWTs. The basic pattern is: create a payload object with user information and claims, sign it with a secret key and options (algorithm, expiration), and return the token string.
The payload should include standard claims: sub (subject, usually user ID), iat (issued at, set automatically), exp (expiration time), and iss (issuer, your application identifier). Add custom claims for application-specific data like user roles, permissions, or tenant ID. Keep the payload small — JWTs are sent with every request, so large payloads increase bandwidth usage.
Verifying JWTs
Verification checks three things: the signature is valid (the token was created with the correct key), the token has not expired (exp claim is in the future), and the claims are acceptable (issuer, audience, etc. match expected values). The jsonwebtoken verify function handles all of this.
Always verify the algorithm. The alg header field specifies which algorithm was used to sign the token, but a well-known attack involves changing the algorithm to none (no signature) or switching from RS256 (asymmetric) to HS256 (symmetric) using the public key as the secret. Always specify the expected algorithm in your verification options.
Access Tokens and Refresh Tokens
The standard pattern uses two types of tokens. Access tokens are short-lived (15 minutes to 1 hour) and carry the user claims needed for API authorization. Refresh tokens are long-lived (days to weeks) and are used exclusively to obtain new access tokens when the current one expires.
Access tokens are sent in the Authorization header with every API request. Refresh tokens are stored in httpOnly, secure, sameSite cookies and sent only to the token refresh endpoint. This separation limits exposure — if an access token is stolen, it expires quickly. If a refresh token is stolen, it can be revoked server-side.
Token Storage
Where you store tokens matters for security. For web applications, store access tokens in memory (a JavaScript variable) and refresh tokens in httpOnly cookies. Do not store tokens in localStorage or sessionStorage — they are accessible to any JavaScript running on the page, making them vulnerable to XSS attacks.
For mobile applications, use the platform secure storage — Keychain on iOS, EncryptedSharedPreferences on Android. For server-to-server communication, store tokens in environment variables or a secrets manager.
Common JWT Mistakes
Using weak signing secrets is the most common mistake. HS256 secrets should be at least 256 bits of cryptographic randomness, not a dictionary word or simple phrase. Use a cryptographically secure random generator to create secrets.
Not validating the audience (aud) claim allows tokens intended for one service to be used on another. Always set and validate the audience claim in multi-service architectures.
Setting excessively long expiration times defeats the purpose of JWTs. If an access token expires in 30 days, a stolen token has 30 days of unauthorized access. Keep access token lifetime under one hour.
Not implementing token revocation is a design decision, not an oversight — but make it intentionally. If you need instant session termination (for security incidents, password changes, or account deactivation), you need a server-side token blocklist or session store, which partially negates the stateless benefit of JWTs.
When Not to Use JWTs
For server-rendered applications (traditional web apps with server-side sessions), session cookies are simpler and more secure. You get instant revocation, smaller cookie size, and no need for refresh token infrastructure. JWTs add complexity without clear benefit in this architecture.
For microservice-to-microservice authentication within a trusted network, mTLS (mutual TLS) or service mesh identity is more appropriate than JWTs. Use JWTs at the API gateway for external client authentication, and mTLS between internal services.
Debugging JWTs
Use our JWT Decoder at AIDToolStack to inspect tokens during development. Paste a token and see the decoded header, payload, all claims with human-readable timestamps, and expiration status. The decoder runs entirely in your browser — safe for inspecting production tokens with sensitive claims.
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.