Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Авторизация 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: отозвать уже выданный токен до истечения срока нельзя.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.