В библиотеке jose основным способом проверки подписи JWT является
функция jwtVerify, которая выполняет криптографическую
проверку токена, валидацию стандартных полей и обеспечивает безопасное
извлечение полезной нагрузки.
jwtVerify работает в связке с алгоритмами JWS (JSON Web
Signature) и требует корректно предоставленного ключа в формате JWK, PEM
или через JWKS-резолвер. В отличие от простого декодирования токена,
здесь выполняется полная проверка подлинности подписи, что делает этот
метод ключевым элементом безопасности при работе с JWT.
При вызове jwtVerify происходит последовательная
обработка:
exp, nbf,
iss, aud)Функция возвращает объект с двумя основными частями:
payload — полезная нагрузка токенаprotectedHeader — заголовок JWTimport { jwtVerify } from 'jose'
const secret = new TextEncoder().encode('super-secret-key')
const { payload, protectedHeader } = await jwtVerify(
token,
secret
)
В данном примере используется симметричный ключ. Алгоритм (например,
HS256) определяется автоматически из заголовка JWT, но
может быть ограничен явно.
Для повышения безопасности рекомендуется фиксировать список разрешённых алгоритмов:
const { payload } = await jwtVerify(token, secret, {
algorithms: ['HS256']
})
Это предотвращает атаки, связанные с подменой алгоритма в заголовке токена.
JWT часто используется в распределённых системах, где важно строго контролировать источник и назначение токена.
const { payload } = await jwtVerify(token, secret, {
issuer: 'https://auth.example.com',
audience: 'api.example.com',
subject: 'user-123'
})
Параметры проверяются строго:
issuer — кто выпустил токенaudience — для кого предназначен токенsubject — субъект токенаНесовпадение любого значения приводит к выбросу ошибки.
JWT содержит временные ограничения:
exp — срок истеченияnbf — не действителен до указанного времениiat — время выпускаВ реальных системах возможны небольшие расхождения времени между
серверами. Для этого используется clockTolerance.
const { payload } = await jwtVerify(token, secret, {
clockTolerance: 5 // 5 секунд
})
Это позволяет компенсировать небольшие временные сдвиги без нарушения безопасности.
В случае асимметричной криптографии используется публичный ключ:
import { jwtVerify, importSPKI } from 'jose'
const publicKey = await importSPKI(
`-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----`,
'RS256'
)
const { payload } = await jwtVerify(token, publicKey)
Преимущество такого подхода:
В распределённых системах ключи часто публикуются через JWKS endpoint. В jose предусмотрен механизм автоматического получения ключей.
import { jwtVerify, createRemoteJWKSet } from 'jose'
const JWKS = createRemoteJWKSet(
new URL('https://auth.example.com/.well-known/jwks.json')
)
const { payload } = await jwtVerify(token, JWKS)
При каждом запросе библиотека:
kid из заголовка JWTjwtVerify выбрасывает исключения при любой проблеме с
токеном.
Основные типы ошибок:
JWTExpired — токен просроченJWSSignatureVerificationFailed — неверная подписьJWTInvalid — некорректный форматJWTClaimValidationFailed — ошибка в claimsПример обработки:
try {
const { payload } = await jwtVerify(token, secret)
} catch (err) {
console.error(err.code)
}
Структурированная обработка ошибок позволяет точно определять причину отказа.
Помимо стандартных полей можно валидировать пользовательские claims
через параметр requiredClaims и ручную проверку:
const { payload } = await jwtVerify(token, secret)
if (!payload.role || payload.role !== 'admin') {
throw new Error('Insufficient role')
}
JWT остаётся только транспортом данных, а бизнес-логика проверок реализуется отдельно.
Иногда требуется проверка типа токена:
const { protectedHeader } = await jwtVerify(token, secret)
if (protectedHeader.typ !== 'JWT') {
throw new Error('Invalid token type')
}
Это защищает от подмены формата токена в мультипротокольных системах.
В серверных приложениях jwtVerify часто применяется в
промежуточных слоях:
export async function authMiddleware(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) {
res.status(401).end()
return
}
try {
const { payload } = await jwtVerify(token, JWKS)
req.user = payload
next()
} catch {
res.status(401).end()
}
}
Такой подход обеспечивает централизованную проверку доступа.
При интенсивной нагрузке критично учитывать:
TextEncoderjwtVerify оптимизирован для серверных сценариев, но
зависимость от криптографических операций остаётся основным фактором
затрат.
При интеграции проверки JWT необходимо учитывать:
algorithmsiss и aud в любом публичном
APIJWT предназначен для передачи утверждений, а не секретов.