JWT Excessive Token Lifetime Vulnerability
JWTs without exp or with multi-year lifetimes turn a single leak into permanent credential exposure. Learn recommended token lifetimes, why short exp matters, and refresh-token rotation.
A JWT without an `exp` claim is valid until the signing key rotates. A JWT with `exp` set one year in the future is the same thing in practice. If that token appears in a log file, a monitoring service, browser history, or a compromised device, the exposure window is the full remaining lifetime. Long-lived access tokens turn a single leak into a long-lasting credential exposure.
How the attack works
The attacker obtains a long-lived JWT through any capture vector: a log file, a proxy, a compromised device, a URL parameter, an XSS vulnerability. Because the token's `exp` is far in the future (or absent), the token remains valid for the entire window. The attacker uses it freely until it expires. With no `exp` at all, the token is valid forever unless the signing key rotates, which may be months or years. Short of key rotation, there is no way to invalidate the token before its natural expiry without extra revocation infrastructure.
The fix
Set access token `exp` to the minimum lifetime your UX allows: 15 minutes for standard APIs, 5 minutes or less for high-security contexts, up to 60 minutes for web sessions. Never issue access tokens without `exp`. Pair short-lived access tokens with longer-lived refresh tokens that are tracked server-side and can be revoked immediately. Always include `jti` if you may need per-token revocation. Define a rotation and expiry strategy and enforce it.
Code examples
The fix: issue a correctly-lifetime access token (Node.js)
const accessToken = jwt.sign(
{
sub: userId,
role: user.role,
jti: crypto.randomUUID(), // for revocation capability
},
secret,
{
algorithm: "HS256",
expiresIn: "15m", // never omit exp
issuer: "https://auth.example.com",
audience: "https://api.example.com",
}
); Recommended access token lifetimes
Context Recommended lifetime
High-security API (banking, healthcare) 5 minutes
Standard API access token 15 minutes
Web session access token 15 to 60 minutes
Refresh token 7 to 30 days
Password reset / email verification 10 to 30 minutes Frequently asked questions
-
What is the recommended JWT access token expiry time?
15 minutes is the standard for most APIs. High-security contexts (banking, healthcare) use 5 minutes or less. Standard web sessions can use up to 60 minutes. Never issue access tokens without an exp claim. Short lifetimes limit the damage if a token leaks, since a compromised token expires quickly without needing active revocation. Pair short-lived access tokens with longer-lived refresh tokens tracked server-side for instant revocation capability.
-
Why are long-lived JWTs dangerous?
A JWT is a bearer token: possession alone is enough to use it. A long-lived or non-expiring token captured through any vector (log, proxy, compromised device, XSS) remains valid for the full window. With no exp at all, the token is valid forever unless the signing key rotates. Short of key rotation, there is no way to invalidate it before natural expiry without per-token revocation infrastructure (a jti blocklist). Short exp is the single most effective automatic defense against token theft.
-
How do I revoke a JWT before it expires?
Three strategies work in practice: (1) short expiry alone: compromised tokens expire quickly with no infrastructure; (2) a jti blocklist in Redis with TTL equal to the remaining token lifetime, checked on every authenticated request; (3) token versioning: a per-user counter in your database that invalidates all older tokens at once. The standard production pattern is short access token expiry (15 min) paired with server-side refresh token rotation, which gives short blast radius on leak, long sessions, and instant revocation through the refresh token database.