Загрузка удалённого JWKS: createRemoteJWKSet

В задачах аутентификации JWT ключевым моментом становится проверка подписи токена. В большинстве современных систем подпись проверяется с использованием публичных ключей, опубликованных в формате JWKS (JSON Web Key Set). Такие ключи могут храниться локально, но в реальных продуктивных системах почти всегда используются удалённые JWKS endpoints.

В библиотеке jose для JavaScript предусмотрен механизм динамической загрузки и кеширования удалённых ключей через createRemoteJWKSet. Это позволяет автоматически получать актуальные ключи с сервера авторизации и использовать их для проверки JWT без ручного управления ключами.


Общий принцип работы удалённого JWKS

JWKS endpoint — это HTTP-ресурс, возвращающий JSON следующего вида:

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "abc123",
      "use": "sig",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

Каждый ключ содержит идентификатор kid, который затем указывается в JWT заголовке:

{
  "alg": "RS256",
  "kid": "abc123"
}

При проверке токена библиотека должна:

  • извлечь kid из заголовка JWT
  • загрузить JWKS с удалённого сервера
  • найти соответствующий ключ
  • использовать его для проверки подписи

createRemoteJWKSet автоматизирует весь этот процесс.


Базовое использование createRemoteJWKSet

Основная функция создаёт источник ключей, который динамически подтягивает JWKS по URL.

import { createRemoteJWKSet, jwtVerify } from 'jose'

const JWKS = createRemoteJWKSet(
  new URL('https://auth.example.com/.well-known/jwks.json')
)

const { payload, protectedHeader } = await jwtVerify(token, JWKS)

Здесь JWKS становится функцией-резолвером ключей. При каждой верификации JWT библиотека:

  • анализирует kid
  • обращается к JWKS endpoint (если нужно)
  • выбирает подходящий ключ
  • выполняет проверку подписи

Внутреннее поведение и кеширование

Одной из ключевых особенностей является встроенный кеш.

После первого запроса JWKS:

  • ответ сохраняется в памяти
  • повторные проверки не вызывают HTTP-запрос каждый раз
  • ключи переиспользуются до истечения TTL или изменения endpoint

Это критично для производительности, особенно при высокой нагрузке.


Обработка обновления ключей

В реальных системах ключи могут ротироваться. Например:

  • добавляется новый kid
  • старый ключ отзывается
  • меняется криптографический алгоритм

createRemoteJWKSet автоматически учитывает такие изменения:

  1. Если kid не найден в кеше
  2. выполняется повторный запрос к JWKS endpoint
  3. кеш обновляется
  4. поиск ключа повторяется

Это обеспечивает устойчивость к ротации ключей без остановки сервиса.


Настройка параметров запроса

createRemoteJWKSet принимает второй аргумент — параметры запроса:

const JWKS = createRemoteJWKSet(
  new URL('https://auth.example.com/.well-known/jwks.json'),
  {
    timeoutDuration: 5000,
    cooldownDuration: 30000
  }
)

timeoutDuration

Ограничивает время ожидания ответа от JWKS endpoint. Если сервер авторизации недоступен или отвечает слишком долго, запрос прерывается.

cooldownDuration

Определяет период, в течение которого повторные неудачные запросы к JWKS будут ограничены. Это защищает систему от перегрузки при сбоях внешнего провайдера.


Поведение при ошибках

Сценарии ошибок могут быть следующими:

JWKS недоступен

Если endpoint не отвечает:

  • используется кеш, если он есть
  • при отсутствии кеша верификация завершается ошибкой

Несовпадение kid

Если токен содержит kid, которого нет в JWKS:

  • выполняется повторная загрузка ключей
  • если ключ не найден снова — ошибка верификации

Некорректный формат JWKS

Если сервер возвращает невалидный JSON или структуру без keys:

  • верификация завершается исключением
  • кеш не обновляется

Работа с несколькими ключами

JWKS может содержать несколько активных ключей:

{
  "keys": [
    { "kid": "key1", "kty": "RSA", "n": "...", "e": "AQAB" },
    { "kid": "key2", "kty": "RSA", "n": "...", "e": "AQAB" }
  ]
}

При этом логика выбора:

  • извлекается kid из JWT
  • производится прямое сопоставление
  • используется только один ключ

Если kid отсутствует, поведение зависит от конфигурации токена и обычно считается ошибкой, так как невозможно однозначно выбрать ключ.


Интеграция с jwtVerify

createRemoteJWKSet чаще всего используется вместе с jwtVerify:

import { jwtVerify, createRemoteJWKSet } from 'jose'

const JWKS = createRemoteJWKSet(
  new URL('https://issuer.example.com/.well-known/jwks.json')
)

const result = await jwtVerify(token, JWKS, {
  issuer: 'https://issuer.example.com',
  audience: 'api-client'
})

Здесь происходит не только проверка подписи, но и валидация:

  • iss (issuer)
  • aud (audience)
  • срок действия токена

JWKS отвечает только за криптографическую часть.


Поведение при масштабировании системы

В распределённых системах JWKS endpoint становится центральной точкой доверия. Поэтому важно учитывать:

Высокая частота проверок

При тысячах запросов в секунду:

  • кеш снижает нагрузку на JWKS сервер
  • HTTP-запросы происходят только при обновлении ключей

Балансировка и CDN

JWKS endpoint часто размещают за CDN:

  • ускоряется доставка ключей
  • уменьшается задержка
  • повышается отказоустойчивость

createRemoteJWKSet работает с этим прозрачно, так как использует обычный HTTP(S) запрос.


Ограничения подхода

Несмотря на удобство, существуют важные ограничения:

Зависимость от внешнего сервиса

Если JWKS провайдер недоступен:

  • новые токены невозможно проверить
  • система может деградировать

Необходимость строгого контроля kid

Ошибки в kid делают токены непроверяемыми. Это требует дисциплины при выпуске JWT.


Риск кеширования устаревших ключей

Хотя библиотека обновляет ключи автоматически, при неправильной конфигурации возможна задержка обновления.


Практические сценарии использования

OAuth 2.0 провайдеры

  • Auth0
  • Keycloak
  • Cognito

Все они предоставляют JWKS endpoints, совместимые с createRemoteJWKSet.


Микросервисная архитектура

Каждый сервис:

  • проверяет токены самостоятельно
  • использует общий JWKS endpoint
  • не хранит секреты подписи

Zero-trust модели

В архитектурах с нулевым доверием:

  • каждый запрос проверяется
  • JWKS используется как источник доверенных ключей
  • подпись — единственный механизм доверия

Важные особенности реализации в jose

  • полностью асинхронная загрузка ключей
  • поддержка строгой типизации ключей
  • защита от повторных запросов при параллельных проверках
  • оптимизированный кеш в памяти процесса

Поведение в многопоточности и serverless

В serverless-средах:

  • кеш существует только в рамках одного инстанса
  • при холодном старте JWKS загружается заново
  • при warm start используется локальный кеш

В многопроцессных системах:

  • каждый процесс имеет собственный кеш
  • синхронизации между процессами нет

Сравнение с локальными ключами

Подход Преимущества Недостатки
Remote JWKS автоматическая ротация, централизованное управление зависимость от сети
Local keys независимость от сети сложная ротация

Использование createRemoteJWKSet становится стандартом для систем, где безопасность и динамическое управление ключами важнее полной автономности проверки токенов.