hi-secure 1.0.32 → 1.0.34
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JWTAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"JWTAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":"AA6GA,OAAO,GAAsC,MAAM,cAAc,CAAC;AAOlE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC9B;AAWD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,CAAoB;gBAEzB,OAAO,EAAE,iBAAiB;IAoBtC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW;IA4C3C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;KAAE;CAgCjE"}
|
|
@@ -1,13 +1,111 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// import jwt from "jsonwebtoken";
|
|
3
|
+
// import { randomUUID } from "crypto";
|
|
4
|
+
// import { AdapterError } from "../core/errors/AdapterError";
|
|
5
|
+
// import { logger } from "../logging";
|
|
2
6
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
7
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
8
|
};
|
|
5
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
10
|
exports.JWTAdapter = void 0;
|
|
11
|
+
// export interface JWTAdapterOptions {
|
|
12
|
+
// secret: string;
|
|
13
|
+
// expiresIn?: string | number;
|
|
14
|
+
// algorithm?: jwt.Algorithm;
|
|
15
|
+
// issuer?: string;
|
|
16
|
+
// audience?: string | string[];
|
|
17
|
+
// }
|
|
18
|
+
// export interface SignOptions {
|
|
19
|
+
// expiresIn?: string | number;
|
|
20
|
+
// jti?: string;
|
|
21
|
+
// subject?: string;
|
|
22
|
+
// issuer?: string;
|
|
23
|
+
// audience?: string | string[];
|
|
24
|
+
// }
|
|
25
|
+
// export class JWTAdapter {
|
|
26
|
+
// private secret: string;
|
|
27
|
+
// private expiresIn?: string | number;
|
|
28
|
+
// private algorithm: jwt.Algorithm;
|
|
29
|
+
// private issuer?: string;
|
|
30
|
+
// private audience?: string | string[];
|
|
31
|
+
// constructor(options: JWTAdapterOptions) {
|
|
32
|
+
// if (!options.secret) {
|
|
33
|
+
// throw new AdapterError("JWT secret is required");
|
|
34
|
+
// }
|
|
35
|
+
// if (options.secret.length < 32) {
|
|
36
|
+
// logger.warn("Weak JWT secret detected", {
|
|
37
|
+
// adapter: "jwt",
|
|
38
|
+
// operation: "init",
|
|
39
|
+
// secretLength: options.secret.length
|
|
40
|
+
// });
|
|
41
|
+
// }
|
|
42
|
+
// this.secret = options.secret;
|
|
43
|
+
// this.expiresIn = options.expiresIn;
|
|
44
|
+
// this.algorithm = options.algorithm || "HS256";
|
|
45
|
+
// this.issuer = options.issuer;
|
|
46
|
+
// this.audience = options.audience;
|
|
47
|
+
// }
|
|
48
|
+
// sign(payload: object, options?: SignOptions) {
|
|
49
|
+
// try {
|
|
50
|
+
// const jwtOptions: jwt.SignOptions = {
|
|
51
|
+
// algorithm: this.algorithm,
|
|
52
|
+
// issuer: options?.issuer || this.issuer,
|
|
53
|
+
// audience: options?.audience || this.audience,
|
|
54
|
+
// jwtid: options?.jti || randomUUID(),
|
|
55
|
+
// subject: options?.subject
|
|
56
|
+
// };
|
|
57
|
+
// if (options?.expiresIn !== undefined) {
|
|
58
|
+
// jwtOptions.expiresIn = options.expiresIn as any;
|
|
59
|
+
// } else if (this.expiresIn !== undefined) {
|
|
60
|
+
// jwtOptions.expiresIn = this.expiresIn as any;
|
|
61
|
+
// }
|
|
62
|
+
// return jwt.sign(payload, this.secret, jwtOptions);
|
|
63
|
+
// } catch (err: any) {
|
|
64
|
+
// logger.error("JWT signing failed", {
|
|
65
|
+
// adapter: "jwt",
|
|
66
|
+
// operation: "sign",
|
|
67
|
+
// reason: err?.message
|
|
68
|
+
// });
|
|
69
|
+
// throw new AdapterError("JWT sign failed");
|
|
70
|
+
// }
|
|
71
|
+
// }
|
|
72
|
+
// verify(token: string, options?: { audience?: string | string[] }) {
|
|
73
|
+
// try {
|
|
74
|
+
// const verifyOptions: jwt.VerifyOptions = {
|
|
75
|
+
// algorithms: [this.algorithm],
|
|
76
|
+
// issuer: this.issuer,
|
|
77
|
+
// audience: (options?.audience || this.audience) as string
|
|
78
|
+
// };
|
|
79
|
+
// return jwt.verify(token, this.secret, verifyOptions);
|
|
80
|
+
// } catch (err: any) {
|
|
81
|
+
// logger.error("JWT verification failed", {
|
|
82
|
+
// adapter: "jwt",
|
|
83
|
+
// operation: "verify",
|
|
84
|
+
// reason: err?.message
|
|
85
|
+
// });
|
|
86
|
+
// if (err?.name === "TokenExpiredError") {
|
|
87
|
+
// throw new AdapterError("JWT token has expired");
|
|
88
|
+
// }
|
|
89
|
+
// if (err?.name === "JsonWebTokenError") {
|
|
90
|
+
// throw new AdapterError("Invalid JWT token");
|
|
91
|
+
// }
|
|
92
|
+
// throw new AdapterError("JWT verification failed");
|
|
93
|
+
// }
|
|
94
|
+
// }
|
|
95
|
+
// }
|
|
7
96
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
97
|
const crypto_1 = require("crypto");
|
|
9
98
|
const AdapterError_1 = require("../core/errors/AdapterError");
|
|
10
99
|
const logging_1 = require("../logging");
|
|
100
|
+
function normalizeAudience(aud) {
|
|
101
|
+
if (!aud)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (typeof aud === "string")
|
|
104
|
+
return aud;
|
|
105
|
+
if (aud.length > 0)
|
|
106
|
+
return aud;
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
11
109
|
class JWTAdapter {
|
|
12
110
|
constructor(options) {
|
|
13
111
|
if (!options.secret) {
|
|
@@ -16,30 +114,41 @@ class JWTAdapter {
|
|
|
16
114
|
if (options.secret.length < 32) {
|
|
17
115
|
logging_1.logger.warn("Weak JWT secret detected", {
|
|
18
116
|
adapter: "jwt",
|
|
19
|
-
operation: "init",
|
|
20
117
|
secretLength: options.secret.length
|
|
21
118
|
});
|
|
22
119
|
}
|
|
23
120
|
this.secret = options.secret;
|
|
24
|
-
this.
|
|
25
|
-
this.algorithm = options.algorithm || "HS256";
|
|
121
|
+
this.algorithm = options.algorithm ?? "HS256";
|
|
26
122
|
this.issuer = options.issuer;
|
|
27
123
|
this.audience = options.audience;
|
|
124
|
+
this.expiresIn = options.expiresIn;
|
|
28
125
|
}
|
|
126
|
+
// ================= SIGN =================
|
|
29
127
|
sign(payload, options) {
|
|
30
128
|
try {
|
|
31
129
|
const jwtOptions = {
|
|
32
130
|
algorithm: this.algorithm,
|
|
33
|
-
|
|
34
|
-
audience: options?.audience || this.audience,
|
|
35
|
-
jwtid: options?.jti || (0, crypto_1.randomUUID)(),
|
|
36
|
-
subject: options?.subject
|
|
131
|
+
jwtid: options?.jti ?? (0, crypto_1.randomUUID)()
|
|
37
132
|
};
|
|
38
|
-
|
|
39
|
-
|
|
133
|
+
// ✅ subject ONLY if string
|
|
134
|
+
if (typeof options?.subject === "string") {
|
|
135
|
+
jwtOptions.subject = options.subject;
|
|
40
136
|
}
|
|
41
|
-
|
|
42
|
-
|
|
137
|
+
// ✅ issuer
|
|
138
|
+
const issuer = options?.issuer ?? this.issuer;
|
|
139
|
+
if (typeof issuer === "string") {
|
|
140
|
+
jwtOptions.issuer = issuer;
|
|
141
|
+
}
|
|
142
|
+
// ✅ audience
|
|
143
|
+
const audience = normalizeAudience(options?.audience ?? this.audience);
|
|
144
|
+
if (audience)
|
|
145
|
+
jwtOptions.audience = audience;
|
|
146
|
+
// ✅ expiresIn
|
|
147
|
+
const expires = options?.expiresIn !== undefined
|
|
148
|
+
? options.expiresIn
|
|
149
|
+
: this.expiresIn;
|
|
150
|
+
if (expires !== undefined) {
|
|
151
|
+
jwtOptions.expiresIn = expires;
|
|
43
152
|
}
|
|
44
153
|
return jsonwebtoken_1.default.sign(payload, this.secret, jwtOptions);
|
|
45
154
|
}
|
|
@@ -52,13 +161,18 @@ class JWTAdapter {
|
|
|
52
161
|
throw new AdapterError_1.AdapterError("JWT sign failed");
|
|
53
162
|
}
|
|
54
163
|
}
|
|
164
|
+
// ================= VERIFY =================
|
|
55
165
|
verify(token, options) {
|
|
56
166
|
try {
|
|
57
167
|
const verifyOptions = {
|
|
58
|
-
algorithms: [this.algorithm]
|
|
59
|
-
issuer: this.issuer,
|
|
60
|
-
audience: (options?.audience || this.audience)
|
|
168
|
+
algorithms: [this.algorithm]
|
|
61
169
|
};
|
|
170
|
+
if (typeof this.issuer === "string") {
|
|
171
|
+
verifyOptions.issuer = this.issuer;
|
|
172
|
+
}
|
|
173
|
+
const audience = normalizeAudience(options?.audience ?? this.audience);
|
|
174
|
+
if (audience)
|
|
175
|
+
verifyOptions.audience = audience;
|
|
62
176
|
return jsonwebtoken_1.default.verify(token, this.secret, verifyOptions);
|
|
63
177
|
}
|
|
64
178
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JWTAdapter.js","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt from \"jsonwebtoken\";\r\nimport { randomUUID } from \"crypto\";\r\nimport { AdapterError } from \"../core/errors/AdapterError\";\r\nimport { logger } from \"../logging\";\r\n\r\nexport interface JWTAdapterOptions {\r\n secret: string;\r\n expiresIn?: string | number;\r\n algorithm?: jwt.Algorithm;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nexport interface SignOptions {\r\n expiresIn?: string | number;\r\n jti?: string;\r\n subject?: string;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nexport class JWTAdapter {\r\n private secret: string;\r\n private expiresIn?: string | number;\r\n private algorithm: jwt.Algorithm;\r\n private issuer?: string;\r\n private audience?: string | string[];\r\n\r\n constructor(options: JWTAdapterOptions) {\r\n if (!options.secret) {\r\n throw new AdapterError(\"JWT secret is required\");\r\n }\r\n\r\n if (options.secret.length < 32) {\r\n logger.warn(\"Weak JWT secret detected\", {\r\n adapter: \"jwt\",\r\n operation: \"init\",\r\n secretLength: options.secret.length\r\n });\r\n }\r\n\r\n this.secret = options.secret;\r\n this.expiresIn = options.expiresIn;\r\n this.algorithm = options.algorithm || \"HS256\";\r\n this.issuer = options.issuer;\r\n this.audience = options.audience;\r\n }\r\n\r\n sign(payload: object, options?: SignOptions) {\r\n try {\r\n const jwtOptions: jwt.SignOptions = {\r\n algorithm: this.algorithm,\r\n issuer: options?.issuer || this.issuer,\r\n audience: options?.audience || this.audience,\r\n jwtid: options?.jti || randomUUID(),\r\n subject: options?.subject\r\n };\r\n\r\n if (options?.expiresIn !== undefined) {\r\n jwtOptions.expiresIn = options.expiresIn as any;\r\n } else if (this.expiresIn !== undefined) {\r\n jwtOptions.expiresIn = this.expiresIn as any;\r\n }\r\n\r\n return jwt.sign(payload, this.secret, jwtOptions);\r\n\r\n } catch (err: any) {\r\n logger.error(\"JWT signing failed\", {\r\n adapter: \"jwt\",\r\n operation: \"sign\",\r\n reason: err?.message\r\n });\r\n\r\n throw new AdapterError(\"JWT sign failed\");\r\n }\r\n }\r\n\r\n verify(token: string, options?: { audience?: string | string[] }) {\r\n try {\r\n const verifyOptions: jwt.VerifyOptions = {\r\n algorithms: [this.algorithm],\r\n issuer: this.issuer,\r\n audience: (options?.audience || this.audience) as string\r\n };\r\n\r\n return jwt.verify(token, this.secret, verifyOptions);\r\n\r\n } catch (err: any) {\r\n logger.error(\"JWT verification failed\", {\r\n adapter: \"jwt\",\r\n operation: \"verify\",\r\n reason: err?.message\r\n });\r\n\r\n if (err?.name === \"TokenExpiredError\") {\r\n throw new AdapterError(\"JWT token has expired\");\r\n }\r\n\r\n if (err?.name === \"JsonWebTokenError\") {\r\n throw new AdapterError(\"Invalid JWT token\");\r\n }\r\n\r\n throw new AdapterError(\"JWT verification failed\");\r\n }\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"JWTAdapter.js","sourceRoot":"","sources":["../../src/adapters/JWTAdapter.ts"],"names":[],"mappings":";AAAA,kCAAkC;AAClC,uCAAuC;AACvC,8DAA8D;AAC9D,uCAAuC;;;;;;AAEvC,uCAAuC;AACvC,sBAAsB;AACtB,mCAAmC;AACnC,iCAAiC;AACjC,uBAAuB;AACvB,oCAAoC;AACpC,IAAI;AAEJ,iCAAiC;AACjC,mCAAmC;AACnC,oBAAoB;AACpB,wBAAwB;AACxB,uBAAuB;AACvB,oCAAoC;AACpC,IAAI;AAEJ,4BAA4B;AAC5B,8BAA8B;AAC9B,2CAA2C;AAC3C,wCAAwC;AACxC,+BAA+B;AAC/B,4CAA4C;AAE5C,gDAAgD;AAChD,iCAAiC;AACjC,gEAAgE;AAChE,YAAY;AAEZ,4CAA4C;AAC5C,wDAAwD;AACxD,kCAAkC;AAClC,qCAAqC;AACrC,sDAAsD;AACtD,kBAAkB;AAClB,YAAY;AAEZ,wCAAwC;AACxC,8CAA8C;AAC9C,yDAAyD;AACzD,wCAAwC;AACxC,4CAA4C;AAC5C,QAAQ;AAER,qDAAqD;AACrD,gBAAgB;AAChB,oDAAoD;AACpD,6CAA6C;AAC7C,0DAA0D;AAC1D,gEAAgE;AAChE,uDAAuD;AACvD,4CAA4C;AAC5C,iBAAiB;AAEjB,sDAAsD;AACtD,mEAAmE;AACnE,yDAAyD;AACzD,gEAAgE;AAChE,gBAAgB;AAEhB,iEAAiE;AAEjE,+BAA+B;AAC/B,mDAAmD;AACnD,kCAAkC;AAClC,qCAAqC;AACrC,uCAAuC;AACvC,kBAAkB;AAElB,yDAAyD;AACzD,YAAY;AACZ,QAAQ;AAER,0EAA0E;AAC1E,gBAAgB;AAChB,yDAAyD;AACzD,gDAAgD;AAChD,uCAAuC;AACvC,2EAA2E;AAC3E,iBAAiB;AAEjB,oEAAoE;AAEpE,+BAA+B;AAC/B,wDAAwD;AACxD,kCAAkC;AAClC,uCAAuC;AACvC,uCAAuC;AACvC,kBAAkB;AAElB,uDAAuD;AACvD,mEAAmE;AACnE,gBAAgB;AAEhB,uDAAuD;AACvD,+DAA+D;AAC/D,gBAAgB;AAEhB,iEAAiE;AACjE,YAAY;AACZ,QAAQ;AACR,IAAI;AAIJ,gEAAkE;AAClE,mCAAoC;AACpC,8DAA2D;AAC3D,wCAAoC;AAoBpC,SAAS,iBAAiB,CACxB,GAAuB;IAEvB,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAA4B,CAAC;IACxD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAa,UAAU;IAOrB,YAAY,OAA0B;QACpC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,2BAAY,CAAC,wBAAwB,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC/B,gBAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;gBACtC,OAAO,EAAE,KAAK;gBACd,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;aACpC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAsB,CAAC;IAClD,CAAC;IAED,2CAA2C;IAC3C,IAAI,CAAC,OAAe,EAAE,OAAqB;QACzC,IAAI,CAAC;YACH,MAAM,UAAU,GAAoB;gBAClC,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,IAAA,mBAAU,GAAE;aACpC,CAAC;YAEF,2BAA2B;YAC3B,IAAI,OAAO,OAAO,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACzC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YACvC,CAAC;YAED,WAAW;YACX,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;YAC9C,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC/B,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;YAC7B,CAAC;YAED,aAAa;YACb,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvE,IAAI,QAAQ;gBAAE,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAE7C,cAAc;YACd,MAAM,OAAO,GACX,OAAO,EAAE,SAAS,KAAK,SAAS;gBAC9B,CAAC,CAAE,OAAO,CAAC,SAAuB;gBAClC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;YAErB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,UAAU,CAAC,SAAS,GAAG,OAAO,CAAC;YACjC,CAAC;YAED,OAAO,sBAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,gBAAM,CAAC,KAAK,CAAC,oBAAoB,EAAE;gBACjC,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,MAAM;gBACjB,MAAM,EAAE,GAAG,EAAE,OAAO;aACrB,CAAC,CAAC;YACH,MAAM,IAAI,2BAAY,CAAC,iBAAiB,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,MAAM,CAAC,KAAa,EAAE,OAA0C;QAC9D,IAAI,CAAC;YACH,MAAM,aAAa,GAAsB;gBACvC,UAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;aAC7B,CAAC;YAEF,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACpC,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YACrC,CAAC;YAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvE,IAAI,QAAQ;gBAAE,aAAa,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAEhD,OAAO,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,gBAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE;gBACtC,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,QAAQ;gBACnB,MAAM,EAAE,GAAG,EAAE,OAAO;aACrB,CAAC,CAAC;YAEH,IAAI,GAAG,EAAE,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBACtC,MAAM,IAAI,2BAAY,CAAC,uBAAuB,CAAC,CAAC;YAClD,CAAC;YAED,IAAI,GAAG,EAAE,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBACtC,MAAM,IAAI,2BAAY,CAAC,mBAAmB,CAAC,CAAC;YAC9C,CAAC;YAED,MAAM,IAAI,2BAAY,CAAC,yBAAyB,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;CACF;AAvGD,gCAuGC","sourcesContent":["// import jwt from \"jsonwebtoken\";\r\n// import { randomUUID } from \"crypto\";\r\n// import { AdapterError } from \"../core/errors/AdapterError\";\r\n// import { logger } from \"../logging\";\r\n\r\n// export interface JWTAdapterOptions {\r\n// secret: string;\r\n// expiresIn?: string | number;\r\n// algorithm?: jwt.Algorithm;\r\n// issuer?: string;\r\n// audience?: string | string[];\r\n// }\r\n\r\n// export interface SignOptions {\r\n// expiresIn?: string | number;\r\n// jti?: string;\r\n// subject?: string;\r\n// issuer?: string;\r\n// audience?: string | string[];\r\n// }\r\n\r\n// export class JWTAdapter {\r\n// private secret: string;\r\n// private expiresIn?: string | number;\r\n// private algorithm: jwt.Algorithm;\r\n// private issuer?: string;\r\n// private audience?: string | string[];\r\n\r\n// constructor(options: JWTAdapterOptions) {\r\n// if (!options.secret) {\r\n// throw new AdapterError(\"JWT secret is required\");\r\n// }\r\n\r\n// if (options.secret.length < 32) {\r\n// logger.warn(\"Weak JWT secret detected\", {\r\n// adapter: \"jwt\",\r\n// operation: \"init\",\r\n// secretLength: options.secret.length\r\n// });\r\n// }\r\n\r\n// this.secret = options.secret;\r\n// this.expiresIn = options.expiresIn;\r\n// this.algorithm = options.algorithm || \"HS256\";\r\n// this.issuer = options.issuer;\r\n// this.audience = options.audience;\r\n// }\r\n\r\n// sign(payload: object, options?: SignOptions) {\r\n// try {\r\n// const jwtOptions: jwt.SignOptions = {\r\n// algorithm: this.algorithm,\r\n// issuer: options?.issuer || this.issuer,\r\n// audience: options?.audience || this.audience,\r\n// jwtid: options?.jti || randomUUID(),\r\n// subject: options?.subject\r\n// };\r\n\r\n// if (options?.expiresIn !== undefined) {\r\n// jwtOptions.expiresIn = options.expiresIn as any;\r\n// } else if (this.expiresIn !== undefined) {\r\n// jwtOptions.expiresIn = this.expiresIn as any;\r\n// }\r\n\r\n// return jwt.sign(payload, this.secret, jwtOptions);\r\n\r\n// } catch (err: any) {\r\n// logger.error(\"JWT signing failed\", {\r\n// adapter: \"jwt\",\r\n// operation: \"sign\",\r\n// reason: err?.message\r\n// });\r\n\r\n// throw new AdapterError(\"JWT sign failed\");\r\n// }\r\n// }\r\n\r\n// verify(token: string, options?: { audience?: string | string[] }) {\r\n// try {\r\n// const verifyOptions: jwt.VerifyOptions = {\r\n// algorithms: [this.algorithm],\r\n// issuer: this.issuer,\r\n// audience: (options?.audience || this.audience) as string\r\n// };\r\n\r\n// return jwt.verify(token, this.secret, verifyOptions);\r\n\r\n// } catch (err: any) {\r\n// logger.error(\"JWT verification failed\", {\r\n// adapter: \"jwt\",\r\n// operation: \"verify\",\r\n// reason: err?.message\r\n// });\r\n\r\n// if (err?.name === \"TokenExpiredError\") {\r\n// throw new AdapterError(\"JWT token has expired\");\r\n// }\r\n\r\n// if (err?.name === \"JsonWebTokenError\") {\r\n// throw new AdapterError(\"Invalid JWT token\");\r\n// }\r\n\r\n// throw new AdapterError(\"JWT verification failed\");\r\n// }\r\n// }\r\n// }\r\n\r\n\r\n\r\nimport jwt, { SignOptions as JwtSignOptions } from \"jsonwebtoken\";\r\nimport { randomUUID } from \"crypto\";\r\nimport { AdapterError } from \"../core/errors/AdapterError\";\r\nimport { logger } from \"../logging\";\r\n\r\ntype ExpiresIn = JwtSignOptions[\"expiresIn\"];\r\n\r\nexport interface JWTAdapterOptions {\r\n secret: string;\r\n expiresIn?: string | number;\r\n algorithm?: jwt.Algorithm;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nexport interface SignOptions {\r\n expiresIn?: string | number;\r\n jti?: string;\r\n subject?: string;\r\n issuer?: string;\r\n audience?: string | string[];\r\n}\r\n\r\nfunction normalizeAudience(\r\n aud?: string | string[]\r\n): string | [string, ...string[]] | undefined {\r\n if (!aud) return undefined;\r\n if (typeof aud === \"string\") return aud;\r\n if (aud.length > 0) return aud as [string, ...string[]];\r\n return undefined;\r\n}\r\n\r\nexport class JWTAdapter {\r\n private secret: string;\r\n private expiresIn?: ExpiresIn;\r\n private algorithm: jwt.Algorithm;\r\n private issuer?: string;\r\n private audience?: string | string[];\r\n\r\n constructor(options: JWTAdapterOptions) {\r\n if (!options.secret) {\r\n throw new AdapterError(\"JWT secret is required\");\r\n }\r\n\r\n if (options.secret.length < 32) {\r\n logger.warn(\"Weak JWT secret detected\", {\r\n adapter: \"jwt\",\r\n secretLength: options.secret.length\r\n });\r\n }\r\n\r\n this.secret = options.secret;\r\n this.algorithm = options.algorithm ?? \"HS256\";\r\n this.issuer = options.issuer;\r\n this.audience = options.audience;\r\n this.expiresIn = options.expiresIn as ExpiresIn;\r\n }\r\n\r\n // ================= SIGN =================\r\n sign(payload: object, options?: SignOptions) {\r\n try {\r\n const jwtOptions: jwt.SignOptions = {\r\n algorithm: this.algorithm,\r\n jwtid: options?.jti ?? randomUUID()\r\n };\r\n\r\n // ✅ subject ONLY if string\r\n if (typeof options?.subject === \"string\") {\r\n jwtOptions.subject = options.subject;\r\n }\r\n\r\n // ✅ issuer\r\n const issuer = options?.issuer ?? this.issuer;\r\n if (typeof issuer === \"string\") {\r\n jwtOptions.issuer = issuer;\r\n }\r\n\r\n // ✅ audience\r\n const audience = normalizeAudience(options?.audience ?? this.audience);\r\n if (audience) jwtOptions.audience = audience;\r\n\r\n // ✅ expiresIn\r\n const expires =\r\n options?.expiresIn !== undefined\r\n ? (options.expiresIn as ExpiresIn)\r\n : this.expiresIn;\r\n\r\n if (expires !== undefined) {\r\n jwtOptions.expiresIn = expires;\r\n }\r\n\r\n return jwt.sign(payload, this.secret, jwtOptions);\r\n } catch (err: any) {\r\n logger.error(\"JWT signing failed\", {\r\n adapter: \"jwt\",\r\n operation: \"sign\",\r\n reason: err?.message\r\n });\r\n throw new AdapterError(\"JWT sign failed\");\r\n }\r\n }\r\n\r\n // ================= VERIFY =================\r\n verify(token: string, options?: { audience?: string | string[] }) {\r\n try {\r\n const verifyOptions: jwt.VerifyOptions = {\r\n algorithms: [this.algorithm]\r\n };\r\n\r\n if (typeof this.issuer === \"string\") {\r\n verifyOptions.issuer = this.issuer;\r\n }\r\n\r\n const audience = normalizeAudience(options?.audience ?? this.audience);\r\n if (audience) verifyOptions.audience = audience;\r\n\r\n return jwt.verify(token, this.secret, verifyOptions);\r\n } catch (err: any) {\r\n logger.error(\"JWT verification failed\", {\r\n adapter: \"jwt\",\r\n operation: \"verify\",\r\n reason: err?.message\r\n });\r\n\r\n if (err?.name === \"TokenExpiredError\") {\r\n throw new AdapterError(\"JWT token has expired\");\r\n }\r\n\r\n if (err?.name === \"JsonWebTokenError\") {\r\n throw new AdapterError(\"Invalid JWT token\");\r\n }\r\n\r\n throw new AdapterError(\"JWT verification failed\");\r\n }\r\n }\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,106 +1,245 @@
|
|
|
1
|
-
import jwt from "jsonwebtoken";
|
|
1
|
+
// import jwt from "jsonwebtoken";
|
|
2
|
+
// import { randomUUID } from "crypto";
|
|
3
|
+
// import { AdapterError } from "../core/errors/AdapterError";
|
|
4
|
+
// import { logger } from "../logging";
|
|
5
|
+
|
|
6
|
+
// export interface JWTAdapterOptions {
|
|
7
|
+
// secret: string;
|
|
8
|
+
// expiresIn?: string | number;
|
|
9
|
+
// algorithm?: jwt.Algorithm;
|
|
10
|
+
// issuer?: string;
|
|
11
|
+
// audience?: string | string[];
|
|
12
|
+
// }
|
|
13
|
+
|
|
14
|
+
// export interface SignOptions {
|
|
15
|
+
// expiresIn?: string | number;
|
|
16
|
+
// jti?: string;
|
|
17
|
+
// subject?: string;
|
|
18
|
+
// issuer?: string;
|
|
19
|
+
// audience?: string | string[];
|
|
20
|
+
// }
|
|
21
|
+
|
|
22
|
+
// export class JWTAdapter {
|
|
23
|
+
// private secret: string;
|
|
24
|
+
// private expiresIn?: string | number;
|
|
25
|
+
// private algorithm: jwt.Algorithm;
|
|
26
|
+
// private issuer?: string;
|
|
27
|
+
// private audience?: string | string[];
|
|
28
|
+
|
|
29
|
+
// constructor(options: JWTAdapterOptions) {
|
|
30
|
+
// if (!options.secret) {
|
|
31
|
+
// throw new AdapterError("JWT secret is required");
|
|
32
|
+
// }
|
|
33
|
+
|
|
34
|
+
// if (options.secret.length < 32) {
|
|
35
|
+
// logger.warn("Weak JWT secret detected", {
|
|
36
|
+
// adapter: "jwt",
|
|
37
|
+
// operation: "init",
|
|
38
|
+
// secretLength: options.secret.length
|
|
39
|
+
// });
|
|
40
|
+
// }
|
|
41
|
+
|
|
42
|
+
// this.secret = options.secret;
|
|
43
|
+
// this.expiresIn = options.expiresIn;
|
|
44
|
+
// this.algorithm = options.algorithm || "HS256";
|
|
45
|
+
// this.issuer = options.issuer;
|
|
46
|
+
// this.audience = options.audience;
|
|
47
|
+
// }
|
|
48
|
+
|
|
49
|
+
// sign(payload: object, options?: SignOptions) {
|
|
50
|
+
// try {
|
|
51
|
+
// const jwtOptions: jwt.SignOptions = {
|
|
52
|
+
// algorithm: this.algorithm,
|
|
53
|
+
// issuer: options?.issuer || this.issuer,
|
|
54
|
+
// audience: options?.audience || this.audience,
|
|
55
|
+
// jwtid: options?.jti || randomUUID(),
|
|
56
|
+
// subject: options?.subject
|
|
57
|
+
// };
|
|
58
|
+
|
|
59
|
+
// if (options?.expiresIn !== undefined) {
|
|
60
|
+
// jwtOptions.expiresIn = options.expiresIn as any;
|
|
61
|
+
// } else if (this.expiresIn !== undefined) {
|
|
62
|
+
// jwtOptions.expiresIn = this.expiresIn as any;
|
|
63
|
+
// }
|
|
64
|
+
|
|
65
|
+
// return jwt.sign(payload, this.secret, jwtOptions);
|
|
66
|
+
|
|
67
|
+
// } catch (err: any) {
|
|
68
|
+
// logger.error("JWT signing failed", {
|
|
69
|
+
// adapter: "jwt",
|
|
70
|
+
// operation: "sign",
|
|
71
|
+
// reason: err?.message
|
|
72
|
+
// });
|
|
73
|
+
|
|
74
|
+
// throw new AdapterError("JWT sign failed");
|
|
75
|
+
// }
|
|
76
|
+
// }
|
|
77
|
+
|
|
78
|
+
// verify(token: string, options?: { audience?: string | string[] }) {
|
|
79
|
+
// try {
|
|
80
|
+
// const verifyOptions: jwt.VerifyOptions = {
|
|
81
|
+
// algorithms: [this.algorithm],
|
|
82
|
+
// issuer: this.issuer,
|
|
83
|
+
// audience: (options?.audience || this.audience) as string
|
|
84
|
+
// };
|
|
85
|
+
|
|
86
|
+
// return jwt.verify(token, this.secret, verifyOptions);
|
|
87
|
+
|
|
88
|
+
// } catch (err: any) {
|
|
89
|
+
// logger.error("JWT verification failed", {
|
|
90
|
+
// adapter: "jwt",
|
|
91
|
+
// operation: "verify",
|
|
92
|
+
// reason: err?.message
|
|
93
|
+
// });
|
|
94
|
+
|
|
95
|
+
// if (err?.name === "TokenExpiredError") {
|
|
96
|
+
// throw new AdapterError("JWT token has expired");
|
|
97
|
+
// }
|
|
98
|
+
|
|
99
|
+
// if (err?.name === "JsonWebTokenError") {
|
|
100
|
+
// throw new AdapterError("Invalid JWT token");
|
|
101
|
+
// }
|
|
102
|
+
|
|
103
|
+
// throw new AdapterError("JWT verification failed");
|
|
104
|
+
// }
|
|
105
|
+
// }
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
import jwt, { SignOptions as JwtSignOptions } from "jsonwebtoken";
|
|
2
111
|
import { randomUUID } from "crypto";
|
|
3
112
|
import { AdapterError } from "../core/errors/AdapterError";
|
|
4
113
|
import { logger } from "../logging";
|
|
5
114
|
|
|
115
|
+
type ExpiresIn = JwtSignOptions["expiresIn"];
|
|
116
|
+
|
|
6
117
|
export interface JWTAdapterOptions {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
118
|
+
secret: string;
|
|
119
|
+
expiresIn?: string | number;
|
|
120
|
+
algorithm?: jwt.Algorithm;
|
|
121
|
+
issuer?: string;
|
|
122
|
+
audience?: string | string[];
|
|
12
123
|
}
|
|
13
124
|
|
|
14
125
|
export interface SignOptions {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
126
|
+
expiresIn?: string | number;
|
|
127
|
+
jti?: string;
|
|
128
|
+
subject?: string;
|
|
129
|
+
issuer?: string;
|
|
130
|
+
audience?: string | string[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalizeAudience(
|
|
134
|
+
aud?: string | string[]
|
|
135
|
+
): string | [string, ...string[]] | undefined {
|
|
136
|
+
if (!aud) return undefined;
|
|
137
|
+
if (typeof aud === "string") return aud;
|
|
138
|
+
if (aud.length > 0) return aud as [string, ...string[]];
|
|
139
|
+
return undefined;
|
|
20
140
|
}
|
|
21
141
|
|
|
22
142
|
export class JWTAdapter {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (options.secret.length < 32) {
|
|
35
|
-
logger.warn("Weak JWT secret detected", {
|
|
36
|
-
adapter: "jwt",
|
|
37
|
-
operation: "init",
|
|
38
|
-
secretLength: options.secret.length
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
this.secret = options.secret;
|
|
43
|
-
this.expiresIn = options.expiresIn;
|
|
44
|
-
this.algorithm = options.algorithm || "HS256";
|
|
45
|
-
this.issuer = options.issuer;
|
|
46
|
-
this.audience = options.audience;
|
|
143
|
+
private secret: string;
|
|
144
|
+
private expiresIn?: ExpiresIn;
|
|
145
|
+
private algorithm: jwt.Algorithm;
|
|
146
|
+
private issuer?: string;
|
|
147
|
+
private audience?: string | string[];
|
|
148
|
+
|
|
149
|
+
constructor(options: JWTAdapterOptions) {
|
|
150
|
+
if (!options.secret) {
|
|
151
|
+
throw new AdapterError("JWT secret is required");
|
|
47
152
|
}
|
|
48
153
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
audience: options?.audience || this.audience,
|
|
55
|
-
jwtid: options?.jti || randomUUID(),
|
|
56
|
-
subject: options?.subject
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
if (options?.expiresIn !== undefined) {
|
|
60
|
-
jwtOptions.expiresIn = options.expiresIn as any;
|
|
61
|
-
} else if (this.expiresIn !== undefined) {
|
|
62
|
-
jwtOptions.expiresIn = this.expiresIn as any;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return jwt.sign(payload, this.secret, jwtOptions);
|
|
66
|
-
|
|
67
|
-
} catch (err: any) {
|
|
68
|
-
logger.error("JWT signing failed", {
|
|
69
|
-
adapter: "jwt",
|
|
70
|
-
operation: "sign",
|
|
71
|
-
reason: err?.message
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
throw new AdapterError("JWT sign failed");
|
|
75
|
-
}
|
|
154
|
+
if (options.secret.length < 32) {
|
|
155
|
+
logger.warn("Weak JWT secret detected", {
|
|
156
|
+
adapter: "jwt",
|
|
157
|
+
secretLength: options.secret.length
|
|
158
|
+
});
|
|
76
159
|
}
|
|
77
160
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
161
|
+
this.secret = options.secret;
|
|
162
|
+
this.algorithm = options.algorithm ?? "HS256";
|
|
163
|
+
this.issuer = options.issuer;
|
|
164
|
+
this.audience = options.audience;
|
|
165
|
+
this.expiresIn = options.expiresIn as ExpiresIn;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ================= SIGN =================
|
|
169
|
+
sign(payload: object, options?: SignOptions) {
|
|
170
|
+
try {
|
|
171
|
+
const jwtOptions: jwt.SignOptions = {
|
|
172
|
+
algorithm: this.algorithm,
|
|
173
|
+
jwtid: options?.jti ?? randomUUID()
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// ✅ subject ONLY if string
|
|
177
|
+
if (typeof options?.subject === "string") {
|
|
178
|
+
jwtOptions.subject = options.subject;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ✅ issuer
|
|
182
|
+
const issuer = options?.issuer ?? this.issuer;
|
|
183
|
+
if (typeof issuer === "string") {
|
|
184
|
+
jwtOptions.issuer = issuer;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ✅ audience
|
|
188
|
+
const audience = normalizeAudience(options?.audience ?? this.audience);
|
|
189
|
+
if (audience) jwtOptions.audience = audience;
|
|
190
|
+
|
|
191
|
+
// ✅ expiresIn
|
|
192
|
+
const expires =
|
|
193
|
+
options?.expiresIn !== undefined
|
|
194
|
+
? (options.expiresIn as ExpiresIn)
|
|
195
|
+
: this.expiresIn;
|
|
196
|
+
|
|
197
|
+
if (expires !== undefined) {
|
|
198
|
+
jwtOptions.expiresIn = expires;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return jwt.sign(payload, this.secret, jwtOptions);
|
|
202
|
+
} catch (err: any) {
|
|
203
|
+
logger.error("JWT signing failed", {
|
|
204
|
+
adapter: "jwt",
|
|
205
|
+
operation: "sign",
|
|
206
|
+
reason: err?.message
|
|
207
|
+
});
|
|
208
|
+
throw new AdapterError("JWT sign failed");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ================= VERIFY =================
|
|
213
|
+
verify(token: string, options?: { audience?: string | string[] }) {
|
|
214
|
+
try {
|
|
215
|
+
const verifyOptions: jwt.VerifyOptions = {
|
|
216
|
+
algorithms: [this.algorithm]
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
if (typeof this.issuer === "string") {
|
|
220
|
+
verifyOptions.issuer = this.issuer;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const audience = normalizeAudience(options?.audience ?? this.audience);
|
|
224
|
+
if (audience) verifyOptions.audience = audience;
|
|
225
|
+
|
|
226
|
+
return jwt.verify(token, this.secret, verifyOptions);
|
|
227
|
+
} catch (err: any) {
|
|
228
|
+
logger.error("JWT verification failed", {
|
|
229
|
+
adapter: "jwt",
|
|
230
|
+
operation: "verify",
|
|
231
|
+
reason: err?.message
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
if (err?.name === "TokenExpiredError") {
|
|
235
|
+
throw new AdapterError("JWT token has expired");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (err?.name === "JsonWebTokenError") {
|
|
239
|
+
throw new AdapterError("Invalid JWT token");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
throw new AdapterError("JWT verification failed");
|
|
105
243
|
}
|
|
244
|
+
}
|
|
106
245
|
}
|