@forklaunch/core 0.13.1 → 0.13.3
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/lib/http/index.d.mts +79 -58
- package/lib/http/index.d.ts +79 -58
- package/lib/http/index.js +107 -43
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +107 -43
- package/lib/http/index.mjs.map +1 -1
- package/package.json +8 -8
package/lib/http/index.mjs
CHANGED
@@ -83,22 +83,44 @@ function isTypedHandler(maybeTypedHandler) {
|
|
83
83
|
import { isNever } from "@forklaunch/common";
|
84
84
|
|
85
85
|
// src/http/discriminateAuthMethod.ts
|
86
|
+
import { createHmac } from "crypto";
|
86
87
|
import { jwtVerify } from "jose";
|
88
|
+
|
89
|
+
// src/http/guards/isBasicAuthMethod.ts
|
90
|
+
function isBasicAuthMethod(maybeBasicAuthMethod) {
|
91
|
+
return typeof maybeBasicAuthMethod === "object" && maybeBasicAuthMethod !== null && "basic" in maybeBasicAuthMethod && maybeBasicAuthMethod.basic != null;
|
92
|
+
}
|
93
|
+
|
94
|
+
// src/http/guards/isHmacMethod.ts
|
95
|
+
function isHmacMethod(maybeHmacMethod) {
|
96
|
+
return typeof maybeHmacMethod === "object" && maybeHmacMethod !== null && "secretKey" in maybeHmacMethod && maybeHmacMethod.secretKey != null;
|
97
|
+
}
|
98
|
+
|
99
|
+
// src/http/guards/isJwtAuthMethod.ts
|
100
|
+
function isJwtAuthMethod(maybeJwtAuthMethod) {
|
101
|
+
return typeof maybeJwtAuthMethod === "object" && maybeJwtAuthMethod !== null && "jwt" in maybeJwtAuthMethod && maybeJwtAuthMethod.jwt != null;
|
102
|
+
}
|
103
|
+
|
104
|
+
// src/http/discriminateAuthMethod.ts
|
87
105
|
async function discriminateAuthMethod(auth) {
|
88
|
-
|
89
|
-
|
106
|
+
let authMethod;
|
107
|
+
if (isBasicAuthMethod(auth)) {
|
108
|
+
authMethod = {
|
90
109
|
type: "basic",
|
91
110
|
auth: {
|
92
111
|
decodeResource: auth.decodeResource,
|
93
112
|
login: auth.basic.login
|
94
113
|
}
|
95
114
|
};
|
96
|
-
} else if (
|
115
|
+
} else if (isJwtAuthMethod(auth)) {
|
97
116
|
const jwt = auth.jwt;
|
98
117
|
let verificationFunction;
|
99
|
-
if ("
|
118
|
+
if ("signatureKey" in jwt) {
|
100
119
|
verificationFunction = async (token) => {
|
101
|
-
const { payload } = await jwtVerify(
|
120
|
+
const { payload } = await jwtVerify(
|
121
|
+
token,
|
122
|
+
Buffer.from(jwt.signatureKey)
|
123
|
+
);
|
102
124
|
return payload;
|
103
125
|
};
|
104
126
|
} else {
|
@@ -106,7 +128,7 @@ async function discriminateAuthMethod(auth) {
|
|
106
128
|
if ("jwksPublicKeyUrl" in jwt) {
|
107
129
|
const jwksResponse = await fetch(jwt.jwksPublicKeyUrl);
|
108
130
|
jwks = (await jwksResponse.json()).keys;
|
109
|
-
} else {
|
131
|
+
} else if ("jwksPublicKey" in jwt) {
|
110
132
|
jwks = [jwt.jwksPublicKey];
|
111
133
|
}
|
112
134
|
verificationFunction = async (token) => {
|
@@ -120,35 +142,35 @@ async function discriminateAuthMethod(auth) {
|
|
120
142
|
}
|
121
143
|
};
|
122
144
|
}
|
123
|
-
|
145
|
+
authMethod = {
|
124
146
|
type: "jwt",
|
125
147
|
auth: {
|
126
148
|
decodeResource: auth.decodeResource,
|
127
149
|
verificationFunction
|
128
150
|
}
|
129
151
|
};
|
130
|
-
} else if (
|
131
|
-
|
132
|
-
type: "
|
133
|
-
auth: {
|
134
|
-
secretKey: auth.secretKey
|
135
|
-
}
|
136
|
-
};
|
137
|
-
} else {
|
138
|
-
return {
|
139
|
-
type: "jwt",
|
152
|
+
} else if (isHmacMethod(auth)) {
|
153
|
+
authMethod = {
|
154
|
+
type: "hmac",
|
140
155
|
auth: {
|
141
|
-
|
142
|
-
verificationFunction: async (
|
143
|
-
const
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
156
|
+
secretKeys: auth.hmac.secretKeys,
|
157
|
+
verificationFunction: async (method, path, body, timestamp, nonce, signature, secretKey) => {
|
158
|
+
const hmac = createHmac("sha256", secretKey);
|
159
|
+
hmac.update(`${method}
|
160
|
+
${path}
|
161
|
+
${body}
|
162
|
+
${timestamp}
|
163
|
+
${nonce}`);
|
164
|
+
const digest = hmac.digest("base64");
|
165
|
+
return digest === signature;
|
148
166
|
}
|
149
167
|
}
|
150
168
|
};
|
151
169
|
}
|
170
|
+
if (authMethod == null) {
|
171
|
+
throw new Error("Invalid auth method");
|
172
|
+
}
|
173
|
+
return authMethod;
|
152
174
|
}
|
153
175
|
|
154
176
|
// src/http/guards/hasPermissionChecks.ts
|
@@ -166,19 +188,14 @@ function hasScopeChecks(maybePermissionedAuth) {
|
|
166
188
|
return typeof maybePermissionedAuth === "object" && maybePermissionedAuth !== null && "requiredScope" in maybePermissionedAuth && maybePermissionedAuth.requiredScope != null;
|
167
189
|
}
|
168
190
|
|
169
|
-
// src/http/guards/isSystemAuthMethod.ts
|
170
|
-
function isSystemAuthMethod(maybeSystemAuthMethod) {
|
171
|
-
return typeof maybeSystemAuthMethod === "object" && maybeSystemAuthMethod !== null && "secretKey" in maybeSystemAuthMethod;
|
172
|
-
}
|
173
|
-
|
174
191
|
// src/http/middleware/request/auth.middleware.ts
|
175
192
|
var invalidAuthorizationTokenFormat = [
|
176
193
|
401,
|
177
194
|
"Invalid Authorization token format."
|
178
195
|
];
|
179
|
-
var
|
196
|
+
var invalidAuthorizationSignature = [
|
180
197
|
403,
|
181
|
-
"Invalid Authorization
|
198
|
+
"Invalid Authorization signature."
|
182
199
|
];
|
183
200
|
var invalidAuthorizationTokenPermissions = [
|
184
201
|
403,
|
@@ -205,6 +222,16 @@ var authorizationTokenRequired = [
|
|
205
222
|
401,
|
206
223
|
"Authorization token required."
|
207
224
|
];
|
225
|
+
var invalidInstantiation = [
|
226
|
+
500,
|
227
|
+
"Invalid instantiation of authorization method."
|
228
|
+
];
|
229
|
+
function parseHmacTokenPart(part, expectedKey) {
|
230
|
+
if (!part) return void 0;
|
231
|
+
const [key, ...rest] = part.split("=");
|
232
|
+
if (key !== expectedKey || rest.length === 0) return void 0;
|
233
|
+
return rest.join("=");
|
234
|
+
}
|
208
235
|
async function checkAuthorizationToken(authorizationMethod, globalOptions, authorizationToken, req) {
|
209
236
|
if (authorizationMethod == null) {
|
210
237
|
return void 0;
|
@@ -216,27 +243,54 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
|
|
216
243
|
if (authorizationToken == null) {
|
217
244
|
return authorizationTokenRequired;
|
218
245
|
}
|
219
|
-
const [tokenPrefix,
|
246
|
+
const [tokenPrefix, ...tokenParts] = authorizationToken.split(" ");
|
247
|
+
if (!tokenParts.length || !tokenPrefix) {
|
248
|
+
return invalidAuthorizationTokenFormat;
|
249
|
+
}
|
220
250
|
let resourceId;
|
221
251
|
const { type, auth } = await discriminateAuthMethod(
|
222
252
|
collapsedAuthorizationMethod
|
223
253
|
);
|
224
254
|
switch (type) {
|
225
|
-
case "
|
226
|
-
|
255
|
+
case "hmac": {
|
256
|
+
const [keyId, timestamp, nonce, signature] = tokenParts;
|
257
|
+
if (keyId == null || timestamp == null || nonce == null || signature == null || tokenPrefix !== (collapsedAuthorizationMethod.tokenPrefix ?? "HMAC")) {
|
227
258
|
return invalidAuthorizationToken;
|
228
259
|
}
|
260
|
+
if (!collapsedAuthorizationMethod.hmac?.secretKeys) {
|
261
|
+
return invalidInstantiation;
|
262
|
+
}
|
263
|
+
const parsedKeyId = parseHmacTokenPart(keyId, "keyId");
|
264
|
+
const parsedTimestamp = parseHmacTokenPart(timestamp, "ts");
|
265
|
+
const parsedNonce = parseHmacTokenPart(nonce, "nonce");
|
266
|
+
const parsedSignature = parseHmacTokenPart(signature, "signature");
|
267
|
+
if (!parsedKeyId || !parsedTimestamp || !parsedNonce || !parsedSignature) {
|
268
|
+
return invalidAuthorizationTokenFormat;
|
269
|
+
}
|
270
|
+
const verificationResult = await auth.verificationFunction(
|
271
|
+
req?.method ?? "",
|
272
|
+
req?.path ?? "",
|
273
|
+
JSON.stringify(req?.body ?? ""),
|
274
|
+
parsedTimestamp,
|
275
|
+
parsedNonce,
|
276
|
+
parsedSignature,
|
277
|
+
collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
|
278
|
+
);
|
279
|
+
if (!verificationResult) {
|
280
|
+
return invalidAuthorizationSignature;
|
281
|
+
}
|
229
282
|
resourceId = null;
|
230
283
|
break;
|
231
284
|
}
|
232
285
|
case "jwt": {
|
286
|
+
const [token] = tokenParts;
|
233
287
|
if (tokenPrefix !== (collapsedAuthorizationMethod.tokenPrefix ?? "Bearer")) {
|
234
288
|
return invalidAuthorizationTokenFormat;
|
235
289
|
}
|
236
290
|
try {
|
237
291
|
const decodedJwt = "decodeResource" in auth && auth.decodeResource ? await auth.decodeResource(token) : "verificationFunction" in auth && auth.verificationFunction ? await auth.verificationFunction(token) : void 0;
|
238
292
|
if (!decodedJwt) {
|
239
|
-
return
|
293
|
+
return invalidAuthorizationToken;
|
240
294
|
}
|
241
295
|
resourceId = decodedJwt;
|
242
296
|
} catch (error) {
|
@@ -246,6 +300,7 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
|
|
246
300
|
break;
|
247
301
|
}
|
248
302
|
case "basic": {
|
303
|
+
const [token] = tokenParts;
|
249
304
|
if (tokenPrefix !== (collapsedAuthorizationMethod.tokenPrefix ?? "Basic")) {
|
250
305
|
return invalidAuthorizationTokenFormat;
|
251
306
|
}
|
@@ -269,9 +324,12 @@ async function checkAuthorizationToken(authorizationMethod, globalOptions, autho
|
|
269
324
|
isNever(type);
|
270
325
|
return [401, "Invalid Authorization method."];
|
271
326
|
}
|
272
|
-
if (
|
327
|
+
if (isHmacMethod(collapsedAuthorizationMethod) && resourceId == null) {
|
273
328
|
return;
|
274
329
|
}
|
330
|
+
if (resourceId == null) {
|
331
|
+
return invalidAuthorizationToken;
|
332
|
+
}
|
275
333
|
if (hasScopeChecks(collapsedAuthorizationMethod)) {
|
276
334
|
if (collapsedAuthorizationMethod.surfaceScopes) {
|
277
335
|
const resourceScopes = await collapsedAuthorizationMethod.surfaceScopes(
|
@@ -1280,13 +1338,15 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1280
1338
|
const maybeTypedHandler = middlewareOrMiddlewareAndTypedHandler[middlewareOrMiddlewareAndTypedHandler.length - 1];
|
1281
1339
|
if (isTypedHandler(maybeTypedHandler)) {
|
1282
1340
|
const { contractDetails, handlers } = maybeTypedHandler;
|
1283
|
-
const
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
);
|
1341
|
+
const finalHandlers = [];
|
1342
|
+
if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
|
1343
|
+
finalHandlers.push(
|
1344
|
+
contractDetailsOrMiddlewareOrTypedHandler
|
1345
|
+
);
|
1346
|
+
}
|
1347
|
+
finalHandlers.push(...middlewareOrMiddlewareAndTypedHandler);
|
1348
|
+
finalHandlers.push(...handlers);
|
1349
|
+
const router = this.registerRoute(method, path, registrationMethod, contractDetails, ...finalHandlers);
|
1290
1350
|
return router;
|
1291
1351
|
} else {
|
1292
1352
|
if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler) || isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
|
@@ -1495,6 +1555,10 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
|
|
1495
1555
|
].forEach((arg) => {
|
1496
1556
|
if (isForklaunchRouter(arg)) {
|
1497
1557
|
this.routers.push(arg);
|
1558
|
+
arg.routerOptions = {
|
1559
|
+
...this.routerOptions ?? {},
|
1560
|
+
...arg.routerOptions ?? {}
|
1561
|
+
};
|
1498
1562
|
}
|
1499
1563
|
});
|
1500
1564
|
return this.registerNestableMiddlewareHandler(
|