@forklaunch/core 0.11.0 → 0.11.1

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.
@@ -6,6 +6,7 @@ import { CorsOptions } from 'cors';
6
6
  import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
7
7
  import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
8
8
  export { LevelWithSilent, LevelWithSilentOrString, Logger } from 'pino';
9
+ import { JWTPayload } from 'jose';
9
10
  import { Readable } from 'stream';
10
11
  import { ZodSchemaValidator } from '@forklaunch/validator/zod';
11
12
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
@@ -221,6 +222,7 @@ type JwtAuthMethods = {
221
222
  type AuthMethodsBase = {
222
223
  readonly tokenPrefix?: string;
223
224
  readonly headerName?: string;
225
+ readonly decodeResource?: (token: string) => JWTPayload | Promise<JWTPayload>;
224
226
  } & (BasicAuthMethods | JwtAuthMethods);
225
227
  type PermissionSet = {
226
228
  readonly allowedPermissions: Set<string>;
@@ -613,7 +615,7 @@ type ExpressLikeSchemaHandler<SV extends AnySchemaValidator, P extends ParamsObj
613
615
  * @returns {Set<string> | Promise<Set<string>>} - A set of authorization strings or a promise that resolves to it.
614
616
  */
615
617
  type ExpressLikeSchemaAuthMapper<SV extends AnySchemaValidator, P extends ParamsObject<SV>, ReqBody extends Body<SV>, ReqQuery extends QueryObject<SV>, ReqHeaders extends HeadersObject<SV>, BaseRequest> = ExpressLikeAuthMapper<SV, P extends infer UnmappedParams ? UnmappedParams extends ParamsObject<SV> ? MapParamsSchema<SV, UnmappedParams> : never : never, ReqBody extends infer UnmappedReqBody ? UnmappedReqBody extends Body<SV> ? MapReqBodySchema<SV, UnmappedReqBody> : never : never, ReqQuery extends infer UnmappedReqQuery ? UnmappedReqQuery extends QueryObject<SV> ? MapReqQuerySchema<SV, UnmappedReqQuery> : never : never, ReqHeaders extends infer UnmappedReqHeaders ? UnmappedReqHeaders extends HeadersObject<SV> ? MapReqHeadersSchema<SV, UnmappedReqHeaders> : never : never, BaseRequest>;
616
- type ExpressLikeAuthMapper<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, BaseRequest> = (sub: string, req?: ResolvedForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders, BaseRequest>) => Set<string> | Promise<Set<string>>;
618
+ type ExpressLikeAuthMapper<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, BaseRequest> = (payload: JWTPayload, req?: ResolvedForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders, BaseRequest>) => Set<string> | Promise<Set<string>>;
617
619
  type TokenPrefix<Auth extends AuthMethodsBase> = undefined extends Auth['tokenPrefix'] ? Auth extends BasicAuthMethods ? 'Basic ' : 'Bearer ' : `${Auth['tokenPrefix']} `;
618
620
  type AuthHeaders<Auth extends AuthMethodsBase> = undefined extends Auth['headerName'] ? {
619
621
  authorization: `${TokenPrefix<Auth>}${string}`;
@@ -6,6 +6,7 @@ import { CorsOptions } from 'cors';
6
6
  import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
7
7
  import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
8
8
  export { LevelWithSilent, LevelWithSilentOrString, Logger } from 'pino';
9
+ import { JWTPayload } from 'jose';
9
10
  import { Readable } from 'stream';
10
11
  import { ZodSchemaValidator } from '@forklaunch/validator/zod';
11
12
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
@@ -221,6 +222,7 @@ type JwtAuthMethods = {
221
222
  type AuthMethodsBase = {
222
223
  readonly tokenPrefix?: string;
223
224
  readonly headerName?: string;
225
+ readonly decodeResource?: (token: string) => JWTPayload | Promise<JWTPayload>;
224
226
  } & (BasicAuthMethods | JwtAuthMethods);
225
227
  type PermissionSet = {
226
228
  readonly allowedPermissions: Set<string>;
@@ -613,7 +615,7 @@ type ExpressLikeSchemaHandler<SV extends AnySchemaValidator, P extends ParamsObj
613
615
  * @returns {Set<string> | Promise<Set<string>>} - A set of authorization strings or a promise that resolves to it.
614
616
  */
615
617
  type ExpressLikeSchemaAuthMapper<SV extends AnySchemaValidator, P extends ParamsObject<SV>, ReqBody extends Body<SV>, ReqQuery extends QueryObject<SV>, ReqHeaders extends HeadersObject<SV>, BaseRequest> = ExpressLikeAuthMapper<SV, P extends infer UnmappedParams ? UnmappedParams extends ParamsObject<SV> ? MapParamsSchema<SV, UnmappedParams> : never : never, ReqBody extends infer UnmappedReqBody ? UnmappedReqBody extends Body<SV> ? MapReqBodySchema<SV, UnmappedReqBody> : never : never, ReqQuery extends infer UnmappedReqQuery ? UnmappedReqQuery extends QueryObject<SV> ? MapReqQuerySchema<SV, UnmappedReqQuery> : never : never, ReqHeaders extends infer UnmappedReqHeaders ? UnmappedReqHeaders extends HeadersObject<SV> ? MapReqHeadersSchema<SV, UnmappedReqHeaders> : never : never, BaseRequest>;
616
- type ExpressLikeAuthMapper<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, BaseRequest> = (sub: string, req?: ResolvedForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders, BaseRequest>) => Set<string> | Promise<Set<string>>;
618
+ type ExpressLikeAuthMapper<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, BaseRequest> = (payload: JWTPayload, req?: ResolvedForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders, BaseRequest>) => Set<string> | Promise<Set<string>>;
617
619
  type TokenPrefix<Auth extends AuthMethodsBase> = undefined extends Auth['tokenPrefix'] ? Auth extends BasicAuthMethods ? 'Basic ' : 'Bearer ' : `${Auth['tokenPrefix']} `;
618
620
  type AuthHeaders<Auth extends AuthMethodsBase> = undefined extends Auth['headerName'] ? {
619
621
  authorization: `${TokenPrefix<Auth>}${string}`;
package/lib/http/index.js CHANGED
@@ -147,16 +147,24 @@ function discriminateAuthMethod(auth) {
147
147
  if ("basic" in auth) {
148
148
  return {
149
149
  type: "basic",
150
- auth: auth.basic
150
+ auth: {
151
+ decodeResource: auth.decodeResource,
152
+ login: auth.basic.login
153
+ }
151
154
  };
152
155
  } else if ("jwt" in auth) {
153
156
  return {
154
157
  type: "jwt",
155
- auth: auth.jwt
158
+ auth: {
159
+ decodeResource: auth.decodeResource
160
+ }
156
161
  };
157
162
  } else {
158
163
  return {
159
- type: "jwt"
164
+ type: "jwt",
165
+ auth: {
166
+ decodeResource: auth.decodeResource
167
+ }
160
168
  };
161
169
  }
162
170
  }
@@ -209,32 +217,38 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
209
217
  return invalidAuthorizationTokenFormat;
210
218
  }
211
219
  try {
212
- const decodedJwt = await (0, import_jose.jwtVerify)(
220
+ const decodedJwt = await auth?.decodeResource?.(token) ?? (await (0, import_jose.jwtVerify)(
213
221
  token,
214
222
  new TextEncoder().encode(process.env.JWT_SECRET)
215
- );
216
- if (!decodedJwt.payload.sub) {
223
+ )).payload;
224
+ if (!decodedJwt) {
217
225
  return invalidAuthorizationSubject;
218
226
  }
219
- resourceId = decodedJwt.payload.sub;
227
+ resourceId = decodedJwt;
220
228
  } catch (error) {
221
- req.openTelemetryCollector.error(error);
229
+ req?.openTelemetryCollector.error(error);
222
230
  return invalidAuthorizationToken;
223
231
  }
224
232
  break;
225
233
  }
226
234
  case "basic": {
227
- if (authorizationToken !== (authorizationMethod.tokenPrefix ?? "Basic")) {
228
- return invalidAuthorizationTokenFormat;
229
- }
230
- const [username, password] = Buffer.from(token, "base64").toString("utf-8").split(":");
231
- if (!username || !password) {
235
+ if (tokenPrefix !== (authorizationMethod.tokenPrefix ?? "Basic")) {
232
236
  return invalidAuthorizationTokenFormat;
233
237
  }
234
- if (!auth.login(username, password)) {
235
- return invalidAuthorizationLogin;
238
+ if (auth.decodeResource) {
239
+ resourceId = await auth.decodeResource(token);
240
+ } else {
241
+ const [username, password] = Buffer.from(token, "base64").toString("utf-8").split(":");
242
+ if (!username || !password) {
243
+ return invalidAuthorizationTokenFormat;
244
+ }
245
+ if (!auth.login(username, password)) {
246
+ return invalidAuthorizationLogin;
247
+ }
248
+ resourceId = {
249
+ sub: username
250
+ };
236
251
  }
237
- resourceId = username;
238
252
  break;
239
253
  }
240
254
  default:
@@ -629,7 +643,18 @@ function parse(req, res, next) {
629
643
  enumerable: true,
630
644
  configurable: false
631
645
  });
632
- req.headers = parsedRequest.value.headers ?? {};
646
+ const parsedHeaders = parsedRequest.value.headers ?? {};
647
+ req.headers = Object.keys(req.headers).reduce(
648
+ (acc, key) => {
649
+ if (parsedHeaders?.[key]) {
650
+ acc[key] = parsedHeaders[key];
651
+ } else {
652
+ acc[key] = req.headers[key];
653
+ }
654
+ return acc;
655
+ },
656
+ {}
657
+ );
633
658
  }
634
659
  if (!parsedRequest.ok) {
635
660
  switch (req.contractDetails.options?.requestValidation) {