
Access Token Lifetime: Choosing an Actual Number
A practical examination of access token lifetime settings, covering JWT expiry, refresh token strategies, and security best practices for backend developers.
Most backend developers treat access token lifetime as a philosophical debate about trust. They argue whether 15 minutes is "too secure" or 1 hour is "too convenient." This is a category error. Access token lifetime is a resource management problem, not a trust problem. The correct number is determined by the computational cost of validating the token versus the potential damage of a stolen credential.
If you are issuing JSON Web Tokens (JWTs) or opaque bearer tokens, you must configure an exp (expiration) claim. But what number belongs there? Let’s look at the mechanism.
The Cost of Validation
Configuring the access token lifetime is a resource management problem, not a trust problem. An access token is a bearer credential. Whoever holds it can act as you. The primary security mechanism against token theft is brevity. If a token is stolen via XSS or a network sniff (on non-TLS connections, though that’s rare now), the attacker has a limited window to use it before it becomes useless.
The correct access token lifetime is determined by the computational cost of validating the token versus the potential damage of a stolen credential. However, there is a secondary mechanism: cryptographic validation. If you are using JWE (encrypted JWTs), a short lifetime limits how long the decrypted contents remain useful to an attacker, though it does not make the encryption key easier or harder to brute-force. If you are using JWS (signed JWTs), the signature is static, so the lifetime doesn’t help with cryptographic breaking, but it still limits the operational window of abuse.
Consider the trade-off. Every time a server receives a request, it must verify the token. This involves:
- Parsing the header and payload.
- Verifying the signature (RSA/ECDSA operations).
- Checking the
exp,nbf, andissclaims. - Checking revocation lists (if applicable).
This process is cheap, but not free. In high-throughput systems, validating a 1-hour token is no more expensive than validating a 5-minute token. The computational cost is identical. The difference is purely in the security window. Therefore, from a pure performance perspective, access token lifetime doesn’t matter. From a security perspective, shorter is better.
But we cannot set it to 1 second. That would require constant re-authentication, destroying user experience. The industry standard compromise emerged from analyzing typical attack vectors: session hijacking via XSS. Most XSS attacks allow immediate script execution. A 5–15 minute window is often sufficient to mitigate the impact of a delayed exploit or a user who logs out and immediately notices anomalous activity.
The Refresh Token Handshake
The refresh token lifetime is a separate configuration from the access token, typically set to days or weeks. You cannot ask users to re-enter their passwords every 15 minutes. This is where the refresh token enters the architecture. The refresh token is a long-lived credential used solely to obtain new access tokens. It is never sent to the API for business logic; it is only sent to the authorization endpoint.
There are two primary models for handling this:
Stateless Refresh Tokens
The refresh token is itself a JWT. The server validates the signature and checks expiration.
- Pros: No database lookup required for issuance. Scales horizontally easily.
- Cons: Cannot be revoked until it expires. If stolen, the attacker has access for the duration of the refresh token (e.g., 7 days).
Stateful Refresh Tokens
The refresh token is a random string stored in a database (Redis, SQL) linked to the user session. The refresh token lifetime should be configured to match your revocation strategy.
- Pros: Can be revoked instantly. Supports rotation.
- Cons: Requires storage infrastructure. Adds latency for validation.
The Rotation Mechanism Best practice dictates refresh token rotation. When a client presents a refresh token to get a new access token:
- The server validates the old refresh token.
- The server issues a new access token.
- The server issues a new refresh token.
- The server invalidates the old refresh token.
This creates a chain. If an attacker steals a refresh token, they can use it once. The next time they present it, it will be invalid because the legitimate client already rotated it. This effectively limits the window of exposure for refresh tokens to the time between requests.
Choosing the Number
So, what is the actual number for your access token? Determining the correct token expiration policy requires balancing security against user experience.
5–15 Minutes (Recommended)
For most modern web applications using single-page applications (SPAs) or mobile apps, an access token lifetime of 5 to 15 minutes is optimal. This aligns with security best practice for modern web applications.
- Why: It balances the negligible cost of validation with a tight security window.
- Mechanism: The client silently refreshes the token in the background using the refresh token before it expires. The user never notices the swap.
- Risk: If the refresh token is stored in
localStorage(vulnerable to XSS), the attacker can also refresh access tokens. However, the short lifetime still limits the damage of any single access token.
1 Hour (Legacy/High-Trust)
Some older systems use 1-hour access tokens.
- Why: To reduce the number of refresh calls.
- Risk: A stolen token is valid for 60 minutes. In that time, an attacker can perform hundreds of actions. This is generally unacceptable for sensitive applications unless combined with strict IP binding or device fingerprinting, which adds complexity.
10 Minutes (High Security)
Financial or healthcare applications may use 10-minute access tokens.
- Why: Minimizes the window for automated scraping or account takeover.
- Risk: Higher load on the authorization server due to frequent refresh requests.
Implementation Details
When configuring jwt expiry, ensure you explicitly set the exp claim. Do not rely on default values. Ensure token expiration is handled correctly in your JWT library.
// Example: Node.js with jsonwebtoken
const payload = {
sub: user.id,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (15 * 60), // 15 minutes
scope: 'read write'
};
const accessToken = jwt.sign(payload, privateKey, { algorithm: 'RS256' });For refresh tokens, use a high-entropy random string (at least 128 bits) if storing them statefully. Do not use JWTs for refresh tokens if you require revocation capability.
Conclusion
Stop guessing. Set your access token lifetime to 15 minutes. Use stateful refresh tokens with rotation. This configuration provides the best balance of security, performance, and user experience. Any deviation requires a specific threat model justification, not a preference for convenience. Following these best practice guidelines ensures a secure authentication flow.
Related posts
Securing Server-Sent Events with OAuth2 Authentication
An examination of securing server-sent events using OAuth2 authentication to ensure real-time data integrity.
Spring Security with JWT: Complete Authentication Implementation
A complete guide to implementing Spring Security with JWT, covering authentication, refresh tokens, and JWT filtering strategies.
acr_values and amr: Requesting Authentication Strength
Explore how acr_values and amr claims define authentication strength in OpenID Connect, enabling step-up authentication and MFA enforcement.