OAuth 2.0 и JWT авторизация на JavaScript: полное руководство

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

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

Начать курс

Введение в OAuth 2.0 и JWT авторизацию на JavaScript

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

Вы узнаете:

  • Что такое OAuth 2.0 и зачем он нужен
  • Как работают JWT токены
  • Как настроить сервер авторизации на Express
  • Как защитить маршруты с помощью middleware

Основы OAuth 2.0: потоки и роли

OAuth 2.0 определяет четыре роли: Resource Owner (пользователь), Client (приложение), Authorization Server (сервер, выдающий токены) и Resource Server (сервер, предоставляющий данные). Самый распространенный поток — Authorization Code Grant, который используется в веб-приложениях.

В этой статье мы сосредоточимся на реализации Resource Server и Authorization Server с использованием JWT, а не внешних провайдеров (Google, GitHub). Это даст вам полный контроль над процессом.

Установка зависимостей

Для начала создайте новый проект Node.js и установите необходимые пакеты:

npm init -y
npm install express jsonwebtoken bcrypt dotenv cors

Описание пакетов:

  • express — фреймворк для создания сервера
  • jsonwebtoken — создание и верификация JWT
  • bcrypt — хеширование паролей
  • dotenv — работа с переменными окружения
  • cors — настройка CORS для API

Реализация JWT авторизации на Node.js

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

Настройка сервера и модели пользователя

Создайте файл server.js и определите базовую структуру:

require('dotenv').config();
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const cors = require('cors');

const app = express();
app.use(express.json());
app.use(cors());

const PORT = process.env.PORT || 3000;
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

// Временное хранилище пользователей (в продакшене используйте БД)
const users = [];

Регистрация и хеширование паролей

При регистрации пароль должен быть захэширован с помощью bcrypt:

app.post('/api/register', async (req, res) => {
    try {
        const { email, password } = req.body;
        
        // Проверка существующего пользователя
        const existingUser = users.find(u => u.email === email);
        if (existingUser) {
            return res.status(400).json({ message: 'Пользователь уже существует' });
        }

        // Хеширование пароля
        const hashedPassword = await bcrypt.hash(password, 10);
        const newUser = { id: users.length + 1, email, password: hashedPassword };
        users.push(newUser);

        res.status(201).json({ message: 'Пользователь создан' });
    } catch (error) {
        res.status(500).json({ message: 'Ошибка сервера' });
    }
});

Логин и генерация JWT токена

После проверки учетных данных генерируем токен с помощью jsonwebtoken:

app.post('/api/login', async (req, res) => {
    try {
        const { email, password } = req.body;
        
        // Поиск пользователя
        const user = users.find(u => u.email === email);
        if (!user) {
            return res.status(401).json({ message: 'Неверные учетные данные' });
        }

        // Сравнение паролей
        const passwordMatch = await bcrypt.compare(password, user.password);
        if (!passwordMatch) {
            return res.status(401).json({ message: 'Неверные учетные данные' });
        }

        // Создание JWT
        const token = jwt.sign(
            { userId: user.id, email: user.email },
            JWT_SECRET,
            { expiresIn: '1h' }
        );

        res.json({ token, expiresIn: 3600 });
    } catch (error) {
        res.status(500).json({ message: 'Ошибка сервера' });
    }
});

Middleware для проверки JWT

Создайте middleware, который будет проверять токен в заголовке Authorization:

function authenticateToken(req, res, next) {
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

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

    jwt.verify(token, JWT_SECRET, (err, user) => {
        if (err) {
            return res.status(403).json({ message: 'Недействительный токен' });
        }
        req.user = user;
        next();
    });
}

Защищенные маршруты

Теперь можно защитить любой маршрут, добавив middleware:

app.get('/api/profile', authenticateToken, (req, res) => {
    const user = users.find(u => u.id === req.user.userId);
    if (!user) {
        return res.status(404).json({ message: 'Пользователь не найден' });
    }
    res.json({ id: user.id, email: user.email });
});

// Запуск сервера
app.listen(PORT, () => {
    console.log(`Сервер запущен на порту ${PORT}`);
});

Интеграция OAuth 2.0 с JWT

Хотя мы не используем внешних провайдеров, вы можете легко расширить систему для поддержки OAuth 2.0. Например, добавить эндпоинт /api/oauth/token, который будет принимать код авторизации и выдавать JWT. Типичный flow:

  1. Пользователь авторизуется через сторонний сервис (Google, GitHub).
  2. Сервис возвращает временный код

Блоги

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