乐闻世界logo
搜索文章和话题

How to implement JWT error handling

2月21日 17:52

JWT error handling is crucial for building robust authentication systems. Here are complete error handling strategies and implementation methods:

Common JWT Error Types

1. Token Format Error

javascript
const jwt = require('jsonwebtoken'); function verifyToken(token) { try { return jwt.verify(token, SECRET_KEY); } catch (error) { if (error.name === 'JsonWebTokenError') { throw new Error('Invalid token format'); } throw error; } }

2. Token Expiration Error

javascript
function verifyToken(token) { try { return jwt.verify(token, SECRET_KEY); } catch (error) { if (error.name === 'TokenExpiredError') { throw new Error('Token has expired'); } throw error; } }

3. Signature Verification Error

javascript
function verifyToken(token) { try { return jwt.verify(token, SECRET_KEY); } catch (error) { if (error.message === 'invalid signature') { throw new Error('Invalid token signature'); } throw error; } }

4. Algorithm Error

javascript
function verifyToken(token) { try { return jwt.verify(token, SECRET_KEY, { algorithms: ['RS256'] }); } catch (error) { if (error.message === 'invalid algorithm') { throw new Error('Invalid token algorithm'); } throw error; } }

Unified Error Handling Middleware

Express Error Handling Middleware

javascript
const jwt = require('jsonwebtoken'); // Authentication middleware function authMiddleware(req, res, next) { const authHeader = req.headers['authorization']; if (!authHeader) { return res.status(401).json({ error: 'Unauthorized', message: 'No token provided' }); } const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : authHeader; try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (error) { handleJwtError(error, res); } } // JWT error handling function function handleJwtError(error, res) { const errorMap = { 'TokenExpiredError': { status: 401, code: 'TOKEN_EXPIRED', message: 'Token has expired' }, 'JsonWebTokenError': { status: 401, code: 'INVALID_TOKEN', message: 'Invalid token' }, 'NotBeforeError': { status: 401, code: 'TOKEN_NOT_ACTIVE', message: 'Token is not active yet' } }; const errorInfo = errorMap[error.name] || { status: 500, code: 'AUTH_ERROR', message: 'Authentication error' }; res.status(errorInfo.status).json({ error: errorInfo.code, message: errorInfo.message, timestamp: new Date().toISOString() }); } // Global error handling middleware app.use((err, req, res, next) => { console.error('Error:', err); if (err.name === 'JsonWebTokenError' || err.name === 'TokenExpiredError' || err.name === 'NotBeforeError') { return handleJwtError(err, res); } res.status(500).json({ error: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' }); });

Error Logging

Structured Logging

