コンテンツにスキップ

JWKS管理

@authrim/server はJSON Web Key Sets (JWKS) を自動的に管理します。認可サーバーの公開鍵を検出し、効率的にキャッシュし、鍵のローテーションを処理します。これらはすべて手動の介入なしに行われます。

JWKSのライフサイクルを理解することで、パフォーマンスの調整や本番環境での鍵関連の問題のトラブルシューティングに役立ちます。

authrim.init() を呼び出すと、SDKはOIDC DiscoveryからJWKSエンドポイントを特定します:

sequenceDiagram
    participant SDK as @authrim/server
    participant Issuer as Authorization Server

    SDK->>Issuer: GET /.well-known/openid-configuration
    Issuer-->>SDK: { "jwks_uri": "https://auth.example.com/jwks" }
    SDK->>Issuer: GET /jwks
    Issuer-->>SDK: { "keys": [ { "kid": "key-1", ... } ] }
    SDK->>SDK: Import and cache public keys
  1. Discovery{issuer}/.well-known/openid-configuration からOIDC Discoveryドキュメントを取得
  2. JWKSフェッチ — Discoveryドキュメント内の jwks_uri からJWKSを取得
  3. 鍵インポート — 各JWKを署名検証用のプラットフォームネイティブCryptoKeyとしてインポート
  4. キャッシュ — 鍵セットをメモリ(またはカスタムキャッシュプロバイダー)に保存

SDKはトークン検証のたびに鍵を取得・インポートしなくて済むよう、インポート済みJWKS keyをキャッシュします。キャッシュは2つの入力で制御されます:

SDKはJWKSエンドポイントレスポンスの Cache-Control ヘッダーに従います。認可サーバーが Cache-Control: max-age=3600 を返した場合、SDKは3600秒間再フェッチを行いません。

SDKは Cache-Control の値に関係なく、最大キャッシュ期間を24時間に制限しています。これにより、サーバーが非常に長いmax-ageを返した場合でも、鍵は最終的に更新されます。

jwksRefreshIntervalMs 設定は、JWKSレスポンスに利用可能な Cache-Control: max-age が含まれない場合のfallback cache TTLです。

const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
jwksRefreshIntervalMs: 1800000, // 30 minutes
});
設定デフォルト説明
jwksRefreshIntervalMs3600000(1時間)cached JWKS keyのfallback TTL

有効なキャッシュ期間は以下の順序で決定されます:

  1. Cache-Controlヘッダー — JWKSレスポンスのヘッダー(存在する場合)
  2. jwksRefreshIntervalMsCache-Control がない、または利用できない場合のfallback TTL
  3. 24時間の上限Cache-Control: max-age の最大値

invalidateJwksCache() でJWKSの強制更新を行います:

// Force the SDK to refetch JWKS on the next validation
authrim.invalidateJwksCache();

これは以下の場合に有用です:

  • 鍵がローテーションされた通知を受け取った場合
  • 古い鍵が原因と思われる署名検証の失敗を検出した場合
  • 手動の鍵ローテーションワークフローを実装している場合

無効化後、次の validateToken() 呼び出しで新しいJWKSフェッチがトリガーされます。

SDKは鍵ローテーションを自動的に処理します。トークンがキャッシュされたJWKSにない kid(Key ID)を参照している場合、SDKは:

  1. single-flight pathで新しいJWKSを1回取得
  2. 新しいkey setでkey selectionを再試行
  3. kid がまだ見つからない場合、token validation経由で jwks_key_not_found を返す

この自動リトライメカニズムは、認可サーバーがリソースサーバーのキャッシュが期限切れになる前に新しい鍵で署名を開始する一般的なローテーションシナリオに対応しています。

flowchart TD
    A["kid: 'key-2' の<br>トークン"] --> B{"kidがキャッシュに<br>存在する?"}
    B -->|はい| C["キャッシュされた<br>鍵を使用"]
    B -->|いいえ| E["新しいJWKSを<br>取得"]
    E --> G{"新しいJWKSに<br>kidが存在する?"}
    G -->|はい| H["新しい鍵を使用"]
    G -->|いいえ| F["jwks_key_not_foundを返す"]

