@dofe/infra-jwt 0.1.40 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { JwtModule } from './jwt.module';
2
+ export { JwksClient } from './jwks-client';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/jwt/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/jwt/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JwtModule = void 0;
3
+ exports.JwksClient = exports.JwtModule = void 0;
4
4
  var jwt_module_1 = require("./jwt.module");
5
5
  Object.defineProperty(exports, "JwtModule", { enumerable: true, get: function () { return jwt_module_1.JwtModule; } });
6
+ var jwks_client_1 = require("./jwks-client");
7
+ Object.defineProperty(exports, "JwksClient", { enumerable: true, get: function () { return jwks_client_1.JwksClient; } });
6
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/jwt/src/index.ts"],"names":[],"mappings":";;;AAAA,2CAAyC;AAAhC,uGAAA,SAAS,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/jwt/src/index.ts"],"names":[],"mappings":";;;AAAA,2CAAyC;AAAhC,uGAAA,SAAS,OAAA;AAClB,6CAA2C;AAAlC,yGAAA,UAAU,OAAA"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Simple JWKS client — fetches and caches public keys from an OIDC JWKS endpoint.
3
+ * Converts JWK RSA keys to PEM format for use with JWT verification.
4
+ *
5
+ * Features:
6
+ * - In-memory cache with configurable TTL
7
+ * - Graceful stale cache on fetch failure
8
+ * - Zero external dependencies (no `jose`, `node-jose`, etc.)
9
+ */
10
+ export declare class JwksClient {
11
+ private readonly jwksUri;
12
+ private readonly cacheTtlMs;
13
+ private cache;
14
+ constructor(jwksUri: string, cacheTtlMs?: number);
15
+ /**
16
+ * Get a PEM public key by key ID (kid).
17
+ * Fetches and caches JWKS on first call or cache expiry.
18
+ * Returns null if no matching key is found.
19
+ */
20
+ getPublicKey(kid: string): Promise<string | null>;
21
+ private ensureCache;
22
+ /**
23
+ * Convert a JWK RSA public key to PEM format (PKCS#1).
24
+ * Uses minimal ASN.1 DER encoding — no external dependencies.
25
+ */
26
+ private jwkToPem;
27
+ private encodeAsn1Length;
28
+ private encodeAsn1Integer;
29
+ private encodeAsn1Sequence;
30
+ }
31
+ //# sourceMappingURL=jwks-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks-client.d.ts","sourceRoot":"","sources":["../../../packages/jwt/src/jwks-client.ts"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,OAAO,CAAC,KAAK,CAAiE;gBAG3D,OAAO,EAAE,MAAM,EACf,UAAU,SAAgB;IAG7C;;;;OAIG;IACG,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAKzC,WAAW;IA0BzB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAyBhB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,kBAAkB;CAO3B"}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JwksClient = void 0;
4
+ /**
5
+ * Simple JWKS client — fetches and caches public keys from an OIDC JWKS endpoint.
6
+ * Converts JWK RSA keys to PEM format for use with JWT verification.
7
+ *
8
+ * Features:
9
+ * - In-memory cache with configurable TTL
10
+ * - Graceful stale cache on fetch failure
11
+ * - Zero external dependencies (no `jose`, `node-jose`, etc.)
12
+ */
13
+ class JwksClient {
14
+ jwksUri;
15
+ cacheTtlMs;
16
+ cache = null;
17
+ constructor(jwksUri, cacheTtlMs = 5 * 60 * 1000) {
18
+ this.jwksUri = jwksUri;
19
+ this.cacheTtlMs = cacheTtlMs;
20
+ }
21
+ /**
22
+ * Get a PEM public key by key ID (kid).
23
+ * Fetches and caches JWKS on first call or cache expiry.
24
+ * Returns null if no matching key is found.
25
+ */
26
+ async getPublicKey(kid) {
27
+ await this.ensureCache();
28
+ return this.cache?.keys.get(kid) ?? null;
29
+ }
30
+ async ensureCache() {
31
+ if (this.cache && Date.now() < this.cache.expiresAt)
32
+ return;
33
+ try {
34
+ const response = await fetch(this.jwksUri);
35
+ if (!response.ok) {
36
+ throw new Error(`JWKS fetch failed: ${response.status}`);
37
+ }
38
+ const jwks = (await response.json());
39
+ const keys = new Map();
40
+ for (const jwk of jwks.keys) {
41
+ if (jwk.kid) {
42
+ keys.set(jwk.kid, this.jwkToPem(jwk));
43
+ }
44
+ }
45
+ this.cache = { keys, expiresAt: Date.now() + this.cacheTtlMs };
46
+ }
47
+ catch (error) {
48
+ // On fetch failure, keep stale cache if available
49
+ if (!this.cache) {
50
+ throw error;
51
+ }
52
+ }
53
+ }
54
+ /**
55
+ * Convert a JWK RSA public key to PEM format (PKCS#1).
56
+ * Uses minimal ASN.1 DER encoding — no external dependencies.
57
+ */
58
+ jwkToPem(jwk) {
59
+ if (jwk.kty !== 'RSA' || !jwk.n || !jwk.e) {
60
+ throw new Error(`Unsupported JWK key type: ${jwk.kty}`);
61
+ }
62
+ const base64UrlDecode = (str) => {
63
+ let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
64
+ while (base64.length % 4 !== 0)
65
+ base64 += '=';
66
+ return Buffer.from(base64, 'base64');
67
+ };
68
+ const modulus = base64UrlDecode(jwk.n);
69
+ const exponent = base64UrlDecode(jwk.e);
70
+ const modulusBytes = this.encodeAsn1Integer(modulus);
71
+ const exponentBytes = this.encodeAsn1Integer(exponent);
72
+ const sequence = this.encodeAsn1Sequence(Buffer.concat([modulusBytes, exponentBytes]));
73
+ const base64Der = sequence.toString('base64');
74
+ const lines = base64Der.match(/.{1,64}/g) ?? [base64Der];
75
+ return `-----BEGIN RSA PUBLIC KEY-----\n${lines.join('\n')}\n-----END RSA PUBLIC KEY-----`;
76
+ }
77
+ encodeAsn1Length(length) {
78
+ if (length < 128) {
79
+ return Buffer.from([length]);
80
+ }
81
+ const bytes = [];
82
+ let len = length;
83
+ while (len > 0) {
84
+ bytes.unshift(len & 0xff);
85
+ len >>= 8;
86
+ }
87
+ return Buffer.from([0x80 | bytes.length, ...bytes]);
88
+ }
89
+ encodeAsn1Integer(value) {
90
+ const data = value[0] & 0x80 ? Buffer.concat([Buffer.from([0x00]), value]) : value;
91
+ return Buffer.concat([
92
+ Buffer.from([0x02]),
93
+ this.encodeAsn1Length(data.length),
94
+ data,
95
+ ]);
96
+ }
97
+ encodeAsn1Sequence(contents) {
98
+ return Buffer.concat([
99
+ Buffer.from([0x30]),
100
+ this.encodeAsn1Length(contents.length),
101
+ contents,
102
+ ]);
103
+ }
104
+ }
105
+ exports.JwksClient = JwksClient;
106
+ //# sourceMappingURL=jwks-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwks-client.js","sourceRoot":"","sources":["../../../packages/jwt/src/jwks-client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dofe/infra-jwt",
3
- "version": "0.1.40",
3
+ "version": "0.1.41",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -8,6 +8,10 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "default": "./dist/index.js"
10
10
  },
11
+ "./jwks-client": {
12
+ "types": "./dist/jwks-client.d.ts",
13
+ "default": "./dist/jwks-client.js"
14
+ },
11
15
  "./jwt.module": {
12
16
  "types": "./dist/jwt.module.d.ts",
13
17
  "default": "./dist/jwt.module.js"
@@ -19,7 +23,7 @@
19
23
  "@nestjs/jwt": "^11.0.0",
20
24
  "reflect-metadata": "^0.2.0",
21
25
  "rxjs": "^7.8.0",
22
- "@dofe/infra-common": "^0.1.40"
26
+ "@dofe/infra-common": "^0.1.41"
23
27
  },
24
28
  "devDependencies": {
25
29
  "rimraf": "^6.1.3",