JWT (JSON Web Token) представляет собой компактный формат передачи утверждений (claims) между сторонами в виде JSON-объекта, который подписывается или шифруется. В контексте аутентификации JWT используется как переносимый токен доступа, содержащий информацию о пользователе и правах доступа без необходимости хранения состояния на сервере.
Структура JWT состоит из трёх частей:
Формат представления:
header.payload.signature
Каждая часть кодируется в Base64URL, что обеспечивает компактность и удобство передачи через HTTP-заголовки.
Библиотека jose представляет собой современную
реализацию стандартов JOSE (JSON Object Signing and Encryption). Она
поддерживает:
Ключевая особенность — строгая поддержка спецификаций RFC и работа через асинхронные API, что делает её подходящей для современных Node.js-приложений.
Установка:
npm install jose
JWT-аутентификация строится на принципе stateless-сервера: сервер не хранит сессии, вся необходимая информация находится в токене.
Основные этапы:
Для подписи токена используется класс SignJWT.
import { SignJWT } from 'jose';
const secret = new TextEncoder().encode('super-secret-key');
const token = await new SignJWT({ role: 'user', id: 123 })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('2h')
.sign(secret);
setProtectedHeader — определяет алгоритм подписиsetIssuedAt — время создания токенаsetExpirationTime — срок действияsign — финальная подписьPayload не должен содержать чувствительные данные (например, пароли), так как он лишь кодируется, но не шифруется.
Для верификации используется jwtVerify.
import { jwtVerify } from 'jose';
const secret = new TextEncoder().encode('super-secret-key');
const { payload, protectedHeader } = await jwtVerify(token, secret);
Результат включает:
payload — данные токенаprotectedHeader — информация о алгоритмеПри несоответствии подписи или истечении срока действия будет выброшена ошибка.
В более безопасных системах применяется пара ключей: приватный и публичный.
import { SignJWT } from 'jose';
import { readFileSync } from 'fs';
const privateKey = readFileSync('./private.pem', 'utf8');
const token = await new SignJWT({ id: 1, role: 'admin' })
.setProtectedHeader({ alg: 'RS256' })
.setIssuedAt()
.setExpirationTime('1h')
.sign(privateKey);
import { jwtVerify } from 'jose';
import { readFileSync } from 'fs';
const publicKey = readFileSync('./public.pem', 'utf8');
const { payload } = await jwtVerify(token, publicKey);
Преимущество RS256 заключается в том, что публичный ключ может быть безопасно распространён среди множества сервисов, в то время как приватный остаётся только у сервера авторизации.
Типичная интеграция в Node.js (например, Express):
import { jwtVerify } from 'jose';
const secret = new TextEncoder().encode('super-secret-key');
export async function authMiddleware(req, res, next) {
try {
const header = req.headers.authorization;
if (!header) {
return res.status(401).send('Token required');
}
const token = header.split(' ')[1];
const { payload } = await jwtVerify(token, secret);
req.user = payload;
next();
} catch (err) {
res.status(401).send('Invalid token');
}
}
После успешной проверки данные пользователя становятся доступны в
req.user.
Payload токена содержит claims, которые делятся на категории:
Стандартизированные поля:
iss — издатель токенаsub — субъект (обычно ID пользователя)aud — аудиторияexp — время истеченияiat — время выпускаПроизвольные поля, например:
rolepermissionsСпецифичные для приложения данные, не стандартизированные.
JWT обычно имеет ограниченный срок действия. Это снижает риск компрометации.
Распространённая схема:
Refresh token используется для получения нового access token без повторного логина.
При работе с jwtVerify возможны типовые ошибки:
JWTExpired — токен истёкJWSSignatureVerificationFailed — неверная подписьJWTInvalid — некорректный форматПример обработки:
import { jwtVerify } from 'jose';
try {
const { payload } = await jwtVerify(token, secret);
} catch (err) {
if (err.code === 'ERR_JWT_EXPIRED') {
// обработка истёкшего токена
}
}
Ключевые принципы:
Особое внимание уделяется защите приватных ключей и предотвращению их утечки.
Помимо подписи, jose поддерживает шифрование
токенов.
JWE используется, когда требуется скрыть содержимое payload.
Основные этапы:
Это добавляет дополнительный уровень защиты поверх подписи.
В микросервисной архитектуре JWT применяется как единый механизм идентификации между сервисами.
Типовая схема:
При использовании RS256 публичный ключ распространяется по всем сервисам, что исключает необходимость обращения к центральному серверу при каждой проверке.
Практика безопасного управления ключами включает:
kid (Key ID) в header токена для выбора
нужного ключаПример header:
{
"alg": "RS256",
"kid": "key-2026-01"
}
Эти ошибки приводят к снижению уровня безопасности и усложняют масштабирование системы.