JWT Authentication
Implement secure authentication with JSON Web Tokens.
Installation
bash
npm install jsonwebtoken bcryptjsnpm install -D @types/jsonwebtoken @types/bcryptjsUser 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! 🚀