JWT Tokens Explained: A Developer's Guide to JSON Web Tokens
What is a JWT?
A JSON Web Token (JWT) is a compact, URL-safe token format used to securely transmit information between two parties as a JSON object. Defined in RFC 7519, JWTs have become the de facto standard for stateless authentication and authorization in modern web applications, APIs, and microservice architectures.
At its core, a JWT is a string composed of three parts separated by dots (.):
- Header — metadata about the token, including the signing algorithm and token type.
- Payload— the actual data (called "claims") that you want to transmit, such as user identity or permissions.
- Signature — a cryptographic hash that ensures the token has not been tampered with.
JWTs are primarily used for two purposes: authentication (proving who the user is) and authorization (determining what the user is allowed to do). Because JWTs are self-contained — they carry all necessary information within the token itself — the server does not need to store session data in a database, making them ideal for distributed systems.
Anatomy of a JWT Token
Let's break down each part of a JWT to understand exactly what's inside. Consider this example token:
Header
The header is a JSON object that is Base64URL-encoded. It typically contains two fields:
alg— the signing algorithm used (e.g.,HS256,RS256).typ— the token type, which is alwaysJWT.
Payload
The payload contains the claims — statements about the user and additional metadata. Claims are categorized into three types: registered, public, and private claims.
subandiatare registered claims defined by the JWT specification.nameandroleare custom (private) claims added by the application.expspecifies when the token expires (as a Unix timestamp).
Signature
The signature is created by taking the encoded header, the encoded payload, a secret key, and applying the algorithm specified in the header. For HMAC SHA-256, the formula looks like this:
The signature serves a critical purpose: it guarantees integrity. If anyone modifies the header or payload after the token is issued, the signature will no longer match, and the server will reject the token. Note that the payload is encoded, not encrypted — anyone can decode and read the payload, but they cannot alter it without invalidating the signature.
Tip: Never store sensitive data (passwords, credit card numbers) in a JWT payload. The payload is only Base64URL-encoded and can be easily decoded by anyone who has the token.
How JWT Authentication Works
JWT-based authentication follows a straightforward flow. Understanding this sequence is essential for implementing it correctly in your applications.
The Authentication Flow
- User logs in— the client sends credentials (username/password) to the server's authentication endpoint.
- Server verifies credentials— the server checks the credentials against the database. If valid, it creates a JWT containing the user's identity and relevant claims.
- Server returns the JWT — the signed token is sent back to the client in the response body (or as an HTTP-only cookie).
- Client stores the token — the client saves the JWT (commonly in an HTTP-only cookie, memory, or localStorage).
- Client sends token with requests — for every subsequent API request, the client includes the JWT in the
Authorizationheader. - Server validates the token — the server verifies the signature, checks the expiration, and extracts the claims to authorize the request.
Example Request Header
The Bearer prefix in the Authorization header tells the server that the token following it is a bearer token — meaning whoever "bears" this token is granted access. The server then decodes the JWT, verifies the signature using its secret key, and processes the request if the token is valid.
Registered Claims
The JWT specification defines a set of registered claims — standardized fields with predefined meanings. While none of them are mandatory, using them ensures interoperability across different systems and libraries.
| Claim | Full Name | Description |
|---|---|---|
iss | Issuer | Identifies the principal that issued the JWT (e.g., your auth server URL). |
sub | Subject | Identifies the subject of the JWT — typically a user ID. |
aud | Audience | Identifies the intended recipients of the JWT (e.g., your API domain). |
exp | Expiration Time | Unix timestamp after which the token must not be accepted. |
nbf | Not Before | Unix timestamp before which the token must not be accepted. |
iat | Issued At | Unix timestamp indicating when the JWT was created. |
jti | JWT ID | A unique identifier for the token, useful for preventing token replay attacks. |
Tip: Always set the exp claim. Tokens without expiration are dangerous — if compromised, they grant permanent access until the signing key is rotated.
JWT vs Session-Based Authentication
Understanding how JWT authentication compares to traditional session-based authentication helps you choose the right approach for your project. Here is a side-by-side comparison:
| Feature | JWT | Session-Based |
|---|---|---|
| Server State | Stateless — no server-side storage needed | Stateful — session stored in memory/database |
| Scalability | Excellent — any server can validate the token | Requires sticky sessions or shared session store |
| Token Size | Larger — contains claims in the token itself | Small — only a session ID is sent to the client |
| Revocation | Difficult — requires blocklist or short expiration | Easy — simply delete the session from the store |
| Cross-Origin (CORS) | Works well — token sent via header | Challenging — cookies have same-origin restrictions |
| Best For | APIs, microservices, mobile apps, SPAs | Traditional server-rendered web applications |
In practice, many modern applications use JWTs for stateless API authentication while leveraging session-based approaches for server-rendered pages. The right choice depends on your architecture, scale requirements, and security needs.
Common JWT Signing Algorithms
The signing algorithm determines how the JWT signature is created and verified. Choosing the right algorithm is a critical security decision. Here are the three most commonly used algorithms:
HS256 (HMAC + SHA-256)
A symmetric algorithm that uses a single shared secret key for both signing and verification. It is fast, simple, and ideal for scenarios where the same server both creates and verifies tokens. You can experiment with HMAC hashing using our HMAC Generator tool.
RS256 (RSA + SHA-256)
An asymmetric algorithm that uses an RSA key pair — a private key to sign tokens and a public key to verify them. This is the preferred choice when multiple services need to verify tokens but only one service should issue them. Common in OAuth 2.0 and OpenID Connect setups.
ES256 (ECDSA + SHA-256)
An asymmetric algorithm based on Elliptic Curve cryptography. ES256 provides the same security level as RS256 but with significantly smaller key sizes and faster signature generation. It is increasingly popular in modern applications and IoT devices.
| Algorithm | Type | Key Size | Best For |
|---|---|---|---|
| HS256 | Symmetric | 256-bit shared secret | Single server, internal APIs |
| RS256 | Asymmetric | 2048-bit RSA key pair | Distributed systems, OAuth 2.0 |
| ES256 | Asymmetric | 256-bit EC key pair | Mobile apps, IoT, modern APIs |
Security Best Practices
JWTs are powerful, but they can introduce serious vulnerabilities if misused. Follow these best practices to keep your JWT implementation secure.
Use Strong Secret Keys
For symmetric algorithms like HS256, your secret key should be at least 256 bits (32 bytes) of cryptographically random data. Never use predictable strings like "mysecret" or "password123". Store secrets in environment variables or a dedicated secrets management service — never hardcode them in your source code.
Set Short Expiration Times
Keep the exp claim as short as practical — typically 15 minutes to 1 hour for access tokens. Use refresh tokens (stored securely as HTTP-only cookies) to issue new access tokens without requiring the user to log in again. This limits the damage window if a token is leaked.
Always Use HTTPS
JWTs are bearer tokens — anyone who possesses the token can use it. Always transmit tokens over HTTPS to prevent man-in-the-middle attacks from intercepting tokens in transit. In production, enforce HTTPS at the infrastructure level and set Secure and SameSite flags on cookies.
Choose the Right Storage Location
Where you store a JWT on the client side has significant security implications:
- HTTP-only cookie — safest option for web apps. The cookie is automatically sent with each request and is not accessible to JavaScript, which protects against XSS attacks.
- localStorage — convenient but vulnerable to XSS. Any JavaScript running on the page can read the token. Avoid this for sensitive applications.
- In-memory (JavaScript variable) — safe from XSS and CSRF, but the token is lost on page refresh. Best combined with a refresh token stored in an HTTP-only cookie.
Prevent XSS and CSRF Attacks
If you store JWTs in cookies, implement CSRF protection using tokens or the SameSite cookie attribute. If you store JWTs in localStorage, implement strict Content Security Policy (CSP) headers and sanitize all user input to prevent XSS. Consider combining both strategies: store tokens in HTTP-only cookies with SameSite=Strict and validate a CSRF token on state-changing requests.
Tip: For added security in multi-factor authentication systems, consider combining JWTs with time-based one-time passwords (TOTP). You can generate TOTP secrets using our TOTP Generator tool.
Common JWT Pitfalls
Even experienced developers make mistakes when implementing JWT authentication. Here are the most common pitfalls to avoid:
Not Setting an Expiration
A JWT without an exp claim remains valid forever — or until you rotate the signing key, which invalidates all outstanding tokens. Always include an expiration time and implement a refresh token strategy for long-lived sessions.
Storing Sensitive Data in the Payload
The JWT payload is Base64URL-encoded, not encrypted. Anyone who obtains the token can decode the payload and read its contents. Never include passwords, credit card numbers, social security numbers, or other personally identifiable information (PII) in your JWT claims. If you need to verify this yourself, try decoding a JWT using our JWT Decoder tool — you will see all claims in plain text.
Warning: Never store passwords, API keys, or other secrets in the JWT payload. The payload is visible to anyone who has the token. Use the payload only for non-sensitive identifiers and metadata.
The "alg: none" Attack
One of the most notorious JWT vulnerabilities is the algorithm none attack. In this exploit, an attacker modifies the JWT header to set "alg": "none" and removes the signature entirely. If the server does not properly validate the algorithm, it may accept the unsigned token as valid.
Always specify the expected algorithm when verifying tokens. Most modern JWT libraries default to a safe behavior, but you should explicitly set the algorithms option to prevent this attack.
Token Size Bloat
Every claim you add to a JWT increases its size. Since the token is sent with every HTTP request (typically in the Authorization header), an oversized JWT can cause performance issues. Some common causes of token bloat include:
- Storing entire user profiles instead of just the user ID
- Including full permission lists instead of role identifiers
- Adding redundant or unused claims
As a rule of thumb, keep your JWT payload under 1 KB. Include only the minimum claims needed for authorization decisions, and look up additional user data from the database when needed.
Decode and Inspect JWTs with BeautiCode
Whether you are debugging an authentication issue, reviewing token claims, or verifying the structure of a JWT, having a quick and reliable decoder is essential. BeautiCode's JWT Decoder tool makes it easy to inspect any JSON Web Token directly in your browser — no server-side processing, no data transmitted.
Here is how to use it:
- Open the JWT Decoder tool.
- Paste your JWT token into the input field.
- Instantly see the decoded header, payload, and signature status.
- Review claims such as
exp,iat, andsubwith human-readable timestamps.
All processing happens entirely on the client side, so your tokens never leave your browser. This makes it safe to decode even production tokens during debugging sessions.
Tip: You can also use the Base64 Decoder to manually decode individual JWT segments. Since each part of a JWT is Base64URL-encoded, decoding the header or payload separately can help you understand the raw structure.
Frequently Asked Questions
Is JWT the same as OAuth?
No. OAuth 2.0 is an authorization framework that defines flows for granting access to resources. JWT is a token format. OAuth 2.0 often uses JWTs as access tokens or ID tokens (especially in OpenID Connect), but the two are not interchangeable concepts. You can use JWTs without OAuth, and OAuth can use token formats other than JWT.
Can a JWT be revoked?
Not directly. Since JWTs are stateless, there is no server-side session to delete. However, you can implement revocation through several strategies: maintain a token blocklist (checking each token against a list of revoked tokens), use short-lived tokens combined with refresh tokens, or rotate signing keys to invalidate all existing tokens at once.
Should I use JWT for sessions?
It depends on your architecture. JWTs work well for API authentication in stateless microservice environments, mobile apps, and single-page applications. For traditional server-rendered applications with simple session requirements, server-side sessions with cookies may be simpler and more secure, since they offer easy revocation and smaller cookie sizes.
What happens when a JWT expires?
When a JWT's exp timestamp has passed, the server should reject the token with a 401 Unauthorized response. The client should then either redirect the user to the login page or, if a refresh token is available, use it to silently obtain a new access token without interrupting the user experience.
Is it safe to decode a JWT in the browser?
Yes, decodinga JWT is perfectly safe. The payload is only Base64URL-encoded and was never meant to be secret. Decoding simply reveals the claims inside the token — it does not compromise the signing key or security of the system. In fact, decoding JWTs on the client side is a common practice for reading user claims or checking expiration before making API calls. Tools like BeautiCode's JWT Decoder perform all processing locally in your browser, so your tokens are never sent to any server.
Related Articles
How to Generate Secure Passwords in 2026: A Complete Guide
Learn why strong passwords matter and how to generate secure passwords using entropy, length, and complexity. Includes practical tips and free tools.
2026-03-23 · 8 min readData FormatsJSON vs YAML: When to Use What — A Developer's Guide
Compare JSON and YAML formats with syntax examples, pros and cons, and use case recommendations for APIs, configs, and CI/CD pipelines.
2026-03-23 · 10 min read