Авторизация API через JWT
Сессии с cookie удобны для сайта в браузере, но мобильному приложению или другому серверу проще работать с токеном. JWT (JSON Web Token) — строка, в которой сервер записывает данные, например номер пользователя, и подписывает их секретным ключом. Клиент присылает токен в каждом запросе, а сервер по подписи проверяет, что токен выдал именно он.
Установка
npm install jsonwebtokenСекретный ключ возьмём из переменной окружения JWT_SECRET — о них отдельный урок. Пока достаточно знать, что ключ должен быть длинной случайной строкой.
Выдача токена
Добавьте в API адрес входа. Проверка пароля — та же функция User.verify() из урока о входе:
// src/controllers/api/authController.js
import jwt from 'jsonwebtoken';
import * as User from '../../models/user.js';
export async function login(req, res) {
const user = await User.verify(req.body?.email, req.body?.password);
if (!user) {
return res.status(401).json({ error: 'Неверная почта или пароль' });
}
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
}Подключите маршрут: router.post('/login', authController.login) в файле src/routes/api/auth.js, а сам роутер — по адресу /api/auth. Проверьте:
curl -X POST http://localhost:3000/api/auth/login -H "Content-Type: application/json" -d '{"email":"anna@example.com","password":"secret123"}'Сервер вернёт {"token":"eyJhbGciOi..."} — длинную строку из трёх частей через точку. Опция expiresIn ограничивает срок жизни токена часом.
Проверка токена
Клиент отправляет токен в заголовке Authorization со словом Bearer перед ним. Создайте middleware src/middleware/requireToken.js:
// src/middleware/requireToken.js
import jwt from 'jsonwebtoken';
export function requireToken(req, res, next) {
const header = req.get('Authorization') ?? '';
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
if (!token) {
return res.status(401).json({ error: 'Нужен токен' });
}
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.userId = payload.userId;
next();
} catch {
res.status(401).json({ error: 'Токен недействителен или истёк' });
}
}jwt.verify() проверяет подпись и срок действия и возвращает данные из токена. Если что-то не так, он выбрасывает исключение.
Защита адресов
Поставьте middleware перед обработчиками, которые меняют данные:
// src/routes/api/posts.js
import { requireToken } from '../../middleware/requireToken.js';
router.post('/', requireToken, api.store);
router.patch('/:id', requireToken, api.update);
router.delete('/:id', requireToken, api.destroy);Теперь удаление без токена вернёт 401, а с токеном пройдёт:
curl -X DELETE http://localhost:3000/api/posts/2 -H "Authorization: Bearer eyJhbGciOi..."Частая ошибка
Содержимое JWT не зашифровано, а только подписано: любой может раскодировать токен и прочитать данные внутри. Не кладите в него пароли, почту или другие личные данные — достаточно номера пользователя. И задавайте короткий expiresIn: отозвать уже выданный токен до истечения срока нельзя.