При верификации JWT через jose и удалённый JWKS (JSON
Web Key Set) библиотека выполняет сопоставление ключа подписи токена с
набором ключей, полученных из JWKS endpoint. Ошибки
JWKSNoMatchingKey и JWKSMultipleMatchingKeys
возникают на этапе выбора подходящего ключа и почти всегда связаны с
некорректной конфигурацией ключей или несогласованностью между issuer и
JWKS.
При вызове:
import { jwtVerify, createRemoteJWKSet } from 'jose'
const JWKS = createRemoteJWKSet(new URL('https://example.com/.well-known/jwks.json'))
const { payload } = await jwtVerify(token, JWKS)
библиотека выполняет последовательность шагов:
kid (Key ID) из заголовка JWTkidalg, kty, use)Любое отклонение от однозначного соответствия приводит к ошибкам выбора ключа.
Ошибка JWKSNoMatchingKey возникает, когда среди всех
ключей JWKS не найден ни один подходящий ключ для проверки подписи
JWT.
1. Несоответствие kid
JWT содержит:
{
"kid": "abc123"
}
но в JWKS нет ключа с таким kid.
JWKS:
{
"keys": [
{ "kid": "def456", "kty": "RSA", "use": "sig" }
]
}
2. Ротация ключей без синхронизации
Старый ключ удалён из JWKS, но токены ещё подписываются им.
3. Неправильный issuer или JWKS URL
Частая ошибка при работе с несколькими окружениями:
4. Несовместимость алгоритмов
Например:
RS256EC ключиПолезно проверять:
console.log(protectedHeader)
console.log(await JWKS(protectedHeader, undefined))
Также важно убедиться, что:
kid присутствует в JWKSalg, kty)Ошибка JWKSMultipleMatchingKeys возникает, когда найдено
несколько ключей, подходящих для проверки одного JWT. Это более редкая,
но критическая ситуация, указывающая на неоднозначность в JWKS.
1. Дублирование kid
JWKS содержит несколько ключей с одинаковым kid:
{
"keys": [
{ "kid": "abc123", "kty": "RSA" },
{ "kid": "abc123", "kty": "RSA" }
]
}
2. Перекрывающиеся ключи при ротации
При плавной ротации ключей старый и новый ключ могут временно иметь одинаковые параметры:
kidalguse: sig3. Некорректная агрегация JWKS
При объединении нескольких источников JWKS (например, multi-tenant auth):
kid4. Ошибки прокси или кеширования
Иногда CDN или кеширующий слой возвращает дубликаты ключей.
При обнаружении нескольких подходящих ключей библиотека не выбирает “первый попавшийся”, а явно выбрасывает:
JWKSMultipleMatchingKeys: multiple matching keys found in JWKS
Это сделано для предотвращения:
Логика внутри jose при поиске ключа:
kidalguse: sigkty)Результат должен быть строго один ключ.
При обновлении ключей:
kidРезультат: JWKSMultipleMatchingKeys
Два региона публикуют JWKS:
kid = key-1kid = key-1После агрегации возникает конфликт.
JWT выдан сервером A, JWKS берётся с сервера B:
JWKSNoMatchingKeyiss и JWKS endpointkid всех входящих JWTkid во всей системеПроверка JWKS перед отдачей:
const kids = jwks.keys.map(k => k.kid)
const hasDuplicates = new Set(kids).size !== kids.length
createRemoteJWKSet кэширует JWKS и может временно
удерживать устаревшие ключи. Это влияет на:
Настройки кэширования могут быть критичны в системах с частой сменой ключей.
kidRS256)