Overview

Protect routes with JWT authentication using @fastify/jwt. Supports role-based access control and token refresh.

Features

  • JWT verification via @fastify/jwt
  • Role-based access control
  • Token generation helpers
  • Refresh token support
  • Customizable error responses

Installation

npx hanma add fastify-jwt --framework fastify

Dependencies

fastify, @fastify/jwt

Code

import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
import jwt from "@fastify/jwt";

interface JwtPayload {
  id: string;
  email: string;
  roles: string[];
}

declare module "@fastify/jwt" {
  interface FastifyJWT {
    payload: JwtPayload;
    user: JwtPayload;
  }
}

export async function registerJwtAuth(app: FastifyInstance): Promise<void> {
  await app.register(jwt, {
    secret: process.env.JWT_SECRET ?? "your-secret-key-change-in-production",
    sign: {
      expiresIn: process.env.JWT_EXPIRES_IN ?? "7d",
    },
    verify: {
      maxAge: process.env.JWT_EXPIRES_IN ?? "7d",
    },
  });

  // Decorate with authenticate method
  app.decorate("authenticate", async (request: FastifyRequest, reply: FastifyReply) => {
    try {
      await request.jwtVerify();
    } catch (err) {
      reply.status(401).send({ 
        error: "Unauthorized",
        message: "Invalid or missing authentication token",
      });
    }
  });
}

// Middleware to verify JWT
export async function authenticate(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  try {
    await request.jwtVerify();
  } catch (err) {
    reply.status(401).send({
      error: "Unauthorized",
      message: "Invalid or missing authentication token",
    });
  }
}

// Middleware to check roles
export function authorize(allowedRoles: string[]) {
  return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
    try {
      await request.jwtVerify();
      
      const user = request.user;
      const hasRole = user.roles.some((role) => allowedRoles.includes(role));
      
      if (!hasRole) {
        reply.status(403).send({
          error: "Forbidden",
          message: "Insufficient permissions",
          required: allowedRoles,
        });
      }
    } catch (err) {
      reply.status(401).send({
        error: "Unauthorized",
        message: "Invalid or missing authentication token",
      });
    }
  };
}

// Generate tokens
export function generateTokens(app: FastifyInstance, payload: JwtPayload) {
  const accessToken = app.jwt.sign(payload);
  const refreshToken = app.jwt.sign(payload, { expiresIn: "30d" });
  
  return { accessToken, refreshToken };
}

// Optional: Refresh token handler
export async function refreshTokenHandler(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  try {
    const decoded = await request.jwtVerify();
    const newToken = request.server.jwt.sign({
      id: decoded.id,
      email: decoded.email,
      roles: decoded.roles,
    });
    
    reply.send({ accessToken: newToken });
  } catch (err) {
    reply.status(401).send({
      error: "Unauthorized",
      message: "Invalid refresh token",
    });
  }
}

// Usage:
// await registerJwtAuth(app);
// app.get("/protected", { preHandler: [authenticate] }, handler);
// app.get("/admin", { preHandler: [authorize(["admin"])] }, handler);

Usage

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

await registerJwtAuth(app);
app.get('/admin', { preHandler: [authorize(['admin'])] }, handler);

Dependencies

Frequently Asked Questions

How do I install JWT Authentication?
Run the following command: npx hanma add fastify-jwt --framework fastify
What dependencies are required?
This snippet requires: fastify, @fastify/jwt. 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 src/middleware/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.