Authorization and Authentication in TypeScript: From Theory to Practice
When developing modern web applications in TypeScript, user data security is a top priority. Two key processes — authentication (identity verification) and authorization (access rights verification) — form the foundation of any secure system. In this guide, we will cover the complete cycle of implementing authentication and authorization in TypeScript using popular libraries and best practices.
1. Basic Concepts and Differences
- Authentication — the process of verifying whether a user is who they claim to be (login + password, biometrics, OAuth).
- Authorization — the process of determining which resources and actions are available to an authenticated user (roles, access rights).
For example: a user entered the correct password (authentication), but is prohibited from deleting other users' posts (authorization).
2. Required Tools and Libraries
For implementation, we will use:
bcrypt— for password hashingjsonwebtoken(JWT) — for creating and verifying tokensexpress+@types/express— server-sidezod— input data validation
3. User Registration (Authentication)
During registration, the password is never stored in plain text. We use bcrypt to create a hash:
import bcrypt from 'bcrypt';import { z } from 'zod';
const registerSchema = z.object({ email: z.string().email(), password: z.string().min(8)});
async function registerUser(email: string, password: string) { const validated = registerSchema.parse({ email, password }); const saltRounds = 10; const hashedPassword = await bcrypt.hash(validated.password, saltRounds); // Save email + hashedPassword to the database // await db.user.create({ data: { email, password: hashedPassword } }); return { success: true };}4. Login and JWT Issuance
After successful password verification, generate an access token:
import jwt from 'jsonwebtoken';
const JWT_SECRET = process.env.JWT_SECRET!; // обязательно в .env
async function loginUser(email: string, password: string) { // 1. Find the user in the database by email // const user = await db.user.findUnique({ where: { email } }); // if (!user) throw new Error('User not found'); // 2. Compare the password const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) throw new Error('Invalid password'); // 3. Create JWT const token = jwt.sign( { userId: user.id, role: user.role }, JWT_SECRET, { expiresIn: '1h' } ); return { token, user: { id: user.id, email: user.email } };}5. Middleware for JWT Verification (Authentication)
Create middleware that will verify the token on every protected request:
import { Request, Response, NextFunction } from 'express';
interface AuthRequest extends Request { user?: { userId: string; role: string };}
export function authMiddleware(req: AuthRequest, res: Response, next: NextFunction) { const authHeader = req.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { return res.status(401).json({ error: 'No token provided' }); } const token = authHeader.split(' ')[1]; try { const decoded = jwt.verify(token, JWT_SECRET) as { userId: string; role: string }; req.user = decoded; next(); } catch (err) { return res.status(403).json({ error: 'Invalid or expired token' }); }}6. Role-Based Authorization (RBAC)
Create midd