How to Fix JWT Token Expired / Invalid Signature Error
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
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists - Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
- Using
returnto 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro