@xlt-token/jwt 2.1.0 → 2.3.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.cjs CHANGED
@@ -1,6 +1,34 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
2
29
  let node_crypto = require("node:crypto");
3
30
  let jsonwebtoken = require("jsonwebtoken");
31
+ jsonwebtoken = __toESM(jsonwebtoken);
4
32
 
5
33
  //#region src/jwt-config.ts
6
34
  const supportedAlgorithms = new Set([
@@ -48,11 +76,12 @@ function normalizeJwtKey(input) {
48
76
  };
49
77
  }
50
78
  function assertStrongHmacSecret(kid, secret) {
51
- if ((typeof secret === "string" ? Buffer.byteLength(secret) : Buffer.isBuffer(secret) ? secret.byteLength : 0) < 32) throw new Error(`JWT key "${kid}" uses a weak HMAC secret; use at least 32 bytes`);
79
+ if ((typeof secret === "string" ? Buffer.byteLength(secret) : Buffer.isBuffer(secret) ? secret.byteLength : secret instanceof node_crypto.KeyObject && secret.type === "secret" ? secret.symmetricKeySize : 0) < 32) throw new Error(`JWT key "${kid}" uses a weak HMAC secret; use at least 32 bytes`);
52
80
  }
53
81
 
54
82
  //#endregion
55
83
  //#region src/jwt-strategy.ts
84
+ const { decode, sign, verify } = jsonwebtoken.default;
56
85
  var JwtStrategy = class {
57
86
  constructor(jwtConfig) {
58
87
  this.jwtConfig = jwtConfig;
@@ -70,14 +99,14 @@ var JwtStrategy = class {
70
99
  return this.signPayload(payload);
71
100
  }
72
101
  verifyToken(token) {
73
- const decoded = (0, jsonwebtoken.decode)(token, { complete: true });
102
+ const decoded = decode(token, { complete: true });
74
103
  if (!decoded || typeof decoded === "string") throw new Error("JWT token is malformed");
75
104
  const { kid, alg } = decoded.header;
76
105
  if (!kid) throw new Error("JWT token header is missing kid");
77
106
  const key = this.jwtConfig.keys.get(kid);
78
107
  if (!key) throw new Error(`JWT key "${kid}" is not configured`);
79
108
  if (alg !== key.algorithm) throw new Error(`JWT algorithm "${String(alg)}" does not match configured key algorithm`);
80
- const payload = (0, jsonwebtoken.verify)(token, key.verificationKey, {
109
+ const payload = verify(token, key.verificationKey, {
81
110
  algorithms: [key.algorithm],
82
111
  ...this.jwtConfig.issuer && { issuer: this.jwtConfig.issuer },
83
112
  ...this.jwtConfig.audience && { audience: this.jwtConfig.audience }
@@ -95,11 +124,11 @@ var JwtStrategy = class {
95
124
  ...this.jwtConfig.audience && { audience: this.jwtConfig.audience },
96
125
  ...expiresIn !== void 0 && { expiresIn }
97
126
  };
98
- return (0, jsonwebtoken.sign)(payload, signingKeyFor(key), options);
127
+ return sign(payload, signingKeyFor(key), options);
99
128
  }
100
129
  };
101
130
  function resolveExpiresIn(timeout) {
102
- if (typeof timeout === "number" && timeout <= 0) return;
131
+ if (timeout === -1) return;
103
132
  return timeout;
104
133
  }
105
134
  function signingKeyFor(key) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/jwt-config.ts","../src/jwt-strategy.ts"],"mappings":";;;;KAEY,YAAA,GAAe,OAAA,CACzB,WAAA;AAAA,UAIe,WAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,MAAA,GAAS,MAAA;EACT,UAAA,GAAa,MAAA;EACb,eAAA,GAAkB,MAAA;AAAA;AAAA,UAGH,MAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,UAAA,EAAY,MAAA;EACZ,eAAA,EAAiB,MAAA;AAAA;AAAA,KAGP,WAAA;AAAA,UAEK,sBAAA;EACf,SAAA;EACA,IAAA,EAAM,WAAA;EACN,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,UAGI,iBAAA;EACf,SAAA;EACA,SAAA,EAAW,MAAA;EACX,IAAA,EAAM,WAAA,SAAoB,MAAA;EAC1B,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,iBAeG,uBAAA,CAAwB,KAAA,EAAO,sBAAA,GAAyB,iBAAA;;;KC7C5D,aAAA,GAAgB,UAAA;EAAe,GAAA;EAAa,GAAA;AAAA;AAAA,cAE3C,WAAA,YAAuB,aAAA,CAAc,aAAA;EAAA,iBAGnB,SAAA;EAAA,SAFpB,IAAA;cAEoB,SAAA,EAAW,iBAAA;EAExC,WAAA,CACE,OAAA,UACA,MAAA,EAAQ,cAAA,EACR,OAAA;IAAY,OAAA,GAAU,aAAA;EAAA;EAcxB,aAAA,CAAc,OAAA,EAAS,aAAA;EAIvB,WAAA,CAAY,KAAA,WAAgB,aAAA;EAAA,QAkCpB,WAAA;AAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/jwt-config.ts","../src/jwt-strategy.ts"],"mappings":";;;;KAGY,YAAA,GAAe,OAAA,CACzB,WAAA;AAAA,UAIe,WAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,MAAA,GAAS,MAAA;EACT,UAAA,GAAa,MAAA;EACb,eAAA,GAAkB,MAAA;AAAA;AAAA,UAGH,MAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,UAAA,EAAY,MAAA;EACZ,eAAA,EAAiB,MAAA;AAAA;AAAA,KAGP,WAAA;AAAA,UAEK,sBAAA;EACf,SAAA;EACA,IAAA,EAAM,WAAA;EACN,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,UAGI,iBAAA;EACf,SAAA;EACA,SAAA,EAAW,MAAA;EACX,IAAA,EAAM,WAAA,SAAoB,MAAA;EAC1B,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,iBAeG,uBAAA,CAAwB,KAAA,EAAO,sBAAA,GAAyB,iBAAA;;;KC5C5D,aAAA,GAAgB,UAAA;EAAe,GAAA;EAAa,GAAA;AAAA;AAAA,cAE3C,WAAA,YAAuB,aAAA,CAAc,aAAA;EAAA,iBAGnB,SAAA;EAAA,SAFpB,IAAA;cAEoB,SAAA,EAAW,iBAAA;EAExC,WAAA,CACE,OAAA,UACA,MAAA,EAAQ,cAAA,EACR,OAAA;IAAY,OAAA,GAAU,aAAA;EAAA;EAcxB,aAAA,CAAc,OAAA,EAAS,aAAA;EAIvB,WAAA,CAAY,KAAA,WAAgB,aAAA;EAAA,QAkCpB,WAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/jwt-config.ts","../src/jwt-strategy.ts"],"mappings":";;;;KAEY,YAAA,GAAe,OAAA,CACzB,WAAA;AAAA,UAIe,WAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,MAAA,GAAS,MAAA;EACT,UAAA,GAAa,MAAA;EACb,eAAA,GAAkB,MAAA;AAAA;AAAA,UAGH,MAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,UAAA,EAAY,MAAA;EACZ,eAAA,EAAiB,MAAA;AAAA;AAAA,KAGP,WAAA;AAAA,UAEK,sBAAA;EACf,SAAA;EACA,IAAA,EAAM,WAAA;EACN,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,UAGI,iBAAA;EACf,SAAA;EACA,SAAA,EAAW,MAAA;EACX,IAAA,EAAM,WAAA,SAAoB,MAAA;EAC1B,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,iBAeG,uBAAA,CAAwB,KAAA,EAAO,sBAAA,GAAyB,iBAAA;;;KC7C5D,aAAA,GAAgB,UAAA;EAAe,GAAA;EAAa,GAAA;AAAA;AAAA,cAE3C,WAAA,YAAuB,aAAA,CAAc,aAAA;EAAA,iBAGnB,SAAA;EAAA,SAFpB,IAAA;cAEoB,SAAA,EAAW,iBAAA;EAExC,WAAA,CACE,OAAA,UACA,MAAA,EAAQ,cAAA,EACR,OAAA;IAAY,OAAA,GAAU,aAAA;EAAA;EAcxB,aAAA,CAAc,OAAA,EAAS,aAAA;EAIvB,WAAA,CAAY,KAAA,WAAgB,aAAA;EAAA,QAkCpB,WAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/jwt-config.ts","../src/jwt-strategy.ts"],"mappings":";;;;KAGY,YAAA,GAAe,OAAA,CACzB,WAAA;AAAA,UAIe,WAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,MAAA,GAAS,MAAA;EACT,UAAA,GAAa,MAAA;EACb,eAAA,GAAkB,MAAA;AAAA;AAAA,UAGH,MAAA;EACf,GAAA;EACA,SAAA,EAAW,YAAA;EACX,UAAA,EAAY,MAAA;EACZ,eAAA,EAAiB,MAAA;AAAA;AAAA,KAGP,WAAA;AAAA,UAEK,sBAAA;EACf,SAAA;EACA,IAAA,EAAM,WAAA;EACN,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,UAGI,iBAAA;EACf,SAAA;EACA,SAAA,EAAW,MAAA;EACX,IAAA,EAAM,WAAA,SAAoB,MAAA;EAC1B,MAAA;EACA,QAAA,GAAW,WAAA;AAAA;AAAA,iBAeG,uBAAA,CAAwB,KAAA,EAAO,sBAAA,GAAyB,iBAAA;;;KC5C5D,aAAA,GAAgB,UAAA;EAAe,GAAA;EAAa,GAAA;AAAA;AAAA,cAE3C,WAAA,YAAuB,aAAA,CAAc,aAAA;EAAA,iBAGnB,SAAA;EAAA,SAFpB,IAAA;cAEoB,SAAA,EAAW,iBAAA;EAExC,WAAA,CACE,OAAA,UACA,MAAA,EAAQ,cAAA,EACR,OAAA;IAAY,OAAA,GAAU,aAAA;EAAA;EAcxB,aAAA,CAAc,OAAA,EAAS,aAAA;EAIvB,WAAA,CAAY,KAAA,WAAgB,aAAA;EAAA,QAkCpB,WAAA;AAAA"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { randomUUID } from "node:crypto";
2
- import { decode, sign, verify } from "jsonwebtoken";
1
+ import { KeyObject, randomUUID } from "node:crypto";
2
+ import jwt from "jsonwebtoken";
3
3
 
4
4
  //#region src/jwt-config.ts
5
5
  const supportedAlgorithms = new Set([
@@ -47,11 +47,12 @@ function normalizeJwtKey(input) {
47
47
  };
48
48
  }
49
49
  function assertStrongHmacSecret(kid, secret) {
50
- if ((typeof secret === "string" ? Buffer.byteLength(secret) : Buffer.isBuffer(secret) ? secret.byteLength : 0) < 32) throw new Error(`JWT key "${kid}" uses a weak HMAC secret; use at least 32 bytes`);
50
+ if ((typeof secret === "string" ? Buffer.byteLength(secret) : Buffer.isBuffer(secret) ? secret.byteLength : secret instanceof KeyObject && secret.type === "secret" ? secret.symmetricKeySize : 0) < 32) throw new Error(`JWT key "${kid}" uses a weak HMAC secret; use at least 32 bytes`);
51
51
  }
52
52
 
53
53
  //#endregion
54
54
  //#region src/jwt-strategy.ts
55
+ const { decode, sign, verify } = jwt;
55
56
  var JwtStrategy = class {
56
57
  constructor(jwtConfig) {
57
58
  this.jwtConfig = jwtConfig;
@@ -98,7 +99,7 @@ var JwtStrategy = class {
98
99
  }
99
100
  };
100
101
  function resolveExpiresIn(timeout) {
101
- if (typeof timeout === "number" && timeout <= 0) return;
102
+ if (timeout === -1) return;
102
103
  return timeout;
103
104
  }
104
105
  function signingKeyFor(key) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/jwt-config.ts","../src/jwt-strategy.ts"],"sourcesContent":["import type { Secret, SignOptions } from \"jsonwebtoken\";\n\nexport type JwtAlgorithm = Extract<\n SignOptions[\"algorithm\"],\n \"HS256\" | \"HS384\" | \"HS512\" | \"RS256\" | \"RS384\" | \"RS512\" | \"ES256\" | \"ES384\" | \"ES512\"\n>;\n\nexport interface JwtKeyInput {\n kid: string;\n algorithm: JwtAlgorithm;\n secret?: Secret;\n signingKey?: Secret;\n verificationKey?: Secret;\n}\n\nexport interface JwtKey {\n kid: string;\n algorithm: JwtAlgorithm;\n signingKey: Secret;\n verificationKey: Secret;\n}\n\nexport type JwtAudience = string | [string, ...string[]];\n\nexport interface JwtStrategyConfigInput {\n activeKid: string;\n keys: JwtKeyInput[];\n issuer?: string;\n audience?: JwtAudience;\n}\n\nexport interface JwtStrategyConfig {\n activeKid: string;\n activeKey: JwtKey;\n keys: ReadonlyMap<string, JwtKey>;\n issuer?: string;\n audience?: JwtAudience;\n}\n\nconst supportedAlgorithms = new Set<JwtAlgorithm>([\n \"HS256\",\n \"HS384\",\n \"HS512\",\n \"RS256\",\n \"RS384\",\n \"RS512\",\n \"ES256\",\n \"ES384\",\n \"ES512\",\n]);\n\nexport function createJwtStrategyConfig(input: JwtStrategyConfigInput): JwtStrategyConfig {\n if (!input.activeKid) {\n throw new Error(\"JWT activeKid is required\");\n }\n if (!input.keys.length) {\n throw new Error(\"JWT keys must not be empty\");\n }\n\n const keys = new Map<string, JwtKey>();\n for (const keyInput of input.keys) {\n const key = normalizeJwtKey(keyInput);\n if (keys.has(key.kid)) {\n throw new Error(`JWT key kid \"${key.kid}\" is duplicated`);\n }\n keys.set(key.kid, key);\n }\n\n const activeKey = keys.get(input.activeKid);\n if (!activeKey) {\n throw new Error(`JWT activeKid \"${input.activeKid}\" does not match any configured key`);\n }\n\n return {\n activeKid: input.activeKid,\n activeKey,\n keys,\n issuer: input.issuer,\n audience: input.audience,\n };\n}\n\nfunction normalizeJwtKey(input: JwtKeyInput): JwtKey {\n if (!input.kid) {\n throw new Error(\"JWT key kid is required\");\n }\n if (!supportedAlgorithms.has(input.algorithm)) {\n throw new Error(`JWT algorithm \"${input.algorithm}\" is not supported`);\n }\n\n const signingKey = input.signingKey ?? input.secret;\n const verificationKey = input.verificationKey ?? input.secret ?? input.signingKey;\n if (!signingKey || !verificationKey) {\n throw new Error(`JWT key \"${input.kid}\" requires signing and verification keys`);\n }\n if (input.algorithm.startsWith(\"HS\")) {\n assertStrongHmacSecret(input.kid, signingKey);\n }\n\n return {\n kid: input.kid,\n algorithm: input.algorithm,\n signingKey,\n verificationKey,\n };\n}\n\nfunction assertStrongHmacSecret(kid: string, secret: Secret): void {\n const length =\n typeof secret === \"string\"\n ? Buffer.byteLength(secret)\n : Buffer.isBuffer(secret)\n ? secret.byteLength\n : 0;\n\n if (length < 32) {\n throw new Error(`JWT key \"${kid}\" uses a weak HMAC secret; use at least 32 bytes`);\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { decode, sign, verify } from \"jsonwebtoken\";\nimport type { JwtPayload, SignOptions } from \"jsonwebtoken\";\nimport type { DurationInput, TokenStrategy, XltTokenConfig } from \"@xlt-token/core\";\nimport type { JwtKey, JwtStrategyConfig } from \"./jwt-config.js\";\n\nexport type XltJwtPayload = JwtPayload & { sub: string; jti: string };\n\nexport class JwtStrategy implements TokenStrategy<XltJwtPayload> {\n readonly kind = \"jwt\" as const;\n\n constructor(private readonly jwtConfig: JwtStrategyConfig) {}\n\n createToken(\n loginId: string,\n config: XltTokenConfig,\n options?: { timeout?: DurationInput },\n ): string {\n const jti = randomUUID();\n const expiresIn = resolveExpiresIn(options?.timeout ?? config.timeout);\n\n return this.signPayload(\n {\n sub: loginId,\n jti,\n },\n expiresIn,\n );\n }\n\n generateToken(payload: XltJwtPayload): string {\n return this.signPayload(payload);\n }\n\n verifyToken(token: string): XltJwtPayload {\n const decoded = decode(token, { complete: true });\n if (!decoded || typeof decoded === \"string\") {\n throw new Error(\"JWT token is malformed\");\n }\n\n const { kid, alg } = decoded.header;\n if (!kid) {\n throw new Error(\"JWT token header is missing kid\");\n }\n\n const key = this.jwtConfig.keys.get(kid);\n if (!key) {\n throw new Error(`JWT key \"${kid}\" is not configured`);\n }\n if (alg !== key.algorithm) {\n throw new Error(`JWT algorithm \"${String(alg)}\" does not match configured key algorithm`);\n }\n\n const payload = verify(token, key.verificationKey, {\n algorithms: [key.algorithm],\n ...(this.jwtConfig.issuer && { issuer: this.jwtConfig.issuer }),\n ...(this.jwtConfig.audience && { audience: this.jwtConfig.audience }),\n });\n if (typeof payload === \"string\") {\n throw new Error(\"JWT payload must be an object\");\n }\n if (typeof payload.sub !== \"string\" || typeof payload.jti !== \"string\") {\n throw new Error(\"JWT payload requires string sub and jti claims\");\n }\n\n return payload as XltJwtPayload;\n }\n\n private signPayload(payload: object, expiresIn?: DurationInput): string {\n const key = this.jwtConfig.activeKey;\n const options: SignOptions = {\n algorithm: key.algorithm,\n keyid: key.kid,\n ...(this.jwtConfig.issuer && { issuer: this.jwtConfig.issuer }),\n ...(this.jwtConfig.audience && { audience: this.jwtConfig.audience }),\n ...(expiresIn !== undefined && { expiresIn }),\n };\n\n return sign(payload, signingKeyFor(key), options);\n }\n}\n\nfunction resolveExpiresIn(timeout: DurationInput): DurationInput | undefined {\n if (typeof timeout === \"number\" && timeout <= 0) {\n return undefined;\n }\n return timeout;\n}\n\nfunction signingKeyFor(key: JwtKey) {\n return key.signingKey;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/jwt-config.ts","../src/jwt-strategy.ts"],"sourcesContent":["import type { Secret, SignOptions } from \"jsonwebtoken\";\nimport { KeyObject } from \"node:crypto\";\n\nexport type JwtAlgorithm = Extract<\n SignOptions[\"algorithm\"],\n \"HS256\" | \"HS384\" | \"HS512\" | \"RS256\" | \"RS384\" | \"RS512\" | \"ES256\" | \"ES384\" | \"ES512\"\n>;\n\nexport interface JwtKeyInput {\n kid: string;\n algorithm: JwtAlgorithm;\n secret?: Secret;\n signingKey?: Secret;\n verificationKey?: Secret;\n}\n\nexport interface JwtKey {\n kid: string;\n algorithm: JwtAlgorithm;\n signingKey: Secret;\n verificationKey: Secret;\n}\n\nexport type JwtAudience = string | [string, ...string[]];\n\nexport interface JwtStrategyConfigInput {\n activeKid: string;\n keys: JwtKeyInput[];\n issuer?: string;\n audience?: JwtAudience;\n}\n\nexport interface JwtStrategyConfig {\n activeKid: string;\n activeKey: JwtKey;\n keys: ReadonlyMap<string, JwtKey>;\n issuer?: string;\n audience?: JwtAudience;\n}\n\nconst supportedAlgorithms = new Set<JwtAlgorithm>([\n \"HS256\",\n \"HS384\",\n \"HS512\",\n \"RS256\",\n \"RS384\",\n \"RS512\",\n \"ES256\",\n \"ES384\",\n \"ES512\",\n]);\n\nexport function createJwtStrategyConfig(input: JwtStrategyConfigInput): JwtStrategyConfig {\n if (!input.activeKid) {\n throw new Error(\"JWT activeKid is required\");\n }\n if (!input.keys.length) {\n throw new Error(\"JWT keys must not be empty\");\n }\n\n const keys = new Map<string, JwtKey>();\n for (const keyInput of input.keys) {\n const key = normalizeJwtKey(keyInput);\n if (keys.has(key.kid)) {\n throw new Error(`JWT key kid \"${key.kid}\" is duplicated`);\n }\n keys.set(key.kid, key);\n }\n\n const activeKey = keys.get(input.activeKid);\n if (!activeKey) {\n throw new Error(`JWT activeKid \"${input.activeKid}\" does not match any configured key`);\n }\n\n return {\n activeKid: input.activeKid,\n activeKey,\n keys,\n issuer: input.issuer,\n audience: input.audience,\n };\n}\n\nfunction normalizeJwtKey(input: JwtKeyInput): JwtKey {\n if (!input.kid) {\n throw new Error(\"JWT key kid is required\");\n }\n if (!supportedAlgorithms.has(input.algorithm)) {\n throw new Error(`JWT algorithm \"${input.algorithm}\" is not supported`);\n }\n\n const signingKey = input.signingKey ?? input.secret;\n const verificationKey = input.verificationKey ?? input.secret ?? input.signingKey;\n if (!signingKey || !verificationKey) {\n throw new Error(`JWT key \"${input.kid}\" requires signing and verification keys`);\n }\n if (input.algorithm.startsWith(\"HS\")) {\n assertStrongHmacSecret(input.kid, signingKey);\n }\n\n return {\n kid: input.kid,\n algorithm: input.algorithm,\n signingKey,\n verificationKey,\n };\n}\n\nfunction assertStrongHmacSecret(kid: string, secret: Secret): void {\n const length =\n typeof secret === \"string\"\n ? Buffer.byteLength(secret)\n : Buffer.isBuffer(secret)\n ? secret.byteLength\n : secret instanceof KeyObject && secret.type === \"secret\" ? secret.symmetricKeySize : 0;\n\n if (length < 32) {\n throw new Error(`JWT key \"${kid}\" uses a weak HMAC secret; use at least 32 bytes`);\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport jwt from \"jsonwebtoken\";\nimport type { JwtPayload, SignOptions } from \"jsonwebtoken\";\nimport type { DurationInput, TokenStrategy, XltTokenConfig } from \"@xlt-token/core\";\nimport type { JwtKey, JwtStrategyConfig } from \"./jwt-config.js\";\n\nconst { decode, sign, verify } = jwt;\n\nexport type XltJwtPayload = JwtPayload & { sub: string; jti: string };\n\nexport class JwtStrategy implements TokenStrategy<XltJwtPayload> {\n readonly kind = \"jwt\" as const;\n\n constructor(private readonly jwtConfig: JwtStrategyConfig) {}\n\n createToken(\n loginId: string,\n config: XltTokenConfig,\n options?: { timeout?: DurationInput },\n ): string {\n const jti = randomUUID();\n const expiresIn = resolveExpiresIn(options?.timeout ?? config.timeout);\n\n return this.signPayload(\n {\n sub: loginId,\n jti,\n },\n expiresIn,\n );\n }\n\n generateToken(payload: XltJwtPayload): string {\n return this.signPayload(payload);\n }\n\n verifyToken(token: string): XltJwtPayload {\n const decoded = decode(token, { complete: true });\n if (!decoded || typeof decoded === \"string\") {\n throw new Error(\"JWT token is malformed\");\n }\n\n const { kid, alg } = decoded.header;\n if (!kid) {\n throw new Error(\"JWT token header is missing kid\");\n }\n\n const key = this.jwtConfig.keys.get(kid);\n if (!key) {\n throw new Error(`JWT key \"${kid}\" is not configured`);\n }\n if (alg !== key.algorithm) {\n throw new Error(`JWT algorithm \"${String(alg)}\" does not match configured key algorithm`);\n }\n\n const payload = verify(token, key.verificationKey, {\n algorithms: [key.algorithm],\n ...(this.jwtConfig.issuer && { issuer: this.jwtConfig.issuer }),\n ...(this.jwtConfig.audience && { audience: this.jwtConfig.audience }),\n });\n if (typeof payload === \"string\") {\n throw new Error(\"JWT payload must be an object\");\n }\n if (typeof payload.sub !== \"string\" || typeof payload.jti !== \"string\") {\n throw new Error(\"JWT payload requires string sub and jti claims\");\n }\n\n return payload as XltJwtPayload;\n }\n\n private signPayload(payload: object, expiresIn?: DurationInput): string {\n const key = this.jwtConfig.activeKey;\n const options: SignOptions = {\n algorithm: key.algorithm,\n keyid: key.kid,\n ...(this.jwtConfig.issuer && { issuer: this.jwtConfig.issuer }),\n ...(this.jwtConfig.audience && { audience: this.jwtConfig.audience }),\n ...(expiresIn !== undefined && { expiresIn }),\n };\n\n return sign(payload, signingKeyFor(key), options);\n }\n}\n\nfunction resolveExpiresIn(timeout: DurationInput): DurationInput | undefined {\n if (timeout === -1) {\n return undefined;\n }\n return timeout;\n}\n\nfunction signingKeyFor(key: JwtKey) {\n return key.signingKey;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xlt-token/jwt",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "JWT token strategy with key rotation for xlt-token",
5
5
  "keywords": [
6
6
  "auth",
@@ -46,7 +46,7 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@xlt-token/core": "^2.1.0",
49
+ "@xlt-token/core": "^2.3.0",
50
50
  "jsonwebtoken": "^9.0.3"
51
51
  },
52
52
  "devDependencies": {