Home/Blog/What is JWT expiration?
Security

What is JWT expiration?

Understand JWT expiration, how the exp claim works, and why token expiration is critical for security and implementing proper refresh mechanisms.

By Inventive HQ Team
What is JWT expiration?

The Concept of JWT Expiration

JWT expiration is a security feature that limits how long a token can be used. The "exp" (expiration time) claim specifies when a token becomes invalid and should no longer be accepted by the server. Once a token expires, presenting it is no longer sufficient for authentication—the user must obtain a new token.

Expiration is critical because it limits the damage if a token is stolen or compromised. An attacker who captures a token gains access only until it expires. Without expiration, a compromised token could be used indefinitely, potentially forever. Expiration provides a natural revocation mechanism even without maintaining a database of invalid tokens.

Most authentication systems use short-lived access tokens (minutes to hours) to minimize the window of vulnerability, combined with longer-lived refresh tokens that can obtain new access tokens. This architecture balances security and user experience—users don't need to log in constantly, but compromised tokens have limited utility.

How JWT Expiration Works

The "exp" claim contains a numeric value representing seconds since the Unix epoch (January 1, 1970). For example, "exp":1735689600 represents January 1, 2025, at 00:00:00 UTC. When the server verifies a JWT, it checks whether the current time is before the expiration time. If the current time is past the expiration time, the token is invalid.

Verification frameworks handle expiration automatically. Most JWT libraries check the exp claim by default and reject expired tokens. In Node.js with jsonwebtoken:

const decoded = jwt.verify(token, secret);
// If token is expired, verify throws an error

The verification automatically rejects tokens where current_time > exp. You don't need to manually check expiration—the library does it.

Calculating expiration times is straightforward. To create a token that expires in one hour:

const payload = {
  sub: 'user_123',
  exp: Math.floor(Date.now() / 1000) + (60 * 60)  // 1 hour from now
};

const token = jwt.sign(payload, secret);

The calculation adds 3600 seconds (one hour) to the current Unix timestamp. This approach works across time zones since Unix timestamps are always UTC.

Choosing Appropriate Expiration Times

Selecting expiration times involves balancing security and user experience. Short expiration times improve security—a compromised token can be used for a shorter period. Long expiration times improve user experience—users don't need to re-authenticate as frequently.

For access tokens used in API requests, typical expiration ranges are 5 minutes to 1 hour. Many modern APIs use 15 minutes, providing a reasonable balance. Very short expiration (5 minutes) offers maximum security but may impact user experience with frequent interruptions.

For browser-based applications, shorter expiration times are generally safe since the entire token is transmitted with each request, making it easier to refresh automatically. A 15-minute access token is reasonable for web applications.

For refresh tokens, much longer expiration (days to weeks) is appropriate since they're used less frequently and stored securely. Refresh tokens might expire in 7 days or even 30 days, but they should still expire eventually. Indefinite refresh token validity creates security risks.

For service-to-service authentication, expiration might be shorter (5 minutes) since services can request new tokens automatically. For background jobs, expiration can be longer since the token is issued and used within a predictable timeframe.

The Refresh Token Pattern

The refresh token pattern elegantly solves the expiration dilemma. Instead of requiring long-lived access tokens, you issue short-lived access tokens and separate, longer-lived refresh tokens:

  1. User logs in and receives both access token (15 minutes) and refresh token (7 days)
  2. Client uses the access token for API requests
  3. When the access token expires, client sends the refresh token to get a new access token
  4. The refresh token can obtain multiple new access tokens before it expires
  5. When the refresh token expires, the user must log in again

This pattern allows users to remain logged in for days while access tokens expire after minutes. If an access token is compromised, it's useful only for 15 minutes. If a refresh token is compromised, it can be used for 7 days but only to obtain new access tokens.

Implementation in Node.js:

// Login endpoint
app.post('/login', (req, res) => {
  const accessToken = jwt.sign(
    { sub: user.id },
    secret,
    { expiresIn: '15m' }
  );

  const refreshToken = jwt.sign(
    { sub: user.id, type: 'refresh' },
    refreshSecret,
    { expiresIn: '7d' }
  );

  res.json({ accessToken, refreshToken });
});

