JWKS管理
@authrim/server はJSON Web Key Sets (JWKS) を自動的に管理します。認可サーバーの公開鍵を検出し、効率的にキャッシュし、鍵のローテーションを処理します。これらはすべて手動の介入なしに行われます。
JWKSのライフサイクルを理解することで、パフォーマンスの調整や本番環境での鍵関連の問題のトラブルシューティングに役立ちます。
JWKS自動検出の仕組み
Section titled “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
- Discovery —
{issuer}/.well-known/openid-configurationからOIDC Discoveryドキュメントを取得 - JWKSフェッチ — Discoveryドキュメント内の
jwks_uriからJWKSを取得 - 鍵インポート — 各JWKを署名検証用のプラットフォームネイティブCryptoKeyとしてインポート
- キャッシュ — 鍵セットをメモリ(またはカスタムキャッシュプロバイダー)に保存
キャッシュの動作
Section titled “キャッシュの動作”SDKはトークン検証のたびに鍵を取得・インポートしなくて済むよう、インポート済みJWKS keyをキャッシュします。キャッシュは2つの入力で制御されます:
Cache-Controlヘッダー
Section titled “Cache-Controlヘッダー”SDKはJWKSエンドポイントレスポンスの Cache-Control ヘッダーに従います。認可サーバーが Cache-Control: max-age=3600 を返した場合、SDKは3600秒間再フェッチを行いません。
SDKは Cache-Control の値に関係なく、最大キャッシュ期間を24時間に制限しています。これにより、サーバーが非常に長いmax-ageを返した場合でも、鍵は最終的に更新されます。
jwksRefreshIntervalMs
Section titled “jwksRefreshIntervalMs”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});| 設定 | デフォルト | 説明 |
|---|---|---|
jwksRefreshIntervalMs | 3600000(1時間) | cached JWKS keyのfallback TTL |
キャッシュの優先順位
Section titled “キャッシュの優先順位”有効なキャッシュ期間は以下の順序で決定されます:
- Cache-Controlヘッダー — JWKSレスポンスのヘッダー(存在する場合)
- jwksRefreshIntervalMs —
Cache-Controlがない、または利用できない場合のfallback TTL - 24時間の上限 —
Cache-Control: max-ageの最大値
キャッシュの無効化
Section titled “キャッシュの無効化”invalidateJwksCache() でJWKSの強制更新を行います:
// Force the SDK to refetch JWKS on the next validationauthrim.invalidateJwksCache();これは以下の場合に有用です:
- 鍵がローテーションされた通知を受け取った場合
- 古い鍵が原因と思われる署名検証の失敗を検出した場合
- 手動の鍵ローテーションワークフローを実装している場合
無効化後、次の validateToken() 呼び出しで新しいJWKSフェッチがトリガーされます。
鍵ローテーションのサポート
Section titled “鍵ローテーションのサポート”SDKは鍵ローテーションを自動的に処理します。トークンがキャッシュされたJWKSにない kid(Key ID)を参照している場合、SDKは:
- single-flight pathで新しいJWKSを1回取得
- 新しいkey setでkey selectionを再試行
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を返す"]
Single-Flightパターン
Section titled “Single-Flightパターン”複数のリクエストが同時に到着し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回のみ)
SSRFからの保護
Section titled “SSRFからの保護”カスタムCacheProvider
Section titled “カスタムCacheProvider”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を実装してください。
明示的なjwksUri vs 自動検出
Section titled “明示的なjwksUri vs 自動検出”デフォルトでは、SDKはOIDC Discoveryドキュメントからの情報でJWKSエンドポイントを検出します。明示的な jwksUri でこれをオーバーライドできます:
// Auto-discovery (default) — fetches from .well-known/openid-configurationconst authrim = createAuthrimServer({ issuer: 'https://auth.example.com', audience: 'https://api.example.com',});
// Explicit JWKS URI — skips discovery, fetches directlyconst 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エンドポイントパスを使用している場合
鍵インポートの警告
Section titled “鍵インポートの警告”SDKは鍵のインポート時に予期しないパラメータに遭遇した場合、警告を出力することがあります。これらの警告は情報提供目的であり、鍵の使用を妨げるものではありません:
- 不明な鍵タイプ — 認識されない
kty値を持つJWKはスキップされる - 必須パラメータの欠落 — 必須フィールド(例:RSAの
nとe)が欠落したJWKはスキップされる - サポートされていないアルゴリズム — サポートリストにない
alg値を持つJWKはスキップされる
スキップされた鍵は、セット内の他の鍵で署名されたトークンの検証に影響しません。
JWKSエラーコード
Section titled “JWKSエラーコード”| コード | 説明 |
|---|---|
jwks_fetch_error | JWKS fetch失敗、response不正、またはcross-origin redirect block |
jwks_key_not_found | refresh後もトークンの kid に一致するkeyが見つからない |
jwks_key_ambiguous | tokenが kid を省略し、algorithmに一致するkeyが0件または複数件 |
jwks_key_import_error | 選択されたkeyを署名検証用にimportできない |
次のステップ
Section titled “次のステップ”- トークン検証 — JWKSを使用するJWT検証パイプライン
- DPoP検証 — JWK ThumbprintによるDPoP証明の検証
- セキュリティに関する考慮事項 — SSRF保護と本番環境のセキュリティガイダンス
- イントロスペクションと失効 — ローカルJWT検証の代替手段