@fluojs/jwt
Advanced tools
@@ -9,8 +9,19 @@ import { type KeyObject } from 'node:crypto'; | ||
| private readonly requestTimeoutMs; | ||
| private readonly cacheMaxEntries; | ||
| private readonly cache; | ||
| constructor(uri: string, cacheTtl?: number, requestTimeoutMs?: number); | ||
| private lifecycleGeneration; | ||
| constructor(uri: string, cacheTtl?: number, requestTimeoutMs?: number, cacheMaxEntries?: number); | ||
| /** | ||
| * Clears all cached JWKS key material held by this client. | ||
| * | ||
| * Call this during application shutdown when the verifier/client lifecycle is | ||
| * owned manually, or when rotating identity-provider configuration. | ||
| */ | ||
| dispose(): void; | ||
| private isAbortError; | ||
| getSigningKey(kid: string): Promise<KeyObject>; | ||
| private pruneExpiredCacheEntries; | ||
| private evictOldestCacheEntries; | ||
| private fetchKeys; | ||
| } | ||
| //# sourceMappingURL=jwks.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"jwks.d.ts","sourceRoot":"","sources":["../../src/signing/jwks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAa9D;;GAEG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IALnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4D;gBAG/D,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAgB,EAC1B,gBAAgB,GAAE,MAAc;IAGnD,OAAO,CAAC,YAAY;IAId,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;YA+BtC,SAAS;CAqCxB"} | ||
| {"version":3,"file":"jwks.d.ts","sourceRoot":"","sources":["../../src/signing/jwks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAiC9D;;GAEG;AACH,qBAAa,UAAU;IAKnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAPlC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4D;IAClF,OAAO,CAAC,mBAAmB,CAAK;gBAGb,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAgB,EAC1B,gBAAgB,GAAE,MAAc,EAChC,eAAe,GAAE,MAAuC;IAO3E;;;;;OAKG;IACH,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,YAAY;IAId,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IA0CpD,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,uBAAuB;YAYjB,SAAS;CAsCxB"} |
+63
-5
| import { createPublicKey } from 'node:crypto'; | ||
| import { JwtConfigurationError, JwtInvalidTokenError } from '../errors.js'; | ||
| const DEFAULT_JWKS_CACHE_MAX_ENTRIES = 100; | ||
| function assertNonNegativeFiniteNumber(value, label) { | ||
| if (!Number.isFinite(value) || value < 0) { | ||
| throw new JwtConfigurationError(`${label} must be a non-negative finite number.`); | ||
| } | ||
| } | ||
| function assertPositiveFiniteNumber(value, label) { | ||
| if (!Number.isFinite(value) || value <= 0) { | ||
| throw new JwtConfigurationError(`${label} must be a positive finite number.`); | ||
| } | ||
| } | ||
| function assertPositiveInteger(value, label) { | ||
| if (!Number.isInteger(value) || value <= 0) { | ||
| throw new JwtConfigurationError(`${label} must be a positive integer.`); | ||
| } | ||
| } | ||
| /** | ||
@@ -8,7 +25,23 @@ * Represents the jwks client. | ||
| cache = new Map(); | ||
| constructor(uri, cacheTtl = 600_000, requestTimeoutMs = 5_000) { | ||
| lifecycleGeneration = 0; | ||
| constructor(uri, cacheTtl = 600_000, requestTimeoutMs = 5_000, cacheMaxEntries = DEFAULT_JWKS_CACHE_MAX_ENTRIES) { | ||
| this.uri = uri; | ||
| this.cacheTtl = cacheTtl; | ||
| this.requestTimeoutMs = requestTimeoutMs; | ||
| this.cacheMaxEntries = cacheMaxEntries; | ||
| assertNonNegativeFiniteNumber(cacheTtl, 'JWKS cache ttl'); | ||
| assertPositiveFiniteNumber(requestTimeoutMs, 'JWKS request timeout'); | ||
| assertPositiveInteger(cacheMaxEntries, 'JWKS cache max entries'); | ||
| } | ||
| /** | ||
| * Clears all cached JWKS key material held by this client. | ||
| * | ||
| * Call this during application shutdown when the verifier/client lifecycle is | ||
| * owned manually, or when rotating identity-provider configuration. | ||
| */ | ||
| dispose() { | ||
| this.lifecycleGeneration += 1; | ||
| this.cache.clear(); | ||
| } | ||
| isAbortError(error) { | ||
@@ -19,2 +52,3 @@ return error instanceof Error && error.name === 'AbortError'; | ||
| const now = Date.now(); | ||
| this.pruneExpiredCacheEntries(now); | ||
| const cached = this.cache.get(kid); | ||
@@ -24,3 +58,7 @@ if (cached && cached.expiresAt > now) { | ||
| } | ||
| const fetchGeneration = this.lifecycleGeneration; | ||
| const keys = await this.fetchKeys(); | ||
| if (fetchGeneration !== this.lifecycleGeneration) { | ||
| throw new JwtConfigurationError('JWKS client was disposed while fetching keys.'); | ||
| } | ||
| const jwk = keys.find(entry => entry.kid === kid); | ||
@@ -39,8 +77,27 @@ if (!jwk) { | ||
| } | ||
| this.cache.set(kid, { | ||
| expiresAt: now + this.cacheTtl, | ||
| key | ||
| }); | ||
| if (this.cacheTtl > 0 && fetchGeneration === this.lifecycleGeneration) { | ||
| this.cache.set(kid, { | ||
| expiresAt: now + this.cacheTtl, | ||
| key | ||
| }); | ||
| this.evictOldestCacheEntries(); | ||
| } | ||
| return key; | ||
| } | ||
| pruneExpiredCacheEntries(now) { | ||
| for (const [kid, cached] of this.cache) { | ||
| if (cached.expiresAt <= now) { | ||
| this.cache.delete(kid); | ||
| } | ||
| } | ||
| } | ||
| evictOldestCacheEntries() { | ||
| while (this.cache.size > this.cacheMaxEntries) { | ||
| const oldestKid = this.cache.keys().next().value; | ||
| if (oldestKid === undefined) { | ||
| return; | ||
| } | ||
| this.cache.delete(oldestKid); | ||
| } | ||
| } | ||
| async fetchKeys() { | ||
@@ -52,2 +109,3 @@ let response; | ||
| }, this.requestTimeoutMs); | ||
| timeout.unref?.(); | ||
| try { | ||
@@ -54,0 +112,0 @@ response = await fetch(this.uri, { |
@@ -27,2 +27,10 @@ import type { JwtAlgorithm, JwtPrincipal, JwtVerifierOptions } from '../types.js'; | ||
| /** | ||
| * Releases verifier-owned remote JWKS cache entries. | ||
| * | ||
| * Call this when disposing a manually managed verifier or replacing its remote | ||
| * identity-provider configuration. Static keys and provider callbacks are not | ||
| * owned by the verifier and are therefore left untouched. | ||
| */ | ||
| dispose(): void; | ||
| /** | ||
| * Verifies a JWT access token with per-call claim-policy overrides while reusing configured key sources. | ||
@@ -29,0 +37,0 @@ * |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/signing/verifier.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAA0B,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAG1G;;GAEG;AACH,eAAO,MAAM,WAAW,eAAiC,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAI3D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAOjE,CAAC;AAmDF,KAAK,gCAAgC,GAAG,IAAI,CAC1C,kBAAkB,EAClB,YAAY,GAAG,UAAU,GAAG,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CACpF,CAAC;AA+LF;;GAEG;AACH,qBACa,kBAAkB;IAMjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqB;IAC/D,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAiC;gBAE/C,OAAO,EAAE,kBAAkB;IAalD,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI7D;;;;;;;;;;OAUG;IACG,8BAA8B,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,CAAC,gCAAgC,CAAC,GACnD,OAAO,CAAC,YAAY,CAAC;IAqBlB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAQ9D,OAAO,CAAC,gCAAgC;YAsB1B,WAAW;IA+BzB,OAAO,CAAC,kBAAkB;YAUZ,oBAAoB;YAgBpB,wBAAwB;YAsBxB,8BAA8B;YAsB9B,kBAAkB;IAWhC,OAAO,CAAC,mBAAmB;IAwB3B,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,yBAAyB;YAiBnB,oBAAoB;CAOnC"} | ||
| {"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/signing/verifier.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAA0B,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAG1G;;GAEG;AACH,eAAO,MAAM,WAAW,eAAiC,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAI3D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAOjE,CAAC;AAmDF,KAAK,gCAAgC,GAAG,IAAI,CAC1C,kBAAkB,EAClB,YAAY,GAAG,UAAU,GAAG,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CACpF,CAAC;AA+LF;;GAEG;AACH,qBACa,kBAAkB;IAMjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqB;IAC/D,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAiC;gBAE/C,OAAO,EAAE,kBAAkB;IAalD,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI7D;;;;;;OAMG;IACH,OAAO,IAAI,IAAI;IAIf;;;;;;;;;;OAUG;IACG,8BAA8B,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,OAAO,CAAC,gCAAgC,CAAC,GACnD,OAAO,CAAC,YAAY,CAAC;IAqBlB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAQ9D,OAAO,CAAC,gCAAgC;YAsB1B,WAAW;IA+BzB,OAAO,CAAC,kBAAkB;YAUZ,oBAAoB;YAgBpB,wBAAwB;YAsBxB,8BAA8B;YAsB9B,kBAAkB;IAWhC,OAAO,CAAC,mBAAmB;IAwB3B,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,yBAAyB;YAiBnB,oBAAoB;CAOnC"} |
@@ -209,3 +209,3 @@ let _initClass; | ||
| assertJwtAlgorithms(options.algorithms, 'JWT verifier'); | ||
| this.jwksClient = options.jwksUri ? new JwksClient(options.jwksUri, options.jwksCacheTtl, options.jwksRequestTimeoutMs) : undefined; | ||
| this.jwksClient = options.jwksUri ? new JwksClient(options.jwksUri, options.jwksCacheTtl, options.jwksRequestTimeoutMs, options.jwksCacheMaxEntries) : undefined; | ||
| this.keyResolutionState = createKeyResolutionState(options.keys); | ||
@@ -220,2 +220,13 @@ this.refreshVerificationOptions = options.refreshToken ? this.createRefreshVerificationOptions(normalizeRefreshTokenOptions(options.refreshToken)) : undefined; | ||
| /** | ||
| * Releases verifier-owned remote JWKS cache entries. | ||
| * | ||
| * Call this when disposing a manually managed verifier or replacing its remote | ||
| * identity-provider configuration. Static keys and provider callbacks are not | ||
| * owned by the verifier and are therefore left untouched. | ||
| */ | ||
| dispose() { | ||
| this.jwksClient?.dispose(); | ||
| } | ||
| /** | ||
| * Verifies a JWT access token with per-call claim-policy overrides while reusing configured key sources. | ||
@@ -331,3 +342,3 @@ * | ||
| this.validateMaxAgeClaims(payload, options.maxAge, clockSkew, now); | ||
| if (typeof payload.exp === 'number' && payload.exp + clockSkew < now) { | ||
| if (typeof payload.exp === 'number' && payload.exp + clockSkew <= now) { | ||
| throw new JwtExpiredTokenError(); | ||
@@ -334,0 +345,0 @@ } |
+2
-0
@@ -28,2 +28,4 @@ import type { KeyObject } from 'node:crypto'; | ||
| jwksCacheTtl?: number; | ||
| /** Maximum number of remote JWKS public keys retained in the in-memory cache. Defaults to `100`. */ | ||
| jwksCacheMaxEntries?: number; | ||
| jwksRequestTimeoutMs?: number; | ||
@@ -30,0 +32,0 @@ jwksUri?: string; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAEnH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACrH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxD,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACzD;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACrD"} | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} |
+4
-4
@@ -12,3 +12,3 @@ { | ||
| ], | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", | ||
| "private": false, | ||
@@ -40,5 +40,5 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@fluojs/core": "^1.0.0", | ||
| "@fluojs/di": "^1.0.0", | ||
| "@fluojs/runtime": "^1.0.0" | ||
| "@fluojs/di": "^1.0.3", | ||
| "@fluojs/core": "^1.0.3", | ||
| "@fluojs/runtime": "^1.1.1" | ||
| }, | ||
@@ -45,0 +45,0 @@ "devDependencies": { |
+8
-1
@@ -62,2 +62,4 @@ # @fluojs/jwt | ||
| `forRootAsync(...)`는 `inject`에 나열된 provider에서 module-level `JwtVerifierOptions` 객체 하나를 resolve합니다. 이 factory는 요청별 상태를 받지 않습니다. 테넌트별 secret이나 identity provider가 필요한 경우 tenant lookup은 애플리케이션의 auth layer에 두고, token verification 중에는 `kid` 같은 token metadata와 미리 구성한 `keys[]`, `jwksUri`, 또는 `secretOrKeyProvider`를 사용해 검증 material을 선택하세요. | ||
| ```typescript | ||
@@ -152,2 +154,4 @@ import { Module, type Token } from '@fluojs/core'; | ||
| JWKS key는 `jwksCacheTtl` 밀리초 동안 cache되며 기본값은 `600_000`입니다. in-memory cache는 `jwksCacheMaxEntries`로 제한되고 기본값은 `100`입니다. lookup 전 만료된 entry를 정리하고, 제한을 넘으면 가장 오래 보관된 key를 제거합니다. 수동 shutdown이나 identity-provider 재설정 시에는 `JwksClient.dispose()` / `DefaultJwtVerifier.dispose()`로 보관 중인 remote key material을 비울 수 있습니다. `jwksCacheTtl`을 `0`으로 설정하면 bounded fetch timeout은 유지하면서 key 보관만 비활성화합니다. | ||
| `JwtService.verify(token, options)`는 호출 단위의 알고리즘/클레임 정책 재정의(`issuer`, `audience`, `clockSkewSeconds`, `maxAge`, `requireExp`)를 적용하더라도, 내부 JWKS client나 정적 key-resolution cache를 다시 만들지 않습니다. 호출 단위 검증은 `jwksUri`, `keys[]`, `publicKey`, `secret`, `secretOrKeyProvider` 같은 구성된 key source 자체를 교체하지는 않습니다. | ||
@@ -157,2 +161,4 @@ | ||
| 멀티테넌트 시스템에서는 발행된 토큰 header에 tenant-specific `kid`를 넣고 호환되는 key source를 미리 구성하는 방식을 권장합니다. `secretOrKeyProvider`는 decoded token header만 인자로 받으므로, request header, route param, 기타 request-context tenant hint는 JWT verifier 호출 전에 애플리케이션 수준 strategy/guard 코드에서 처리해야 합니다. | ||
| ### 리프레시 토큰 | ||
@@ -168,3 +174,3 @@ | ||
| 검증은 잘못된 시간 정책에 대해 fail closed로 동작합니다. 검증에 참여하는 `exp`, `nbf`, `iat` 클레임은 유한한 JWT NumericDate 숫자여야 하며, `clockSkewSeconds`도 음수가 아닌 유한 숫자여야 합니다. 유한하지 않은 값은 expiration, not-before, age check를 늘리는 대신 거부됩니다. | ||
| 검증은 잘못된 시간 정책에 대해 fail closed로 동작합니다. 검증에 참여하는 `exp`, `nbf`, `iat` 클레임은 유한한 JWT NumericDate 숫자여야 하며, `clockSkewSeconds`도 음수가 아닌 유한 숫자여야 합니다. 유한하지 않은 값은 expiration, not-before, age check를 늘리는 대신 거부됩니다. verifier 시간이 `exp` NumericDate에 도달하면 토큰은 만료된 것으로 처리되며, 양수 clock skew가 경계를 덮지 않는 한 equality도 만료로 간주합니다. | ||
@@ -186,2 +192,3 @@ ## 공개 API 개요 | ||
| - `JwtClaims`, `JwtSigner`, `JwtVerifier`, `JwtKeyEntry`, `JwtAlgorithm`: 공개 서명 및 검증 계약입니다. | ||
| - `RefreshTokenOptions`, `RefreshTokenStore`, `RefreshTokenRecord`, `RefreshTokenConsumeInput`, `RefreshTokenRotateInput`, `RefreshTokenConsumeResult`: refresh-token 저장, rotation, replay detection 계약입니다. | ||
@@ -188,0 +195,0 @@ ### 에러와 diagnostics |
+8
-1
@@ -62,2 +62,4 @@ # @fluojs/jwt | ||
| `forRootAsync(...)` resolves one module-level `JwtVerifierOptions` object from the providers listed in `inject`. It does not receive per-request state. For tenant-specific secrets or identity providers, keep tenant lookup in your application auth layer and use token metadata such as `kid` with configured `keys[]`, `jwksUri`, or `secretOrKeyProvider` to select verification material during token verification. | ||
| ```typescript | ||
@@ -152,2 +154,4 @@ import { Module, type Token } from '@fluojs/core'; | ||
| JWKS keys are cached for `jwksCacheTtl` milliseconds (`600_000` by default) and the in-memory cache is bounded by `jwksCacheMaxEntries` (`100` by default). Expired entries are pruned before lookups, the oldest retained key is evicted when the bound is exceeded, and `JwksClient.dispose()` / `DefaultJwtVerifier.dispose()` clears retained remote key material during manual shutdown or identity-provider reconfiguration. A `jwksCacheTtl` of `0` disables key retention while still using bounded fetch timeouts. | ||
| `JwtService.verify(token, options)` applies per-call algorithm and claim-policy overrides (`issuer`, `audience`, `clockSkewSeconds`, `maxAge`, `requireExp`) without rebuilding the underlying JWKS client or static key-resolution cache. Per-call verification does not replace configured key sources such as `jwksUri`, `keys[]`, `publicKey`, `secret`, or `secretOrKeyProvider`. | ||
@@ -157,2 +161,4 @@ | ||
| For multi-tenant systems, prefer putting a tenant-specific `kid` in issued token headers and configuring compatible key sources up front. `secretOrKeyProvider` is called with the decoded token header only, so request headers, route params, or other request-context tenant hints must be handled by application-level strategy/guard code before calling the JWT verifier. | ||
| ### Refresh tokens | ||
@@ -168,3 +174,3 @@ | ||
| Verification fails closed on malformed time policy. `exp`, `nbf`, and `iat` claims that participate in verification must be finite JWT NumericDate numbers, and `clockSkewSeconds` must be a non-negative finite number. Non-finite values are rejected instead of extending expiration, not-before, or age checks. | ||
| Verification fails closed on malformed time policy. `exp`, `nbf`, and `iat` claims that participate in verification must be finite JWT NumericDate numbers, and `clockSkewSeconds` must be a non-negative finite number. Non-finite values are rejected instead of extending expiration, not-before, or age checks. A token is expired when verifier time reaches its `exp` NumericDate; equality is treated as expired unless positive clock skew still covers the boundary. | ||
@@ -186,2 +192,3 @@ ## Public API Overview | ||
| - `JwtClaims`, `JwtSigner`, `JwtVerifier`, `JwtKeyEntry`, `JwtAlgorithm`: Public signing and verification contracts. | ||
| - `RefreshTokenOptions`, `RefreshTokenStore`, `RefreshTokenRecord`, `RefreshTokenConsumeInput`, `RefreshTokenRotateInput`, and `RefreshTokenConsumeResult`: Refresh-token storage, rotation, and replay-detection contracts. | ||
@@ -188,0 +195,0 @@ ### Errors and diagnostics |
116146
6.55%1801
5.08%205
3.54%Updated
Updated
Updated