Security

Token-Based Authentication: How It Works and Why Developers Use It

10 min read
By
Token-Based Authentication: How It Works and Why Developers Use It

NovelCrypt

Token-based authentication is the default session management approach for modern web applications and APIs. If you have built a React app with a backend API, used a mobile app with OAuth, or configured CI/CD pipelines, you have worked with tokens. Understanding how they work — and where they fail — is essential for building secure systems.

What Token-Based Authentication Is

In token-based authentication, the server does not store session state. Instead, after a user logs in successfully, the server issues a signed token that the client stores and sends with each subsequent request. The server validates the token on each request by checking its signature and claims, without looking up session state in a database.

The most common token format is JWT (JSON Web Token), defined in RFC 7519. JWTs encode claims (key-value pairs about the user and the session) in a compact, URL-safe format that can be signed with a symmetric key (HMAC) or an asymmetric key pair (RSA or ECDSA).

### How It Works Step by Step

1. The user submits credentials (username and password) to a login endpoint. 2. The server verifies the credentials against its user database. 3. If valid, the server creates a JWT containing claims (user ID, roles, expiration time) and signs it with a secret key or private key. 4. The server sends the JWT to the client. 5. The client stores the token (in localStorage, sessionStorage, or an httpOnly cookie). 6. For each subsequent request, the client includes the token — typically in the Authorization header as "Bearer <token>". 7. The server verifies the token signature, checks the expiration, and extracts the claims to identify the user and their permissions. 8. The server processes the request based on the authenticated user and their authorization level.

Authentication vs Authorization

These two concepts are conflated constantly, and the confusion causes real security problems.

Authentication is the process of verifying who a user is. It answers the question "are you who you claim to be?" When a user enters a password and the server checks it against a hash, that is authentication. In a JWT, the sub (subject) claim holds the authenticated user identifier.

Authorization is the process of determining what an authenticated user is allowed to do. It answers the question "do you have permission to perform this action?" When the server checks whether a user can access a specific resource, that is authorization. In a JWT, the scope, roles, or permissions claims carry authorization information.

A common mistake is treating authentication as sufficient for access control. A user who authenticates successfully is not automatically authorized to do everything. A logged-in customer should not be able to access admin endpoints just because their token is valid. Every protected endpoint must check both that the token is valid (authentication) and that the token's claims permit the requested action (authorization).

### JWT Claims for Auth and Authz

| Claim | Purpose | Category | | --- | --- | --- | | sub | Subject (user ID) | Authentication | | iss | Issuer (who created the token) | Validation | | exp | Expiration time | Validation | | iat | Issued at time | Validation | | scope | Granted permissions | Authorization | | roles | User roles (admin, user) | Authorization | | aud | Intended audience | Validation |

Token vs Session-Based Authentication

Session-based authentication stores state on the server. After login, the server creates a session record (in memory, a database, or a session store like Redis) and sends the client an opaque session ID (typically in a cookie). On each request, the server looks up the session ID to find the user.

| Property | Session-Based | Token-Based | | --- | --- | --- | | State | Server-side | Stateless (in token) | | Storage | Cookie (automatic) | Client-side (manual) | | Revocation | Immediate (delete session) | Requires denylist or short expiry | | Scaling | Requires shared session store | No shared state needed | | Mobile/API | Requires cookie handling | Works with any client | | CSRF risk | Higher (cookies auto-sent) | Lower (tokens sent manually) | | XSS risk | Lower (httpOnly cookies) | Higher (if token in localStorage) |

Neither approach is universally better. Sessions are simpler to revoke and work well for traditional server-rendered web applications. Tokens are better for stateless APIs, microservices, mobile apps, and single-page applications where maintaining server-side session state is impractical.

JWT Structure

A JWT has three parts separated by dots: header.payload.signature

### Header

The header specifies the token type and signing algorithm:

{"alg": "HS256", "typ": "JWT"}

This is Base64URL-encoded to form the first part of the token. The alg field tells the server which algorithm to use for verification — this is where HS256 vs RS256 matters.

### Payload

The payload contains the claims:

{"sub": "user-123", "name": "Alice", "iat": 1727712000, "exp": 1727712600, "scope": "read:posts write:posts"}

This is Base64URL-encoded to form the second part. The payload is not encrypted — anyone with the token can read it. Do not put sensitive data in a JWT payload unless you are using encrypted JWTs (JWE, RFC 7516).

### Signature

The signature is computed over the Base64URL-encoded header and payload:

HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)

For RS256, the signature uses the RSA private key, and verification uses the public key. The signature ensures the token has not been tampered with. If anyone modifies the payload, the signature will not match. You can generate JWT secrets and HMAC keys using the JWT secret generator and HMAC generator.

Token Storage: Security Trade-Offs

Where you store the token on the client is one of the most consequential security decisions in a token-based system.

### localStorage

Storing tokens in localStorage is the most common approach in SPAs because it is easy to access from JavaScript. The major risk is XSS: any cross-site scripting vulnerability in your application can read the token from localStorage and exfiltrate it. If you use localStorage, you must be rigorous about XSS prevention — content security policies, input sanitization, and careful handling of user-generated content.

### httpOnly Cookies

Storing tokens in an httpOnly cookie protects against XSS-based theft because JavaScript cannot access httpOnly cookies. The trade-off is that cookies are automatically sent with every request to the origin domain, which creates CSRF risk. You must use SameSite attributes and CSRF tokens to mitigate this. This approach works well but requires more configuration.

### sessionStorage

sessionMemory is similar to localStorage but is cleared when the tab closes. This limits the token lifetime, which is good for security, but also means the user loses their session when they close the tab. Use this for short-lived sessions where persistence is not needed.

