OAuth 2.0 и JWT авторизация на TypeScript: полное руководство для начинающих

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

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

Начать курс

Введение в OAuth 2.0 и JWT на TypeScript

В современной веб-разработке безопасная авторизация пользователей — это основа любого приложения. Два самых популярных стандарта — OAuth 2.0 и JWT (JSON Web Token) — позволяют реализовать надежную систему аутентификации и разграничения доступа. В этой статье мы разберем, как объединить эти технологии на TypeScript, чтобы создать безопасный API с нуля.

Эта статья предназначена для начинающих разработчиков, которые уже знакомы с основами TypeScript и хотят понять, как работает авторизация на практике. Мы не будем углубляться в сложные теоретические выкладки, а сосредоточимся на конкретных примерах кода.

Что такое OAuth 2.0?

OAuth 2.0 — это протокол авторизации, который позволяет сторонним приложениям получать ограниченный доступ к ресурсам пользователя без передачи его пароля. Представьте, что вы хотите войти на сайт через Google: вы не даете сайту свой пароль от Google, а просто разрешаете ему получить ваш email и имя. Это и есть OAuth 2.0.

Что такое JWT?

JWT (JSON Web Token) — это компактный и самодостаточный формат токена, который содержит всю необходимую информацию о пользователе в зашифрованном виде. Токен состоит из трех частей: заголовок, полезная нагрузка (payload) и подпись. Благодаря подписи сервер может быть уверен, что токен не был подделан.

1. Настройка проекта и установка зависимостей

Для начала создадим новый проект на TypeScript. Откройте терминал и выполните следующие команды:

mkdir oauth-jwt-ts
cd oauth-jwt-ts
npm init -y
npm install express jsonwebtoken bcrypt dotenv cors
npm install -D typescript ts-node @types/express @types/jsonwebtoken @types/bcrypt @types/cors @types/node nodemon

Создайте файл tsconfig.json с базовой конфигурацией:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Структура папок будет выглядеть так:

  • src/ — исходный код
  • src/controllers/ — контроллеры
  • src/middleware/ — middleware для проверки токенов
  • src/models/ — модели данных
  • src/routes/ — маршруты
  • .env — переменные окружения

2. Реализация базовой аутентификации с JWT

Начнем с создания простого механизма регистрации и входа с использованием JWT. Создадим файл src/config.ts для хранения констант:

import dotenv from 'dotenv';
dotenv.config();

export const config = {
  jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
  jwtExpiresIn: process.env.JWT_EXPIRES_IN || '15m',
  refreshTokenExpiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || '7d',
  port: process.env.PORT || 3000
};

Теперь создадим middleware для проверки JWT токена. Файл src/middleware/auth.middleware.ts:

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { config } from '../config';

export interface AuthRequest extends Request {
  user?: { userId: string; email: string };
}

export const authenticateToken = (req: AuthRequest, res: Response, next: NextFunction) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

  if (!token) {
    return res.status(401).json({ message: 'Токен не предоставлен' });
  }

  try {
    const decoded = jwt.verify(token, config.jwtSecret) as { userId: string; email: string };
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(403).json({ message: 'Недействительный или просроченный токен' });
  }
};

Создадим простой контроллер для регистрации и входа. Файл src/controllers/auth.controller.ts:

import { Request, Response } from 'express';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { config } from '../config';

// В реальном проекте здесь будет работа с базой данных
const users: Array<{ id: string; email: string; password: string }> = [];

export const register = async (req: Request, res: Response) => {
  try {
    const { email, password } = req.body;

    if (!email || !password) {
      return res.status(400).json({ message: 'Email и пароль обязательны' });
    }

    // Проверка, существует ли пользователь
    const existingUser = users.find(u => u.email === email);
    if (existingUser) {
      return res.status(409).json({ message: 'Пользователь с таким email уже существует' });
    }

    // Хешируем пароль
    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(password, salt);

    // Создаем пользователя (в реальности — сохраняем в БД)
    const newUser = {
      id: String(users.length + 1),
      email,
      password: hashedPassword
    };
    users.push(newUser);

    // Генерируем JWT токен
    const token = jwt.sign(
      { userId: newUser.id, email: newUser.email },
      config.jwtSecret,
      { expiresIn: config.jwtExpiresIn }
    );

    res.status(201).json({
      message: 'Пользователь успешно зарегистрирован',
      token,
      user: { id: newUser.id, email: newUser.email }
    });
  } catch (error) {
    res.status(500).json({ message: 'Ошибка сервера' });
  }
};

export const login = async (req: Request, res: Response) => {
  try {
    const { email, password } = req.body;

    if (!email || !password) {
      return res.status(400).json({ message: 'Email и пароль обязательны' });
    }

    // Ищем пользователя
    const user = users.find(u => u.email === email);
    if (!user) {
      return res.status(401).json({ message: 'Неверный email или пароль' });
    }

    // Проверяем пароль
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      return res.status(401).                

Блоги

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