Введение в OAuth 2.0 и JWT: что это и зачем нужно?
Современные веб-приложения редко обходятся без системы авторизации. Будь то вход через Google, доступ к API или защита маршрутов в вашем Node.js приложении — вам понадобятся OAuth 2.0 и JWT (JSON Web Tokens). Эти технологии стали стандартом де-факто для безопасного обмена данными между клиентом и сервером.
В этой статье мы разберём, как работают OAuth 2.0 и JWT, и напишем простую систему авторизации на JavaScript (Node.js). Материал рассчитан на начинающих разработчиков, поэтому мы будем двигаться от теории к практике с подробными примерами кода.
Часть 1: Что такое OAuth 2.0 и как он работает?
OAuth 2.0 — это протокол авторизации, который позволяет приложениям получать ограниченный доступ к ресурсам пользователя без передачи пароля. Представьте, что вы хотите войти на сайт через свой аккаунт Google. OAuth 2.0 как раз отвечает за этот процесс.
Основные роли в OAuth 2.0
- Resource Owner — пользователь, владеющий данными.
- Client — ваше приложение (например, Node.js сервер).
- Authorization Server — сервер, выдающий токены (например, Google, GitHub).
- Resource Server — сервер, хранящий данные пользователя.
Типичный поток OAuth 2.0 (Authorization Code Grant)
- Пользователь нажимает «Войти через Google».
- Приложение перенаправляет его на сервер авторизации Google.
- Пользователь даёт согласие.
- Google возвращает
authorization codeна сервер приложения. - Сервер обменивает этот код на
access token. - Сервер использует access token для доступа к данным пользователя.
На практике, для простых приложений часто используют JWT как формат токена доступа.
Часть 2: JWT — структура и принцип работы
JWT (JSON Web Token) — это компактный и самодостаточный способ передачи информации между сторонами в виде JSON-объекта. Токен подписывается, поэтому его можно верифицировать без обращения к базе данных.
Структура JWT
JWT состоит из трёх частей, разделённых точками (.):
header.payload.signature
- Header — содержит тип токена и алгоритм подписи (например, HS256).
- Payload — данные (claims), например, ID пользователя, роль, время жизни.
- Signature — подпись, созданная с помощью секретного ключа.
Пример декодированного JWT
{
"alg": "HS256",
"typ": "JWT"
}
.
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}
.
[signature]
Важно: payload не зашифрован, только подписан. Никогда не храните конфиденциальные данные (пароли) в JWT!
Часть 3: Реализация JWT авторизации на Node.js
Теперь перейдём к практике. Мы создадим простой REST API на Express.js с JWT-авторизацией. Для этого нам понадобятся библиотеки jsonwebtoken и bcryptjs.
Шаг 1: Установка зависимостей
npm init -y
npm install express jsonwebtoken bcryptjs dotenv
npm install --save-dev nodemon
Шаг 2: Базовая настройка сервера
// server.js
require('dotenv').config();
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const app = express();
app.use(express.json());
const users = []; // В реальном проекте — база данных
const PORT = process.env.PORT || 3000;
const JWT_SECRET = process.env.JWT_SECRET || 'mysecretkey123';
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Шаг 3: Регистрация пользователя
app.post('/register', async (req, res) => {
try {
const { username, password } = req.body;
// Проверяем, существует ли пользователь
const existingUser = users.find(u => u.username === username);
if (existingUser) {
return res.status(400).json({ message: 'Пользователь уже существует' });
}
// Хэшируем пароль
const hashedPassword = await bcrypt.hash(password, 10);
// Сохраняем пользователя
const newUser = { id: users.length + 1, username, password: hashedPassword };
users.push(newUser);
res.status(201).json({ message: 'Пользователь создан' });
} catch (error) {
res.status(500).json({ message: 'Ошибка сервера' });
}
});
Шаг 4: Вход и выдача JWT
app.post('/login', async (req, res) => {
try {
const { username, password } = req.body;
// Ищем пользователя
const user = users.find(u => u.username === username);
if (!user) {
return res.status(400).json({ message: 'Неверные данные' });
}
// Проверяем пароль
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(400).json({ message: 'Неверные данные' });
}
// Создаём JWT токен
const token = jwt.sign(
{ userId: user.id, username: user.username },
JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
} catch (error) {
res.status(500).json({ message: 'Ошибка сервера' });
}
});
Шаг 5: Middleware для проверки токена
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) => {