// Refresh endpoint
app.post('/refresh', (req, res) => {
  const refreshToken = req.body.refreshToken;

  try {
    const decoded = jwt.verify(refreshToken, refreshSecret);

    const newAccessToken = jwt.sign(
      { sub: decoded.sub },
      secret,
      { expiresIn: '15m' }
    );

    res.json({ accessToken: newAccessToken });
  } catch (err) {
    res.status(401).json({ error: 'Invalid refresh token' });
  }
});

The refresh token and access token use different secrets, allowing them to be verified independently. The refresh token includes a type claim to distinguish it from access tokens.

Clock Skew and Time Synchronization

JWT expiration verification compares the exp claim to the current server time. If servers have significant time differences, legitimate tokens might appear expired on some servers. Distributed systems must maintain reasonable time synchronization.

Most organizations use NTP (Network Time Protocol) to synchronize time across servers. This keeps time differences within milliseconds. JWT libraries provide a clock skew tolerance to handle minor discrepancies:

jwt.verify(token, secret, { clockTolerance: 10 });  // 10 seconds tolerance

This allows tokens to remain valid for 10 seconds past their expiration time, accommodating minor time differences. Set clock tolerance to a reasonable value (5-30 seconds), not minutes.

Revocation Alongside Expiration

Expiration is a passive revocation mechanism—tokens automatically become invalid at a certain time. However, sometimes you need active revocation before expiration, such as when a user logs out or when suspicious activity is detected.

Several approaches complement expiration-based revocation. The simplest is token blacklisting: maintain a database of revoked token IDs and check it during verification. However, this adds a database lookup to every request, impacting performance.

Alternative approaches include:

  1. JWT ID (jti) and revocation: Include a unique token ID and check it against a revocation list
  2. Short expiration + rotation: Keep expiration short and rotate tokens frequently
  3. Session identifier: Include a session ID and invalidate sessions when needed
  4. Version tracking: Include a version number and increment it to invalidate all old tokens

For simple systems, relying on short expiration without explicit revocation is acceptable. For systems requiring user logout or session termination, implement one of these approaches.

Handling Expired Tokens in Frontend Applications

Frontend applications should detect when tokens expire and handle the situation gracefully. When an API returns a 401 Unauthorized response, the client should:

  1. Check if the access token is expired
  2. If expired, attempt to refresh using the refresh token
  3. If refresh succeeds, retry the original request with the new token
  4. If refresh fails (refresh token expired), redirect to login

This flow allows users to remain logged in as long as their refresh token is valid, transparently refreshing expired access tokens. Implement this in an HTTP interceptor:

// Example using axios
api.interceptors.response.use(
  response => response,
  error => {
    const originalRequest = error.config;

    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      return refreshAccessToken()
        .then(newToken => {
          originalRequest.headers.Authorization = `Bearer ${newToken}`;
          return api(originalRequest);
        })
        .catch(() => {
          // Refresh failed, redirect to login
          window.location.href = '/login';
        });
    }

    return Promise.reject(error);
  }
);

Best Practices for JWT Expiration

Always set expiration times on tokens. Never create indefinitely valid tokens. At minimum, set some reasonable expiration, preferably relatively short.

Use the refresh token pattern for applications with human users. Short-lived access tokens paired with longer-lived refresh tokens provide optimal security and user experience.

Keep access token expiration short—typically 5 minutes to 1 hour depending on your risk tolerance. Shorter expiration times improve security significantly.

Keep refresh token expiration long enough for user convenience but not indefinitely. Consider 7 days to 30 days typical. Log out functionality should invalidate refresh tokens.

Synchronize server time using NTP. Don't rely on client-provided timestamps. The server determines what the current time is for expiration validation.

Don't implement custom clock skew handling beyond what your JWT library provides. Small clock tolerances are reasonable, but large tolerances compromise security.

Implement active revocation mechanisms for scenarios requiring logout before token expiration. Blacklisting or session invalidation handles these cases.

Conclusion

JWT expiration limits the lifetime of tokens, automatically restricting access if a token is compromised. Combined with the refresh token pattern, expiration enables both security and user convenience. Short-lived access tokens expire within minutes, limiting damage from compromise, while longer-lived refresh tokens maintain user sessions. Always set appropriate expiration times, implement the refresh token pattern for applications with users, and maintain server time synchronization. Expiration, combined with signature verification, creates a secure token-based authentication system.

Need Expert IT & Security Guidance?

Our team is ready to help protect and optimize your business technology infrastructure.