Введение в 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— создание и верификация JWTbcrypt— хеширование паролей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:
- Пользователь авторизуется через сторонний сервис (Google, GitHub).
- Сервис возвращает временный код