### Memory Only

Storing the token in a JavaScript variable (not persisting it anywhere) is the most secure option against XSS — a compromised script on another page cannot read a variable in your application's memory. The trade-off is that the token is lost on page refresh, which degrades user experience. This approach is viable when paired with a silent refresh mechanism using a refresh token stored in an httpOnly cookie.

| Storage | XSS Risk | CSRF Risk | Persists | Complexity | | --- | --- | --- | --- | --- | | localStorage | High | Low | Yes | Low | | httpOnly cookie | Low | Medium | Yes | Medium | | sessionStorage | High | Low | Tab only | Low | | Memory only | Low | Low | No | High |

When NOT to Use Tokens

Tokens are not always the right choice. Avoid token-based authentication when:

You need immediate revocation. Stateless JWTs cannot be revoked before their expiration without a server-side denylist. If you need to immediately invalidate a user's session (after a password change, a security incident, or account deletion), server-side sessions are simpler. If you must use JWTs, keep access tokens very short (5 minutes) and use revocable refresh tokens.

Your tokens would contain sensitive data. JWT payloads are Base64URL-encoded, not encrypted. Anyone who obtains the token can read its contents. If you need to include sensitive information, use encrypted JWTs (JWE) or keep the data server-side and reference it by token subject.

You are building a traditional server-rendered application. For server-rendered apps where the backend and frontend are the same origin, session cookies are simpler, more secure, and require less infrastructure than tokens.

Your system does not need statelessness. If you have a single server with a database and no plans for horizontal scaling, the complexity of token-based auth is not justified.

Common Security Pitfalls

### Algorithm Confusion

JWT implementations must verify that the token's alg header matches the expected algorithm. A classic attack (CVE-2015-9235 and variants) sends a token with alg set to "none" or switches from RS256 to HS256, tricking the server into using the public key as an HMAC secret. Always explicitly specify the expected algorithm and reject tokens with mismatched headers. Read our JWT security pitfalls guide for more on this and other attacks.

### Long-Lived Access Tokens

Access tokens that last for hours or days create a large window of exposure if stolen. Keep access tokens to 5-15 minutes and use refresh tokens for session continuity. Rotate refresh tokens on each use to detect theft.

### Weak Signing Keys

HMAC-signed JWTs are only as secure as the signing key. A weak or predictable secret can be brute-forced offline, allowing an attacker to forge tokens. Use a cryptographically random secret of at least 256 bits for HS256. For RS256, use at least 2048-bit RSA keys. Read our JWT secret key best practices for detailed guidance.

### Not Validating All Claims

A valid signature does not mean the token is acceptable. You must also check exp (not expired), iss (issued by the expected authority), aud (intended for your service), and any custom claims relevant to your application. Failing to check aud can allow a token issued for one service to be used on another.

### Storing Tokens in URLs

Some implementations pass tokens as URL query parameters (e.g., ws://api.example.com?token=...). This exposes the token in browser history, server logs, and Referer headers. Use the Authorization header instead. If you must use URLs for WebSocket authentication, use a short-lived token and switch to a different authentication mechanism after the connection is established.

Building It Right

For a production token-based system, follow these practices:

Use RS256 or ES256 for signing so the API can verify tokens with the public key without the signing server sharing its private key. This allows multiple services to verify tokens independently. Read HS256 vs RS256 for the full comparison.

Implement secret rotation so you can change signing keys without invalidating all active sessions.

Use short-lived access tokens (5-15 minutes) with refresh tokens. Store the refresh token in an httpOnly cookie and the access token in memory.

Validate every claim on every request. Do not cache token validation results unless you have a revocation mechanism.

For the cryptographic foundations — how symmetric and asymmetric encryption relate to JWT signing — read our symmetric vs asymmetric encryption guide. For choosing the right HMAC algorithm for token signing and request verification, see our HMAC-SHA256 vs SHA-512 comparison. And for a different authentication model that predates tokens, see our LDAP authentication guide.

Frequently Asked Questions

What is token-based authentication?

Token-based authentication is a method where, after a user logs in, the server issues a signed token (typically a JWT) that the client includes with each subsequent request. The server validates the token signature and claims to authenticate the request without storing session state server-side. This makes it suitable for stateless APIs and distributed systems.

What is the difference between authentication and authorization?

Authentication verifies who you are (proving identity, typically via username and password). Authorization determines what you are allowed to do (checking permissions and access levels). In JWT terms, the sub claim identifies who you are (authentication), while the scope or roles claims specify what you can access (authorization). Authentication answers "who are you?" and authorization answers "what can you do?"

Are tokens more secure than sessions?

Neither is inherently more secure. Server-side sessions are harder to steal because the session ID is opaque and state is server-controlled. Tokens contain claims that a client can read, which is fine for non-sensitive data but means you must not store secrets in them. Tokens are better for stateless and distributed architectures; sessions are better when you need immediate revocation. The security depends on implementation, token storage, and rotation practices.

How long should a JWT token last?

Access tokens should be short-lived — 5 to 15 minutes for most applications. This limits the window of exposure if a token is stolen. Pair short-lived access tokens with longer-lived refresh tokens (hours to days) that can be revoked server-side. Avoid access tokens lasting hours or days, as there is no way to revoke a stateless JWT before it expires without a server-side denylist.

Explore the Share Password Securely: Try it now

Try NovelCrypt Tools

Experience military-grade encryption for your sensitive data. Create self-destructing messages, encrypt files, or explore our experimental lab tools.

Explore NovelCrypt