JWT Token Expiration Check: Correct Implementation and Common Errors Analysis

Nov 21, 2025 · Programming · 9 views · 7.8

Keywords: JWT | Token Expiration | Timestamp | Authentication | JavaScript Security

Abstract: This article provides an in-depth exploration of JSON Web Token (JWT) expiration checking mechanisms, analyzing common time format processing errors when using jwt.decode, presenting correct validation methods based on jwt.verify, and comparing the advantages and disadvantages of various implementation approaches. Through detailed code examples and principle analysis, it helps developers understand JWT exp claim format, timestamp conversion principles, and security verification best practices.

Core Issues in JWT Token Expiration Checking

In JSON Web Token (JWT)-based authentication systems, token expiration checking is a critical component for ensuring security. Developers often encounter time format processing errors when using the jwt.decode method, leading to ineffective expiration judgment logic.

Timestamp Format Confusion and Solutions

The JWT standard specifies that the exp claim uses Unix timestamp format, representing the number of seconds since January 1, 1970, 00:00:00 UTC. Meanwhile, JavaScript's Date.now() method returns the number of milliseconds. This unit difference is the fundamental cause of expiration check failures.

Original erroneous code:

if (exp < (new Date().getTime() + 1) / 1000) {
  return false;
}

Correct implementation:

if (Date.now() >= exp * 1000) {
  return false;
}

By multiplying exp by 1000 to convert it to milliseconds and comparing it with the current timestamp, we ensure consistency in time units.

Using jwt.verify for Comprehensive Validation

While direct decoding can check expiration time, jwt.verify provides more comprehensive security validation. It not only checks if the token has expired but also verifies the signature's validity, preventing tampering attacks.

Recommended secure validation code:

function isAuthenticated() {
  const token = localStorage.getItem('token');
  try {
    jwt.verify(token, secret);
    return true;
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      // Handle token expiration logic
      return false;
    }
    // Handle other validation errors
    return false;
  }
}

Alternative Manual Decoding Approaches

In certain scenarios where JWT libraries are unavailable, expiration checking can be implemented through manual decoding. This approach requires handling Base64URL encoding and JSON parsing.

Browser environment implementation:

function isTokenExpired(token) {
  const payloadBase64 = token.split('.')[1];
  const base64 = payloadBase64.replace(/-/g, '+').replace(/_/g, '/');
  const jsonPayload = decodeURIComponent(
    atob(base64)
      .split('')
      .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
      .join('')
  );
  const { exp } = JSON.parse(jsonPayload);
  return Date.now() >= exp * 1000;
}

Node.js environment implementation:

function isTokenExpired(token) {
  const payloadBase64 = token.split('.')[1];
  const decodedJson = Buffer.from(payloadBase64, 'base64').toString();
  const { exp } = JSON.parse(decodedJson);
  return Date.now() >= exp * 1000;
}

JWT Structure Analysis and Security Considerations

JWT consists of three parts: Header, Payload, and Signature. The exp field in the payload is the standard expiration time claim. According to RFC 7519 standard, all time-related claims should use NumericDate format (Unix timestamp).

Security considerations:

Performance and Implementation Recommendations

In practical applications, it's recommended to prioritize official library validation methods to ensure security and compatibility. For high-performance scenarios, consider caching validation results to avoid repeated decoding operations.

Complete authentication function example:

async function checkAuthentication() {
  const token = localStorage.getItem('token');
  const refreshToken = localStorage.getItem('refreshToken');
  
  if (!token) return false;
  
  try {
    // Verify main token
    const decoded = jwt.verify(token, secret);
    return true;
  } catch (err) {
    if (err.name === 'TokenExpiredError' && refreshToken) {
      // Token expired, attempt refresh
      return await refreshAccessToken(refreshToken);
    }
    return false;
  }
}

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.