Skip to content

How to Fix JWT Token Expired / Invalid Signature Error

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about How to Fix JWT Token Expired / Invalid Signature Error. We cover key concepts, practical examples, and best practices.

The Problem

Your API client receives:

{
  "error": "TokenExpiredError",
  "message": "jwt expired"
}

Or:

{
  "error": "JsonWebTokenError",
  "message": "invalid signature"
}

The JWT token is expired, or the signature verification failed because the token was tampered with or signed with a different secret.

Quick Fix

1. Check token expiry

Decode the JWT to see the expiration time:

# Decode JWT (without verification)
echo "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDAwMDAwMDB9.signature" | cut -d. -f2 | base64 -d 2>/dev/null

# Look for the "exp" claim — this is the expiry timestamp
// Check if token is expired in JavaScript
function isTokenExpired(token) {
  const payload = JSON.parse(atob(token.split('.')[1]))
  return Date.now() >= payload.exp * 1000
}

2. Refresh the token

// Intercept 401 and refresh automatically
async function fetchWithAuth(url, options = {}) {
  const token = getAccessToken()

  const response = await fetch(url, {
    ...options,
    headers: { ...options.headers, 'Authorization': `Bearer ${token}` }
  })

  if (response.status === 401) {
    // Token expired — refresh it
    const newToken = await refreshToken()
    setAccessToken(newToken)

    // Retry the original request
    return fetch(url, {
      ...options,
      headers: { ...options.headers, 'Authorization': `Bearer ${newToken}` }
    })
  }

  return response
}

3. Fix the secret key

The server must use the same secret key for verification:

// Wrong — different secret for signing and verifying
const jwt = require('jsonwebtoken')

// Signing with one secret
const token = jwt.sign({ userId: 1 }, 'secret1', { expiresIn: '1h' })

// Verifying with another
jwt.verify(token, 'secret2')  // throws invalid signature

// Right — use the same secret
const SECRET = process.env.JWT_SECRET || 'fallback-dev-secret'
const token = jwt.sign({ userId: 1 }, SECRET, { expiresIn: '1h' })
jwt.verify(token, SECRET)

4. Check issuer and audience

If the token includes iss (issuer) or aud (audience), verify them:

// Server-side verification with options
jwt.verify(token, SECRET, {
  issuer: 'https://auth.example.com',
  audience: 'https://api.example.com'
})

If these do not match, the server rejects the token.

5. Use RS256 (asymmetric) signing

For production, use RS256 instead of HS256:

// Generate RSA key pair
const crypto = require('crypto')
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
  modulusLength: 2048,
})

// Auth server signs with private key
const token = jwt.sign({ userId: 1 }, privateKey, {
  algorithm: 'RS256',
  expiresIn: '1h'
})

// API server verifies with public key
jwt.verify(token, publicKey, { algorithms: ['RS256'] })

6. Check for clock skew

The server and client clocks may be out of sync:

// Allow 30 seconds of clock skew
jwt.verify(token, SECRET, {
  clockTolerance: 30  // seconds
})

Prevention

  • Keep token expiry short (15-60 minutes) with refresh tokens.
  • Store the JWT secret in environment variables, not in code.
  • Use RS256 for production to keep verify keys separate from signing keys.
  • Implement automatic token refresh before expiry.
  • Log and monitor JWT verification failures.

Common Mistakes with token expired

  1. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  2. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  3. Using return to exit a function early instead of wrapping a pure value in the monad

These mistakes appear frequently in real-world JWT code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### What causes "invalid signature" in JWT?

The token's signature does not match what the server calculated. This usually means the token was tampered with, or the server is using a different secret/algorithm than the one used to sign it.

Can I decode a JWT without the secret?

Yes. The payload of a JWT is base64-encoded, not encrypted. Anyone can read the payload. Never store sensitive data in a JWT payload. The secret is only needed for verification.

What is the difference between HS256 and RS256?

HS256 uses a single shared secret for signing and verification. RS256 uses a private key for signing and a public key for verification. RS256 is preferred for production because the signing key can be kept private.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro