Skip to contentPedro Farbo
Lesson 12 / 2560 min

JWT Authentication

JWT Authentication

Implement secure authentication with JSON Web Tokens.

Installation

bash
npm install jsonwebtoken bcryptjsnpm install -D @types/jsonwebtoken @types/bcryptjs

User Model

prisma
model User {  id        String   @id @default(uuid())  email     String   @unique  password  String  name      String  role      Role     @default(USER)  createdAt DateTime @default(now())} enum Role {  USER  ADMIN}

Auth Service

typescript
// src/services/auth.service.tsimport bcrypt from 'bcryptjs';import jwt from 'jsonwebtoken';import { prisma } from '../config/database';import { AppError } from '../errors/AppError'; interface RegisterDTO {  name: string;  email: string;  password: string;} export class AuthService {  async register(data: RegisterDTO) {    const exists = await prisma.user.findUnique({      where: { email: data.email }    });     if (exists) {      throw new AppError('Email already registered', 400);    }     const hashedPassword = await bcrypt.hash(data.password, 12);     const user = await prisma.user.create({      data: {        ...data,        password: hashedPassword      },      select: { id: true, email: true, name: true, role: true }    });     const token = this.generateToken(user.id);     return { user, token };  }   async login(email: string, password: string) {    const user = await prisma.user.findUnique({      where: { email }    });     if (!user) {      throw new AppError('Invalid credentials', 401);    }     const validPassword = await bcrypt.compare(password, user.password);     if (!validPassword) {      throw new AppError('Invalid credentials', 401);    }     const token = this.generateToken(user.id);     return {      user: {        id: user.id,        email: user.email,        name: user.name,        role: user.role      },      token    };  }   private generateToken(userId: string): string {    return jwt.sign(      { userId },      process.env.JWT_SECRET!,      { expiresIn: '7d' }    );  }}

Auth Middleware

typescript
// src/middlewares/auth.middleware.tsimport { Request, Response, NextFunction } from 'express';import jwt from 'jsonwebtoken';import { prisma } from '../config/database'; interface JWTPayload {  userId: string;} declare global {  namespace Express {    interface Request {      user?: { id: string; role: string };    }  }} export const authenticate = async (  req: Request,  res: Response,  next: NextFunction) => {  const authHeader = req.headers.authorization;   if (!authHeader?.startsWith('Bearer ')) {    return res.status(401).json({ error: 'Token not provided' });  }   const token = authHeader.split(' ')[1];   try {    const { userId } = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;     const user = await prisma.user.findUnique({      where: { id: userId },      select: { id: true, role: true }    });     if (!user) {      return res.status(401).json({ error: 'User not found' });    }     req.user = user;    next();  } catch {    return res.status(401).json({ error: 'Invalid token' });  }};

Routes

typescript
// src/routes/auth.routes.tsrouter.post('/register', validate(registerSchema), controller.register);router.post('/login', validate(loginSchema), controller.login);router.get('/me', authenticate, controller.me);

Summary

  • ✅ Registration with hashed password
  • ✅ Login with JWT token
  • ✅ Auth middleware for protection
  • ✅ User data in request

Next lesson: Authorization and RBAC! 🚀

Enjoyed the content? Your contribution helps keep everything online and free!

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e