@superblocksteam/shared 0.9521.0 → 0.9523.0

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
@@ -20,5 +20,4 @@ export * from './utils/tracedEventEmitter';
20
20
  export * from './utils/unreachable';
21
21
  export * from './tracing/methodTracing';
22
22
  export * from './tracing/errorSanitizer';
23
- export * from './jwt';
24
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -36,5 +36,4 @@ __exportStar(require("./utils/tracedEventEmitter"), exports);
36
36
  __exportStar(require("./utils/unreachable"), exports);
37
37
  __exportStar(require("./tracing/methodTracing"), exports);
38
38
  __exportStar(require("./tracing/errorSanitizer"), exports);
39
- __exportStar(require("./jwt"), exports);
40
39
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,0CAAwB;AACxB,0CAAwB;AACxB,0CAAwB;AACxB,4CAA0B;AAC1B,8CAA4B;AAC5B,kDAAgC;AAChC,8CAA4B;AAC5B,mDAAiC;AACjC,iDAA+B;AAC/B,0CAAwB;AACxB,+CAA6B;AAC7B,uDAAqC;AACrC,2CAAyB;AACzB,4CAA0B;AAC1B,6CAA2B;AAC3B,gDAA8B;AAC9B,yCAAuB;AACvB,6DAA2C;AAC3C,sDAAoC;AACpC,0DAAwC;AACxC,2DAAyC;AACzC,wCAAsB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,0CAAwB;AACxB,0CAAwB;AACxB,0CAAwB;AACxB,4CAA0B;AAC1B,8CAA4B;AAC5B,kDAAgC;AAChC,8CAA4B;AAC5B,mDAAiC;AACjC,iDAA+B;AAC/B,0CAAwB;AACxB,+CAA6B;AAC7B,uDAAqC;AACrC,2CAAyB;AACzB,4CAA0B;AAC1B,6CAA2B;AAC3B,gDAA8B;AAC9B,yCAAuB;AACvB,6DAA2C;AAC3C,sDAAoC;AACpC,0DAAwC;AACxC,2DAAyC"}
@@ -20,5 +20,4 @@ export * from './utils/tracedEventEmitter';
20
20
  export * from './utils/unreachable';
21
21
  export * from './tracing/methodTracing';
22
22
  export * from './tracing/errorSanitizer';
23
- export * from './jwt';
24
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
package/dist-esm/index.js CHANGED
@@ -20,5 +20,4 @@ export * from './utils/tracedEventEmitter';
20
20
  export * from './utils/unreachable';
21
21
  export * from './tracing/methodTracing';
22
22
  export * from './tracing/errorSanitizer';
23
- export * from './jwt';
24
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,OAAO,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/shared",
3
- "version": "0.9521.0",
3
+ "version": "0.9523.0",
4
4
  "description": "Superblocks Shared Resources",
5
5
  "repository": "https://github.com/superblocksteam/shared.git",
6
6
  "license": "Superblocks Community Software License",
@@ -35,9 +35,6 @@
35
35
  "isomorphic-ws": "^5.0.0",
36
36
  "js-base64": "^3.7.5",
37
37
  "json5": "2.2.3",
38
- "jsonwebtoken": "^9.0.2",
39
- "jwks-rsa": "^3.1.0",
40
- "jwt-decode": "^4.0.0",
41
38
  "lodash": "^4.17.21",
42
39
  "normalizr": "3.6.2",
43
40
  "pino": "6.11.0",
@@ -52,7 +49,6 @@
52
49
  "@swc/core": "^1.10.14",
53
50
  "@swc/jest": "0.2.37",
54
51
  "@types/jest": "29.5.12",
55
- "@types/jsonwebtoken": "^9.0.7",
56
52
  "@types/lodash": "^4.17.0",
57
53
  "@types/node": "20.17.24",
58
54
  "@types/semver": "^7.5.6",
