Overview

Protect routes with JWT authentication. Supports role-based access control.

Features

  • JWT verification
  • Role-based access control
  • Token extraction from header/cookie
  • Customizable error responses

Installation

npx hanma add jwt-auth

Dependencies

jsonwebtoken

Code


import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET || 'changeme-in-production';

/**
 * Extended Request interface with user payload
 */
export interface AuthenticatedRequest extends Request {
  user?: JwtPayload;
}

export interface JwtPayload {
  id: string;
  email: string;
  role?: string;
  [key: string]: unknown;
}

/**
 * Extract JWT token from Authorization header
 * Supports: "Bearer <token>" format
 */
const extractToken = (req: Request): string | null => {
  const authHeader = req.headers.authorization;
  
  if (!authHeader) return null;
  
  // Support "Bearer <token>" format
  if (authHeader.startsWith('Bearer ')) {
    return authHeader.slice(7);
  }
  
  return authHeader;
};

/**
 * JWT Authentication Middleware
 * Verifies token and attaches decoded payload to req.user
 */
export const authenticate = (
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
) => {
  const token = extractToken(req);

  if (!token) {
    return res.status(401).json({
      success: false,
      message: 'Access denied. No token provided.',
    });
  }

  try {
    const decoded = jwt.verify(token, JWT_SECRET) as JwtPayload;
    req.user = decoded;
    next();
  } catch (error) {
    if (error instanceof jwt.TokenExpiredError) {
      return res.status(401).json({
        success: false,
        message: 'Token has expired.',
      });
    }
    if (error instanceof jwt.JsonWebTokenError) {
      return res.status(401).json({
        success: false,
        message: 'Invalid token.',
      });
    }
    return res.status(500).json({
      success: false,
      message: 'Failed to authenticate token.',
    });
  }
};

/**
 * Optional authentication - doesn't fail if no token provided
 * Useful for routes that behave differently for logged-in users
 */
export const optionalAuth = (
  req: AuthenticatedRequest,
  res: Response,
  next: NextFunction
) => {
  const token = extractToken(req);

  if (!token) {
    return next();
  }

  try {
    const decoded = jwt.verify(token, JWT_SECRET) as JwtPayload;
    req.user = decoded;
  } catch {
    // Token invalid, but that's okay for optional auth
  }
  
  next();
};

/**
 * Role-based authorization middleware
 * Use after authenticate middleware
 * @param allowedRoles - Array of roles that can access the route
 */
export const authorize = (...allowedRoles: string[]) => {
  return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({
        success: false,
        message: 'Authentication required.',
      });
    }

    if (!req.user.role || !allowedRoles.includes(req.user.role)) {
      return res.status(403).json({
        success: false,
        message: 'Insufficient permissions.',
      });
    }

    next();
  };
};

Usage

import { authenticate, authorize } from './middleware/auth/jwt-auth';

app.get('/admin', authenticate, authorize(['admin']), handler);

Dependencies

Frequently Asked Questions

How do I install JWT Authentication?
Run the following command: npx hanma add jwt-auth
What dependencies are required?
This snippet requires: jsonwebtoken. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to middleware/auth/jwt-auth.ts.
Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.