Авторизация и аутентификация в TypeScript: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Авторизация и аутентификация в TypeScript: от теории к практике

При разработке современных веб-приложений на TypeScript безопасность данных пользователей является приоритетной задачей. Два ключевых процесса — аутентификация (подтверждение личности) и авторизация (проверка прав доступа) — образуют фундамент любой защищённой системы. В этом руководстве мы разберём полный цикл реализации аутентификации и авторизации на TypeScript с использованием популярных библиотек и лучших практик.

1. Основные понятия и отличия

  • Аутентификация — процесс проверки, является ли пользователь тем, за кого себя выдаёт (логин + пароль, биометрия, OAuth).
  • Авторизация — процесс определения, какие ресурсы и действия доступны аутентифицированному пользователю (роли, права доступа).

Например: пользователь ввёл верный пароль (аутентификация), но ему запрещено удалять чужие посты (авторизация).

2. Необходимые инструменты и библиотеки

Для реализации мы будем использовать:

  • bcrypt — для хеширования паролей
  • jsonwebtoken (JWT) — для создания и проверки токенов
  • express + @types/express — серверная часть
  • zod — валидация входных данных

3. Регистрация пользователя (аутентификация)

При регистрации пароль никогда не хранится в открытом виде. Используем bcrypt для создания хеша:

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);
    
    // Сохраняем email + hashedPassword в базу данных
    // await db.user.create({ data: { email, password: hashedPassword } });
    return { success: true };
}

4. Вход в систему и выдача JWT

После успешной проверки пароля генерируем access-токен:

import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET!; // обязательно в .env

async function loginUser(email: string, password: string) {
    // 1. Найти пользователя в БД по email
    // const user = await db.user.findUnique({ where: { email } });
    // if (!user) throw new Error('User not found');
    
    // 2. Сравнить пароль
    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) throw new Error('Invalid password');
    
    // 3. Создать 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 для проверки JWT (аутентификация)

Создаём middleware, который будет проверять токен на каждый защищённый запрос:

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. Авторизация на основе ролей (RBAC)

Создаём middleware для проверки прав доступа:

type Role = 'admin' | 'user' | 'moderator';

export function authorize(...allowedRoles: Role[]) {
    return (req: AuthRequest, res: Response, next: NextFunction) => {
        if (!req.user) {
            return res.status(401).json({ error: 'Not authenticated' });
        }
        if (!allowedRoles.includes(req.user.role as Role)) {
            return res.status(403).json({ error: 'Insufficient permissions' });
        }
        next();
    };
}

// Использование в роуте:
// router.delete('/users/:id', authMiddleware, authorize('admin'), deleteUserHandler);

7. Безопасное хранение секретов

Никогда не храните JWT_SECRET и другие ключи в коде. Используйте переменные окружения:

// .env
JWT_SECRET=your-very-strong-secret-key-here
JWT_EXPIRES_IN=1h                    

Рекомендаций