複数のリクエストが同時に到着しJWKSの更新をトリガーした場合、SDKはそれらを単一のネットワークリクエストに集約します。これにより「thundering herd」問題を防止します:

sequenceDiagram
    participant R1 as リクエスト 1
    participant R2 as リクエスト 2
    participant R3 as リクエスト 3
    participant SDK as @authrim/server
    participant Auth as 認可サーバー

    R1->>SDK: validateToken() — kid未検出
    R2->>SDK: validateToken() — kid未検出
    R3->>SDK: validateToken() — kid未検出
    SDK->>Auth: GET /jwks(単一リクエスト)
    Auth-->>SDK: { "keys": [...] }
    SDK-->>R1: 検証結果
    SDK-->>R2: 検証結果
    SDK-->>R3: 検証結果

3つのリクエストすべてが同じJWKSフェッチを待ち、結果を共有します。これにより:

  • 認可サーバーへの負荷を軽減
  • 同時検証間での一貫した鍵状態を保証
  • レイテンシを最小化(ラウンドトリップは1回のみ)

custom evictionやobservabilityが必要なprocess-local cache向けに、CacheProvider を注入できます。SDKのcache valueにはimport済みkey objectが含まれるため、runtime key表現を安全に保持・復元できない限り、JSON Redis例のような実装は使わないでください。

import type { CacheProvider } from '@authrim/server/providers';
const cache = new Map<string, { value: any[]; expiresAt: number }>();
const localJwksCache: CacheProvider<any[]> = {
get(key) {
const entry = cache.get(key);
if (!entry || entry.expiresAt <= Date.now()) {
cache.delete(key);
return undefined;
}
return entry.value;
},
set(key, value, ttlMs = 3600_000) {
cache.set(key, { value, expiresAt: Date.now() + ttlMs });
},
delete(key) {
cache.delete(key);
},
};
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
jwksCache: localJwksCache,
});

このhookは、process-local TTL policy、metrics、isolate-local cacheとの統合が必要な場合に使います。cross-processの分散キャッシュでは、SDK pathの外側でauthorization serverのJWKS responseをキャッシュするか、返却前にkeyを安全に再インポートできるproviderを実装してください。

デフォルトでは、SDKはOIDC Discoveryドキュメントからの情報でJWKSエンドポイントを検出します。明示的な jwksUri でこれをオーバーライドできます:

// Auto-discovery (default) — fetches from .well-known/openid-configuration
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
});
// Explicit JWKS URI — skips discovery, fetches directly
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
});

明示的な jwksUri を使用する場面:

  • 認可サーバーがOIDC Discoveryをサポートしていない場合
  • 起動時間を短縮するために初期のDiscoveryリクエストを避けたい場合
  • 非標準のJWKSエンドポイントパスを使用している場合

SDKは鍵のインポート時に予期しないパラメータに遭遇した場合、警告を出力することがあります。これらの警告は情報提供目的であり、鍵の使用を妨げるものではありません:

  • 不明な鍵タイプ — 認識されない kty 値を持つJWKはスキップされる
  • 必須パラメータの欠落 — 必須フィールド(例:RSAの ne)が欠落したJWKはスキップされる
  • サポートされていないアルゴリズム — サポートリストにない alg 値を持つJWKはスキップされる

スキップされた鍵は、セット内の他の鍵で署名されたトークンの検証に影響しません。

コード説明
jwks_fetch_errorJWKS fetch失敗、response不正、またはcross-origin redirect block
jwks_key_not_foundrefresh後もトークンの kid に一致するkeyが見つからない
jwks_key_ambiguoustokenが kid を省略し、algorithmに一致するkeyが0件または複数件
jwks_key_import_error選択されたkeyを署名検証用にimportできない