javascript
const winston = require('winston'); const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'auth-errors.log' }) ] }); function logAuthError(error, req) { logger.error({ error: error.name, message: error.message, stack: error.stack, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'], path: req.path, method: req.method }); } // Usage example function authMiddleware(req, res, next) { try { const decoded = jwt.verify(token, SECRET_KEY); req.user = decoded; next(); } catch (error) { logAuthError(error, req); handleJwtError(error, res); } }

Token Refresh Error Handling

Error Handling for Token Refresh

javascript
async function refreshToken(refreshToken) { try { // Verify refresh token const decoded = jwt.verify(refreshToken, REFRESH_SECRET); // Check if blacklisted const isBlacklisted = await checkBlacklist(refreshToken); if (isBlacklisted) { throw new Error('Token has been revoked'); } // Generate new access token const accessToken = jwt.sign( { userId: decoded.userId }, ACCESS_SECRET, { expiresIn: '15m' } ); return { accessToken, expiresIn: 900 }; } catch (error) { throw handleRefreshError(error); } } function handleRefreshError(error) { const errorMap = { 'TokenExpiredError': { code: 'REFRESH_TOKEN_EXPIRED', message: 'Refresh token has expired, please login again', action: 'LOGIN_REQUIRED' }, 'JsonWebTokenError': { code: 'INVALID_REFRESH_TOKEN', message: 'Invalid refresh token', action: 'LOGIN_REQUIRED' }, 'TokenRevokedError': { code: 'TOKEN_REVOKED', message: 'Token has been revoked', action: 'LOGIN_REQUIRED' } }; const errorInfo = errorMap[error.name] || { code: 'REFRESH_ERROR', message: 'Failed to refresh token', action: 'RETRY' }; return { ...errorInfo, originalError: error.message }; }

Frontend Error Handling

Axios Interceptor

javascript
import axios from 'axios'; const api = axios.create({ baseURL: 'https://api.example.com' }); // Response interceptor api.interceptors.response.use( response => response, async error => { const originalRequest = error.config; // Handle 401 errors if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; const errorCode = error.response.data?.error; // Token expired, try refresh if (errorCode === 'TOKEN_EXPIRED') { try { const refreshToken = localStorage.getItem('refreshToken'); const response = await axios.post('/auth/refresh', { refreshToken }); const { accessToken } = response.data; localStorage.setItem('accessToken', accessToken); // Retry original request originalRequest.headers.Authorization = `Bearer ${accessToken}`; return api(originalRequest); } catch (refreshError) { // Refresh failed, redirect to login localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); window.location.href = '/login'; return Promise.reject(refreshError); } } // Other 401 errors, redirect to login localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); window.location.href = '/login'; } // Handle other errors return Promise.reject(error); } );

Error Monitoring and Alerting

Sentry Integration

javascript
const Sentry = require('@sentry/node'); Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV }); function authMiddleware(req, res, next) { try { const decoded = jwt.verify(token, SECRET_KEY); req.user = decoded; next(); } catch (error) { // Send error to Sentry Sentry.captureException(error, { tags: { errorType: error.name, endpoint: req.path }, extra: { ip: req.ip, userAgent: req.headers['user-agent'] } }); handleJwtError(error, res); } }

Custom Error Classes

Create Custom Errors

javascript
class AuthError extends Error { constructor(code, message, statusCode = 401) { super(message); this.name = 'AuthError'; this.code = code; this.statusCode = statusCode; } } class TokenExpiredError extends AuthError { constructor() { super('TOKEN_EXPIRED', 'Token has expired', 401); } } class InvalidTokenError extends AuthError { constructor() { super('INVALID_TOKEN', 'Invalid token', 401); } } class TokenRevokedError extends AuthError { constructor() { super('TOKEN_REVOKED', 'Token has been revoked', 401); } } // Use custom errors function verifyToken(token) { try { return jwt.verify(token, SECRET_KEY); } catch (error) { if (error.name === 'TokenExpiredError') { throw new TokenExpiredError(); } if (error.name === 'JsonWebTokenError') { throw new InvalidTokenError(); } throw new AuthError('AUTH_ERROR', 'Authentication failed'); } }

Error Recovery Strategies

Retry Mechanism

javascript
async function verifyTokenWithRetry(token, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return jwt.verify(token, SECRET_KEY); } catch (error) { // Only retry transient errors if (error.name === 'TokenExpiredError') { throw error; // Don't retry expired errors } if (i === maxRetries - 1) { throw error; } // Wait before retry await new Promise(resolve => setTimeout(resolve, 100 * (i + 1))); } } }

Best Practices

Error Handling Checklist

  • Unified error format
  • Detailed error logging
  • Distinguish different error types
  • Provide friendly error messages
  • Implement error monitoring
  • Set up error alerts
  • Handle frontend errors
  • Implement retry mechanism
  • Use custom error classes
  • Regularly analyze error logs

Error Handling Principles

  1. Don't expose sensitive info: Error messages shouldn't contain keys or internal implementation details
  2. Log complete context: Logs should include request info, user info, etc.
  3. Provide clear feedback: Users should know what happened and how to resolve it
  4. Monitor critical errors: Set up alerts for important errors
  5. Regularly analyze logs: Discover potential issues and attack patterns

With comprehensive error handling mechanisms, you can improve the robustness and user experience of JWT authentication systems.

标签:JWT