package/src/index.ts CHANGED
@@ -20,4 +20,3 @@ export * from './utils/tracedEventEmitter';
20
20
  export * from './utils/unreachable';
21
21
  export * from './tracing/methodTracing';
22
22
  export * from './tracing/errorSanitizer';
23
- export * from './jwt';
@@ -1,2 +0,0 @@
1
- export * from './verifier';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jwt/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
package/dist/jwt/index.js DELETED
@@ -1,18 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./verifier"), exports);
18
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jwt/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B"}
@@ -1,39 +0,0 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- import { JwtPayload, VerifyOptions } from 'jsonwebtoken';
4
- import { Agent as HttpAgent } from 'http';
5
- import { Agent as HttpsAgent } from 'https';
6
- /**
7
- * Verifies JWTs using a JWKS endpoint to resolve signing keys on demand.
8
- */
9
- export declare class JwtVerifier {
10
- private readonly jwks;
11
- constructor(options: JwtVerifierOptions);
12
- verify<T extends JwtPayload>(token: string, options?: VerifyOptions): Promise<T>;
13
- }
14
- /**
15
- * Options that configure how the verifier fetches and caches JWKS signing keys.
16
- */
17
- export interface JwtVerifierOptions {
18
- jwksUri: string;
19
- rateLimit?: boolean;
20
- cache?: boolean;
21
- cacheMaxEntries?: number;
22
- cacheMaxAge?: number;
23
- jwksRequestsPerMinute?: number;
24
- proxy?: string;
25
- requestHeaders?: Headers;
26
- timeout?: number;
27
- requestAgent?: HttpAgent | HttpsAgent;
28
- fetcher?(jwksUri: string): Promise<{
29
- keys: unknown;
30
- }>;
31
- getKeysInterceptor?(): Promise<JwtKey[]>;
32
- }
33
- export type Headers = Record<string, string>;
34
- export interface JwtKey {
35
- kid: string;
36
- alg: string;
37
- [key: string]: unknown;
38
- }
39
- //# sourceMappingURL=verifier.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/jwt/verifier.ts"],"names":[],"mappings":";;AAEA,OAAO,EAAa,UAAU,EAAU,aAAa,EAAE,MAAM,cAAc,CAAC;AAG5E,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,OAAO,CAAC;AAE5C;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAC9B,OAAO,EAAE,kBAAkB;IAIjC,MAAM,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC;CAiBvF;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACtC,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtD,kBAAkB,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC1C;AAED,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE7C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
@@ -1,37 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.JwtVerifier = void 0;
7
- const jwks_rsa_1 = __importDefault(require("jwks-rsa"));
8
- const jwt_decode_1 = require("jwt-decode");
9
- const jsonwebtoken_1 = require("jsonwebtoken");
10
- const lodash_1 = require("lodash");
11
- const errors_1 = require("../errors");
12
- /**
13
- * Verifies JWTs using a JWKS endpoint to resolve signing keys on demand.
14
- */
15
- class JwtVerifier {
16
- constructor(options) {
17
- this.jwks = (0, jwks_rsa_1.default)(options);
18
- }
19
- async verify(token, options) {
20
- const decodedHeader = (0, jwt_decode_1.jwtDecode)(token, { header: true });
21
- if ((0, lodash_1.isEmpty)(decodedHeader.kid)) {
22
- throw new errors_1.UnauthorizedError('Invalid JWT as kid header is missing.');
23
- }
24
- const publicKey = (await this.jwks.getSigningKey(decodedHeader.kid)).getPublicKey();
25
- if ((0, lodash_1.isEmpty)(publicKey)) {
26
- throw new errors_1.UnauthorizedError('JWT could not be verified as no corresponding public key was found.');
27
- }
28
- try {
29
- return (0, jsonwebtoken_1.verify)(token, publicKey, options);
30
- }
31
- catch (err) {
32
- throw new errors_1.UnauthorizedError(`JWT verification failed: ${err.message}`);
33
- }
34
- }
35
- }
36
- exports.JwtVerifier = JwtVerifier;
37
- //# sourceMappingURL=verifier.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.js","sourceRoot":"","sources":["../../src/jwt/verifier.ts"],"names":[],"mappings":";;;;;;AAAA,wDAA+B;AAC/B,2CAAuC;AACvC,+CAA4E;AAC5E,mCAAiC;AACjC,sCAA8C;AAI9C;;GAEG;AACH,MAAa,WAAW;IAEtB,YAAY,OAA2B;QACrC,IAAI,CAAC,IAAI,GAAG,IAAA,kBAAO,EAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,MAAM,CAAuB,KAAa,EAAE,OAAuB;QACvE,MAAM,aAAa,GAAG,IAAA,sBAAS,EAAY,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,IAAA,gBAAO,EAAC,aAAa,CAAC,GAAG,CAAC,EAAE;YAC9B,MAAM,IAAI,0BAAiB,CAAC,uCAAuC,CAAC,CAAC;SACtE;QAED,MAAM,SAAS,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;QACpF,IAAI,IAAA,gBAAO,EAAC,SAAS,CAAC,EAAE;YACtB,MAAM,IAAI,0BAAiB,CAAC,qEAAqE,CAAC,CAAC;SACpG;QAED,IAAI;YACF,OAAO,IAAA,qBAAM,EAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAM,CAAC;SAC/C;QAAC,OAAO,GAAG,EAAE;YACZ,MAAM,IAAI,0BAAiB,CAAC,4BAA6B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;SACnF;IACH,CAAC;CACF;AAvBD,kCAuBC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=verifier.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.test.d.ts","sourceRoot":"","sources":["../../src/jwt/verifier.test.ts"],"names":[],"mappings":""}
@@ -1,108 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const crypto_1 = require("crypto");
4
- const jsonwebtoken_1 = require("jsonwebtoken");
5
- const verifier_1 = require("./verifier");
6
- const KEY_ID = 'test-key';
7
- const createSigningMaterial = (kid = KEY_ID) => {
8
- const { publicKey, privateKey } = (0, crypto_1.generateKeyPairSync)('rsa', {
9
- modulusLength: 2048
10
- });
11
- const jwk = {
12
- ...publicKey.export({ format: 'jwk' }),
13
- kid,
14
- alg: 'RS256',
15
- use: 'sig'
16
- };
17
- return {
18
- jwk,
19
- privateKeyPem: privateKey.export({ type: 'pkcs1', format: 'pem' }).toString()
20
- };
21
- };
22
- const buildJwtVerifier = (jwk) => {
23
- const getKeysInterceptor = jest.fn(async () => [jwk]);
24
- const options = {
25
- jwksUri: 'https://example.com/.well-known/jwks.json',
26
- cache: false,
27
- getKeysInterceptor
28
- };
29
- return { verifier: new verifier_1.JwtVerifier(options), getKeysInterceptor };
30
- };
31
- describe('JwtVerifier', () => {
32
- let signingMaterial;
33
- beforeAll(() => {
34
- signingMaterial = createSigningMaterial();
35
- });
36
- it('verifies a token with a matching JWKS entry', async () => {
37
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
38
- const payload = {
39
- sub: 'user-123',
40
- scope: 'read:all'
41
- };
42
- const token = (0, jsonwebtoken_1.sign)(payload, signingMaterial.privateKeyPem, {
43
- algorithm: 'RS256',
44
- keyid: signingMaterial.jwk.kid
45
- });
46
- const result = await verifier.verify(token);
47
- expect(getKeysInterceptor).toHaveBeenCalledTimes(1);
48
- expect(result.sub).toBe(payload.sub);
49
- expect(result.scope).toBe(payload.scope);
50
- });
51
- it('throws UnauthorizedError when the signature does not match the JWKS entry', async () => {
52
- const { verifier } = buildJwtVerifier(signingMaterial.jwk);
53
- const mismatchedKey = createSigningMaterial();
54
- const invalidToken = (0, jsonwebtoken_1.sign)({ sub: 'user-456' }, mismatchedKey.privateKeyPem, {
55
- algorithm: 'RS256',
56
- keyid: signingMaterial.jwk.kid
57
- });
58
- await expect(verifier.verify(invalidToken)).rejects.toMatchObject({
59
- status: 401,
60
- message: expect.stringContaining('invalid signature')
61
- });
62
- });
63
- it('honors JwtVerifyOptions when audience and issuer match', async () => {
64
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
65
- const audience = 'target-app';
66
- const issuer = 'https://issuer.example.com';
67
- const token = (0, jsonwebtoken_1.sign)({ sub: 'user-789' }, signingMaterial.privateKeyPem, {
68
- algorithm: 'RS256',
69
- keyid: signingMaterial.jwk.kid,
70
- audience,
71
- issuer
72
- });
73
- const result = await verifier.verify(token, {
74
- algorithms: ['RS256'],
75
- audience,
76
- issuer
77
- });
78
- expect(getKeysInterceptor).toHaveBeenCalledTimes(1);
79
- expect(result.sub).toBe('user-789');
80
- expect(result.aud).toBe(audience);
81
- expect(result.iss).toBe(issuer);
82
- });
83
- it('rejects when VerifyOptions constraints fail', async () => {
84
- const { verifier } = buildJwtVerifier(signingMaterial.jwk);
85
- const audience = 'target-app';
86
- const issuer = 'https://issuer.example.com';
87
- const token = (0, jsonwebtoken_1.sign)({ sub: 'user-987' }, signingMaterial.privateKeyPem, {
88
- algorithm: 'RS256',
89
- keyid: signingMaterial.jwk.kid,
90
- audience,
91
- issuer
92
- });
93
- await expect(verifier.verify(token, {
94
- algorithms: ['RS256'],
95
- audience: `${audience}-mismatch`,
96
- issuer
97
- })).rejects.toMatchObject({
98
- status: 401,
99
- message: expect.stringContaining('jwt audience invalid')
100
- });
101
- });
102
- it('rejects malformed tokens before attempting JWKS lookup', async () => {
103
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
104
- await expect(verifier.verify('definitely-not-a-jwt')).rejects.toThrow('Invalid token specified');
105
- expect(getKeysInterceptor).not.toHaveBeenCalled();
106
- });
107
- });
108
- //# sourceMappingURL=verifier.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.test.js","sourceRoot":"","sources":["../../src/jwt/verifier.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export * from './verifier';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jwt/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from './verifier';
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jwt/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
@@ -1,39 +0,0 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- import { JwtPayload, VerifyOptions } from 'jsonwebtoken';
4
- import { Agent as HttpAgent } from 'http';
5
- import { Agent as HttpsAgent } from 'https';
6
- /**
7
- * Verifies JWTs using a JWKS endpoint to resolve signing keys on demand.
8
- */
9
- export declare class JwtVerifier {
10
- private readonly jwks;
11
- constructor(options: JwtVerifierOptions);
12
- verify<T extends JwtPayload>(token: string, options?: VerifyOptions): Promise<T>;
13
- }
14
- /**
15
- * Options that configure how the verifier fetches and caches JWKS signing keys.
16
- */
17
- export interface JwtVerifierOptions {
18
- jwksUri: string;
19
- rateLimit?: boolean;
20
- cache?: boolean;
21
- cacheMaxEntries?: number;
22
- cacheMaxAge?: number;
23
- jwksRequestsPerMinute?: number;
24
- proxy?: string;
25
- requestHeaders?: Headers;
26
- timeout?: number;
27
- requestAgent?: HttpAgent | HttpsAgent;
28
- fetcher?(jwksUri: string): Promise<{
29
- keys: unknown;
30
- }>;
31
- getKeysInterceptor?(): Promise<JwtKey[]>;
32
- }
33
- export type Headers = Record<string, string>;
34
- export interface JwtKey {
35
- kid: string;
36
- alg: string;
37
- [key: string]: unknown;
38
- }
39
- //# sourceMappingURL=verifier.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/jwt/verifier.ts"],"names":[],"mappings":";;AAEA,OAAO,EAAa,UAAU,EAAU,aAAa,EAAE,MAAM,cAAc,CAAC;AAG5E,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,OAAO,CAAC;AAE5C;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;gBAC9B,OAAO,EAAE,kBAAkB;IAIjC,MAAM,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC;CAiBvF;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACtC,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtD,kBAAkB,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC1C;AAED,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE7C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
@@ -1,30 +0,0 @@
1
- import jwksRsa from 'jwks-rsa';
2
- import { jwtDecode } from 'jwt-decode';
3
- import { verify } from 'jsonwebtoken';
4
- import { isEmpty } from 'lodash';
5
- import { UnauthorizedError } from '../errors';
6
- /**
7
- * Verifies JWTs using a JWKS endpoint to resolve signing keys on demand.
8
- */
9
- export class JwtVerifier {
10
- constructor(options) {
11
- this.jwks = jwksRsa(options);
12
- }
13
- async verify(token, options) {
14
- const decodedHeader = jwtDecode(token, { header: true });
15
- if (isEmpty(decodedHeader.kid)) {
16
- throw new UnauthorizedError('Invalid JWT as kid header is missing.');
17
- }
18
- const publicKey = (await this.jwks.getSigningKey(decodedHeader.kid)).getPublicKey();
19
- if (isEmpty(publicKey)) {
20
- throw new UnauthorizedError('JWT could not be verified as no corresponding public key was found.');
21
- }
22
- try {
23
- return verify(token, publicKey, options);
24
- }
25
- catch (err) {
26
- throw new UnauthorizedError(`JWT verification failed: ${err.message}`);
27
- }
28
- }
29
- }
30
- //# sourceMappingURL=verifier.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.js","sourceRoot":"","sources":["../../src/jwt/verifier.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAyB,MAAM,EAAiB,MAAM,cAAc,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAI9C;;GAEG;AACH,MAAM,OAAO,WAAW;IAEtB,YAAY,OAA2B;QACrC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,MAAM,CAAuB,KAAa,EAAE,OAAuB;QACvE,MAAM,aAAa,GAAG,SAAS,CAAY,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE;YAC9B,MAAM,IAAI,iBAAiB,CAAC,uCAAuC,CAAC,CAAC;SACtE;QAED,MAAM,SAAS,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;QACpF,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE;YACtB,MAAM,IAAI,iBAAiB,CAAC,qEAAqE,CAAC,CAAC;SACpG;QAED,IAAI;YACF,OAAO,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAM,CAAC;SAC/C;QAAC,OAAO,GAAG,EAAE;YACZ,MAAM,IAAI,iBAAiB,CAAC,4BAA6B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;SACnF;IACH,CAAC;CACF"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=verifier.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.test.d.ts","sourceRoot":"","sources":["../../src/jwt/verifier.test.ts"],"names":[],"mappings":""}
@@ -1,106 +0,0 @@
1
- import { generateKeyPairSync } from 'crypto';
2
- import { sign } from 'jsonwebtoken';
3
- import { JwtVerifier } from './verifier';
4
- const KEY_ID = 'test-key';
5
- const createSigningMaterial = (kid = KEY_ID) => {
6
- const { publicKey, privateKey } = generateKeyPairSync('rsa', {
7
- modulusLength: 2048
8
- });
9
- const jwk = {
10
- ...publicKey.export({ format: 'jwk' }),
11
- kid,
12
- alg: 'RS256',
13
- use: 'sig'
14
- };
15
- return {
16
- jwk,
17
- privateKeyPem: privateKey.export({ type: 'pkcs1', format: 'pem' }).toString()
18
- };
19
- };
20
- const buildJwtVerifier = (jwk) => {
21
- const getKeysInterceptor = jest.fn(async () => [jwk]);
22
- const options = {
23
- jwksUri: 'https://example.com/.well-known/jwks.json',
24
- cache: false,
25
- getKeysInterceptor
26
- };
27
- return { verifier: new JwtVerifier(options), getKeysInterceptor };
28
- };
29
- describe('JwtVerifier', () => {
30
- let signingMaterial;
31
- beforeAll(() => {
32
- signingMaterial = createSigningMaterial();
33
- });
34
- it('verifies a token with a matching JWKS entry', async () => {
35
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
36
- const payload = {
37
- sub: 'user-123',
38
- scope: 'read:all'
39
- };
40
- const token = sign(payload, signingMaterial.privateKeyPem, {
41
- algorithm: 'RS256',
42
- keyid: signingMaterial.jwk.kid
43
- });
44
- const result = await verifier.verify(token);
45
- expect(getKeysInterceptor).toHaveBeenCalledTimes(1);
46
- expect(result.sub).toBe(payload.sub);
47
- expect(result.scope).toBe(payload.scope);
48
- });
49
- it('throws UnauthorizedError when the signature does not match the JWKS entry', async () => {
50
- const { verifier } = buildJwtVerifier(signingMaterial.jwk);
51
- const mismatchedKey = createSigningMaterial();
52
- const invalidToken = sign({ sub: 'user-456' }, mismatchedKey.privateKeyPem, {
53
- algorithm: 'RS256',
54
- keyid: signingMaterial.jwk.kid
55
- });
56
- await expect(verifier.verify(invalidToken)).rejects.toMatchObject({
57
- status: 401,
58
- message: expect.stringContaining('invalid signature')
59
- });
60
- });
61
- it('honors JwtVerifyOptions when audience and issuer match', async () => {
62
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
63
- const audience = 'target-app';
64
- const issuer = 'https://issuer.example.com';
65
- const token = sign({ sub: 'user-789' }, signingMaterial.privateKeyPem, {
66
- algorithm: 'RS256',
67
- keyid: signingMaterial.jwk.kid,
68
- audience,
69
- issuer
70
- });
71
- const result = await verifier.verify(token, {
72
- algorithms: ['RS256'],
73
- audience,
74
- issuer
75
- });
76
- expect(getKeysInterceptor).toHaveBeenCalledTimes(1);
77
- expect(result.sub).toBe('user-789');
78
- expect(result.aud).toBe(audience);
79
- expect(result.iss).toBe(issuer);
80
- });
81
- it('rejects when VerifyOptions constraints fail', async () => {
82
- const { verifier } = buildJwtVerifier(signingMaterial.jwk);
83
- const audience = 'target-app';
84
- const issuer = 'https://issuer.example.com';
85
- const token = sign({ sub: 'user-987' }, signingMaterial.privateKeyPem, {
86
- algorithm: 'RS256',
87
- keyid: signingMaterial.jwk.kid,
88
- audience,
89
- issuer
90
- });
91
- await expect(verifier.verify(token, {
92
- algorithms: ['RS256'],
93
- audience: `${audience}-mismatch`,
94
- issuer
95
- })).rejects.toMatchObject({
96
- status: 401,
97
- message: expect.stringContaining('jwt audience invalid')
98
- });
99
- });
100
- it('rejects malformed tokens before attempting JWKS lookup', async () => {
101
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
102
- await expect(verifier.verify('definitely-not-a-jwt')).rejects.toThrow('Invalid token specified');
103
- expect(getKeysInterceptor).not.toHaveBeenCalled();
104
- });
105
- });
106
- //# sourceMappingURL=verifier.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verifier.test.js","sourceRoot":"","sources":["../../src/jwt/verifier.test.ts"],"names":[],"mappings":"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"}
package/src/jwt/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './verifier';
@@ -1,144 +0,0 @@
1
- import { generateKeyPairSync } from 'crypto';
2
- import { JwtPayload, sign } from 'jsonwebtoken';
3
-
4
- import { JwtVerifier, JwtVerifierOptions, JwtKey } from './verifier';
5
-
6
- type SigningMaterial = {
7
- privateKeyPem: string;
8
- jwk: JwtKey;
9
- };
10
-
11
- const KEY_ID = 'test-key';
12
-
13
- const createSigningMaterial = (kid = KEY_ID): SigningMaterial => {
14
- const { publicKey, privateKey } = generateKeyPairSync('rsa', {
15
- modulusLength: 2048
16
- });
17
-
18
- const jwk = {
19
- ...publicKey.export({ format: 'jwk' }),
20
- kid,
21
- alg: 'RS256',
22
- use: 'sig'
23
- } as SigningMaterial['jwk'];
24
-
25
- return {
26
- jwk,
27
- privateKeyPem: privateKey.export({ type: 'pkcs1', format: 'pem' }).toString()
28
- };
29
- };
30
-
31
- const buildJwtVerifier = (jwk: SigningMaterial['jwk']) => {
32
- const getKeysInterceptor = jest.fn(async () => [jwk]);
33
-
34
- const options: JwtVerifierOptions = {
35
- jwksUri: 'https://example.com/.well-known/jwks.json',
36
- cache: false,
37
- getKeysInterceptor
38
- };
39
-
40
- return { verifier: new JwtVerifier(options), getKeysInterceptor };
41
- };
42
-
43
- describe('JwtVerifier', () => {
44
- let signingMaterial: SigningMaterial;
45
- beforeAll(() => {
46
- signingMaterial = createSigningMaterial();
47
- });
48
- it('verifies a token with a matching JWKS entry', async () => {
49
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
50
-
51
- type TokenPayload = JwtPayload & { scope: string };
52
-
53
- const payload: TokenPayload = {
54
- sub: 'user-123',
55
- scope: 'read:all'
56
- };
57
-
58
- const token = sign(payload, signingMaterial.privateKeyPem, {
59
- algorithm: 'RS256',
60
- keyid: signingMaterial.jwk.kid
61
- });
62
-
63
- const result = await verifier.verify<TokenPayload>(token);
64
-
65
- expect(getKeysInterceptor).toHaveBeenCalledTimes(1);
66
- expect(result.sub).toBe(payload.sub);
67
- expect(result.scope).toBe(payload.scope);
68
- });
69
-
70
- it('throws UnauthorizedError when the signature does not match the JWKS entry', async () => {
71
- const { verifier } = buildJwtVerifier(signingMaterial.jwk);
72
-
73
- const mismatchedKey = createSigningMaterial();
74
-
75
- const invalidToken = sign({ sub: 'user-456' }, mismatchedKey.privateKeyPem, {
76
- algorithm: 'RS256',
77
- keyid: signingMaterial.jwk.kid
78
- });
79
-
80
- await expect(verifier.verify(invalidToken)).rejects.toMatchObject({
81
- status: 401,
82
- message: expect.stringContaining('invalid signature')
83
- });
84
- });
85
-
86
- it('honors JwtVerifyOptions when audience and issuer match', async () => {
87
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
88
-
89
- type ScopedPayload = JwtPayload & { sub: string };
90
-
91
- const audience = 'target-app';
92
- const issuer = 'https://issuer.example.com';
93
-
94
- const token = sign({ sub: 'user-789' }, signingMaterial.privateKeyPem, {
95
- algorithm: 'RS256',
96
- keyid: signingMaterial.jwk.kid,
97
- audience,
98
- issuer
99
- });
100
-
101
- const result = await verifier.verify<ScopedPayload>(token, {
102
- algorithms: ['RS256'],
103
- audience,
104
- issuer
105
- });
106
-
107
- expect(getKeysInterceptor).toHaveBeenCalledTimes(1);
108
- expect(result.sub).toBe('user-789');
109
- expect(result.aud).toBe(audience);
110
- expect(result.iss).toBe(issuer);
111
- });
112
-
113
- it('rejects when VerifyOptions constraints fail', async () => {
114
- const { verifier } = buildJwtVerifier(signingMaterial.jwk);
115
-
116
- const audience = 'target-app';
117
- const issuer = 'https://issuer.example.com';
118
-
119
- const token = sign({ sub: 'user-987' }, signingMaterial.privateKeyPem, {
120
- algorithm: 'RS256',
121
- keyid: signingMaterial.jwk.kid,
122
- audience,
123
- issuer
124
- });
125
-
126
- await expect(
127
- verifier.verify(token, {
128
- algorithms: ['RS256'],
129
- audience: `${audience}-mismatch`,
130
- issuer
131
- })
132
- ).rejects.toMatchObject({
133
- status: 401,
134
- message: expect.stringContaining('jwt audience invalid')
135
- });
136
- });
137
-
138
- it('rejects malformed tokens before attempting JWKS lookup', async () => {
139
- const { verifier, getKeysInterceptor } = buildJwtVerifier(signingMaterial.jwk);
140
-
141
- await expect(verifier.verify('definitely-not-a-jwt')).rejects.toThrow('Invalid token specified');
142
- expect(getKeysInterceptor).not.toHaveBeenCalled();
143
- });
144
- });
@@ -1,61 +0,0 @@
1
- import jwksRsa from 'jwks-rsa';
2
- import { jwtDecode } from 'jwt-decode';
3
- import { JwtHeader, JwtPayload, verify, VerifyOptions } from 'jsonwebtoken';
4
- import { isEmpty } from 'lodash';
5
- import { UnauthorizedError } from '../errors';
6
- import { Agent as HttpAgent } from 'http';
7
- import { Agent as HttpsAgent } from 'https';
8
-
9
- /**
10
- * Verifies JWTs using a JWKS endpoint to resolve signing keys on demand.
11
- */
12
- export class JwtVerifier {
13
- private readonly jwks: jwksRsa.JwksClient;
14
- constructor(options: JwtVerifierOptions) {
15
- this.jwks = jwksRsa(options);
16
- }
17
-
18
- async verify<T extends JwtPayload>(token: string, options?: VerifyOptions): Promise<T> {
19
- const decodedHeader = jwtDecode<JwtHeader>(token, { header: true });
20
- if (isEmpty(decodedHeader.kid)) {
21
- throw new UnauthorizedError('Invalid JWT as kid header is missing.');
22
- }
23
-
24
- const publicKey = (await this.jwks.getSigningKey(decodedHeader.kid)).getPublicKey();
25
- if (isEmpty(publicKey)) {
26
- throw new UnauthorizedError('JWT could not be verified as no corresponding public key was found.');
27
- }
28
-
29
- try {
30
- return verify(token, publicKey, options) as T;
31
- } catch (err) {
32
- throw new UnauthorizedError(`JWT verification failed: ${(err as Error).message}`);
33
- }
34
- }
35
- }
36
-
37
- /**
38
- * Options that configure how the verifier fetches and caches JWKS signing keys.
39
- */
40
- export interface JwtVerifierOptions {
41
- jwksUri: string;
42
- rateLimit?: boolean;
43
- cache?: boolean;
44
- cacheMaxEntries?: number;
45
- cacheMaxAge?: number;
46
- jwksRequestsPerMinute?: number;
47
- proxy?: string;
48
- requestHeaders?: Headers;
49
- timeout?: number;
50
- requestAgent?: HttpAgent | HttpsAgent;
51
- fetcher?(jwksUri: string): Promise<{ keys: unknown }>;
52
- getKeysInterceptor?(): Promise<JwtKey[]>;
53
- }
54
-
55
- export type Headers = Record<string, string>;
56
-
57
- export interface JwtKey {
58
- kid: string;
59
- alg: string;
60
- [key: string]: unknown;
61
- }