@rekog/mcp-nest 1.7.4-alpha.13 → 1.7.4-alpha.15

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.
@@ -11,6 +11,10 @@ interface OAuthCallbackRequest extends ExpressRequest {
11
11
  provider: string;
12
12
  };
13
13
  }
14
+ interface RequestWithRawBody extends ExpressRequest {
15
+ rawBody?: Buffer;
16
+ textBody?: string;
17
+ }
14
18
  export declare function createMcpOAuthController(endpoints?: OAuthEndpointConfiguration, options?: {
15
19
  disableWellKnownProtectedResourceMetadata?: boolean;
16
20
  disableWellKnownAuthorizationServerMetadata?: boolean;
@@ -23,6 +27,8 @@ export declare function createMcpOAuthController(endpoints?: OAuthEndpointConfig
23
27
  readonly store: IOAuthStore;
24
28
  readonly jwtTokenService: JwtTokenService;
25
29
  readonly clientService: ClientService;
30
+ parseRequestBody(body: any, req?: RequestWithRawBody): Record<string, any>;
31
+ captureRawBody(req: RequestWithRawBody, res: Response, next: NextFunction): void;
26
32
  getProtectedResourceMetadata(): {
27
33
  authorization_servers: string[];
28
34
  resource: string;
@@ -47,8 +53,9 @@ export declare function createMcpOAuthController(endpoints?: OAuthEndpointConfig
47
53
  authorize(query: any, req: any, res: Response, next: NextFunction): Promise<void>;
48
54
  handleProviderCallback(req: OAuthCallbackRequest, res: Response, next: NextFunction): void;
49
55
  processAuthenticationSuccess(req: OAuthCallbackRequest, res: Response): Promise<void>;
50
- exchangeToken(body: any, req: any): Promise<TokenPair>;
51
- extractClientCredentials(req: any, body: any): {
56
+ exchangeToken(body: any, req: RequestWithRawBody, res: Response): Promise<TokenPair>;
57
+ processTokenExchange(parsedBody: Record<string, any>, req: RequestWithRawBody): Promise<TokenPair>;
58
+ extractClientCredentials(req: RequestWithRawBody, body: any): {
52
59
  client_id: string;
53
60
  client_secret?: string;
54
61
  };
@@ -65,7 +72,6 @@ export declare function createMcpOAuthController(endpoints?: OAuthEndpointConfig
65
72
  client_secret?: string;
66
73
  }): Promise<TokenPair>;
67
74
  validatePKCE(code_verifier: string, code_challenge: string, method: string): boolean;
68
- parseRequestBody(body: any): Record<string, any>;
69
75
  };
70
76
  };
71
77
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-oauth.controller.d.ts","sourceRoot":"","sources":["../../src/authz/mcp-oauth.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,MAAM,EAMP,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5E,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAE7D,UAAU,oBAAqB,SAAQ,cAAc;IACnD,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,gBAAgB,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,0BAA+B,EAC1C,OAAO,CAAC,EAAE;IACR,yCAAyC,CAAC,EAAE,OAAO,CAAC;IACpD,2CAA2C,CAAC,EAAE,OAAO,CAAC;CACvD;kBA8B4C,kBAAkB,SACpB,WAAW,mBACxB,eAAe,iBACjB,aAAa;;4BAPnB,MAAM;+BACH,OAAO;0BACZ,kBAAkB;wBAGK,WAAW;kCACxB,eAAe;gCACjB,aAAa;;;;;;;;;;;;;;;;;;;;;wCAwGO,GAAG;yBAM/B,GAAG,OAEd,GAAG,OACI,QAAQ,QACN,YAAY;oCA2Ed,oBAAoB,OACpB,QAAQ,QACN,YAAY;0CA2BrB,oBAAoB,OACpB,QAAQ;4BAmFC,GAAG,OACL,GAAG,GACd,OAAO,CAAC,SAAS,CAAC;sCA0Dd,GAAG,QACF,GAAG,GACR;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE;6CA+BtC,GAAG,qBACQ;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,IAAI;2CAqCC,MAAM,iBACG,MAAM,iBACN,MAAM,qBACF;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,SAAS,CAAC;+CA4FJ,MAAM,qBACF;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,SAAS,CAAC;oCA6EJ,MAAM,kBACL,MAAM,UACd,MAAM,GACb,OAAO;+BAgBa,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;EAsBnD"}
1
+ {"version":3,"file":"mcp-oauth.controller.d.ts","sourceRoot":"","sources":["../../src/authz/mcp-oauth.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,MAAM,EAMP,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5E,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAE7D,UAAU,oBAAqB,SAAQ,cAAc;IACnD,IAAI,CAAC,EAAE;QACL,OAAO,EAAE,gBAAgB,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAGD,UAAU,kBAAmB,SAAQ,cAAc;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,0BAA+B,EAC1C,OAAO,CAAC,EAAE;IACR,yCAAyC,CAAC,EAAE,OAAO,CAAC;IACpD,2CAA2C,CAAC,EAAE,OAAO,CAAC;CACvD;kBAgC4C,kBAAkB,SACpB,WAAW,mBACxB,eAAe,iBACjB,aAAa;;4BARnB,MAAM;+BACH,OAAO;0BACZ,kBAAkB;wBAIK,WAAW;kCACxB,eAAe;gCACjB,aAAa;+BAWhB,GAAG,QAAQ,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;4BA+CxD,kBAAkB,OAAO,QAAQ,QAAQ,YAAY;;;;;;;;;;;;;;;;;;;;;wCAoIzB,GAAG;yBAM/B,GAAG,OAEd,GAAG,OACI,QAAQ,QACN,YAAY;oCA2Ed,oBAAoB,OACpB,QAAQ,QACN,YAAY;0CA2BrB,oBAAoB,OACpB,QAAQ;4BAmFC,GAAG,OACL,kBAAkB,OACG,QAAQ,GACxC,OAAO,CAAC,SAAS,CAAC;yCA6CP,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,OAC1B,kBAAkB,GACtB,OAAO,CAAC,SAAS,CAAC;sCA4Dd,kBAAkB,QACjB,GAAG,GACR;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE;6CA+BtC,GAAG,qBACQ;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,IAAI;2CAqCC,MAAM,iBACG,MAAM,iBACN,MAAM,qBACF;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,SAAS,CAAC;+CA4FJ,MAAM,qBACF;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,GAC/D,OAAO,CAAC,SAAS,CAAC;oCA6EJ,MAAM,kBACL,MAAM,UACd,MAAM,GACb,OAAO;;EAcb"}
@@ -45,6 +45,66 @@ function createMcpOAuthController(endpoints = {}, options) {
45
45
  this.isProduction = options.cookieSecure;
46
46
  this.options = options;
47
47
  }
48
+ parseRequestBody(body, req) {
49
+ if (body && typeof body === 'object' && Object.keys(body).length > 0) {
50
+ return body;
51
+ }
52
+ if (typeof body === 'string' && body.length > 0) {
53
+ const params = new URLSearchParams(body);
54
+ const parsedBody = {};
55
+ for (const [key, value] of params.entries()) {
56
+ parsedBody[key] = value;
57
+ }
58
+ return parsedBody;
59
+ }
60
+ if (req?.textBody) {
61
+ const params = new URLSearchParams(req.textBody);
62
+ const parsedBody = {};
63
+ for (const [key, value] of params.entries()) {
64
+ parsedBody[key] = value;
65
+ }
66
+ return parsedBody;
67
+ }
68
+ if (req?.rawBody) {
69
+ const bodyString = req.rawBody.toString('utf-8');
70
+ if (bodyString) {
71
+ const params = new URLSearchParams(bodyString);
72
+ const parsedBody = {};
73
+ for (const [key, value] of params.entries()) {
74
+ parsedBody[key] = value;
75
+ }
76
+ return parsedBody;
77
+ }
78
+ }
79
+ return {};
80
+ }
81
+ captureRawBody(req, res, next) {
82
+ if (req.headers['content-type']?.includes('application/x-www-form-urlencoded')) {
83
+ let rawBody = '';
84
+ req.on('data', (chunk) => {
85
+ rawBody += chunk.toString('utf-8');
86
+ });
87
+ req.on('end', () => {
88
+ req.textBody = rawBody;
89
+ if (rawBody) {
90
+ const params = new URLSearchParams(rawBody);
91
+ const parsedBody = {};
92
+ for (const [key, value] of params.entries()) {
93
+ parsedBody[key] = value;
94
+ }
95
+ req.body = parsedBody;
96
+ }
97
+ next();
98
+ });
99
+ req.on('error', (err) => {
100
+ this.logger.error('Error reading request body:', err);
101
+ next(err);
102
+ });
103
+ }
104
+ else {
105
+ next();
106
+ }
107
+ }
48
108
  getProtectedResourceMetadata() {
49
109
  const authorizationServerIssuer = this.options.jwtIssuer;
50
110
  const resourceIdentifier = this.options.resource;
@@ -189,23 +249,55 @@ function createMcpOAuthController(endpoints = {}, options) {
189
249
  await this.store.removeOAuthSession(sessionId);
190
250
  res.redirect(redirectUrl.toString());
191
251
  }
192
- async exchangeToken(body, req) {
193
- const parsedBody = this.parseRequestBody(body);
252
+ async exchangeToken(body, req, res) {
253
+ const isFormUrlEncoded = req.headers['content-type']?.includes('application/x-www-form-urlencoded');
254
+ const isBodyEmpty = !body ||
255
+ (typeof body === 'object' &&
256
+ Object.keys(body).length === 0);
257
+ if (isFormUrlEncoded && isBodyEmpty) {
258
+ return new Promise((resolve, reject) => {
259
+ this.captureRawBody(req, res, (err) => {
260
+ if (err) {
261
+ reject(err instanceof Error ? err : new Error(String(err ?? 'error')));
262
+ return;
263
+ }
264
+ void (async () => {
265
+ try {
266
+ const parsedBody = this.parseRequestBody(req.body || body, req);
267
+ const result = await this.processTokenExchange(parsedBody, req);
268
+ resolve(result);
269
+ }
270
+ catch (error) {
271
+ reject(error instanceof Error
272
+ ? error
273
+ : new Error(String(error ?? 'error')));
274
+ }
275
+ })();
276
+ });
277
+ });
278
+ }
279
+ const parsedBody = this.parseRequestBody(body, req);
280
+ return this.processTokenExchange(parsedBody, req);
281
+ }
282
+ async processTokenExchange(parsedBody, req) {
194
283
  const { grant_type, code, code_verifier, redirect_uri, refresh_token } = parsedBody;
195
284
  if (!grant_type) {
196
285
  this.logger.error('Missing grant_type in request body:', {
197
- bodyType: typeof body,
198
- bodyKeys: Object.keys(body || {}),
199
286
  parsedBodyKeys: Object.keys(parsedBody),
200
287
  contentType: req.headers['content-type'],
201
- rawBody: body,
288
+ textBody: req.textBody,
289
+ parsedBody,
202
290
  });
203
291
  throw new common_1.BadRequestException('Missing grant_type parameter');
204
292
  }
205
293
  switch (grant_type) {
206
294
  case 'authorization_code': {
207
295
  const clientCredentials = this.extractClientCredentials(req, parsedBody);
208
- return await this.handleAuthorizationCodeGrant(code, code_verifier, redirect_uri, clientCredentials);
296
+ return await this.handleAuthorizationCodeGrant(typeof code === 'string' ? code : String(code ?? ''), typeof code_verifier === 'string'
297
+ ? code_verifier
298
+ : String(code_verifier ?? ''), typeof redirect_uri === 'string'
299
+ ? redirect_uri
300
+ : String(redirect_uri ?? ''), clientCredentials);
209
301
  }
210
302
  case 'refresh_token': {
211
303
  let clientCredentials;
@@ -215,14 +307,16 @@ function createMcpOAuthController(endpoints = {}, options) {
215
307
  catch {
216
308
  clientCredentials = { client_id: '' };
217
309
  }
218
- return await this.handleRefreshTokenGrant(refresh_token, clientCredentials);
310
+ return await this.handleRefreshTokenGrant(typeof refresh_token === 'string'
311
+ ? refresh_token
312
+ : String(refresh_token ?? ''), clientCredentials);
219
313
  }
220
314
  default:
221
315
  throw new common_1.BadRequestException(`Unsupported grant_type: ${grant_type}`);
222
316
  }
223
317
  }
224
318
  extractClientCredentials(req, body) {
225
- const parsedBody = this.parseRequestBody(body);
319
+ const parsedBody = this.parseRequestBody(body, req);
226
320
  const authHeader = req.headers?.authorization;
227
321
  if (authHeader && authHeader.startsWith('Basic ')) {
228
322
  const credentials = Buffer.from(authHeader.slice(6), 'base64').toString('utf-8');
@@ -312,7 +406,7 @@ function createMcpOAuthController(endpoints = {}, options) {
312
406
  user_data: userData,
313
407
  });
314
408
  await this.store.removeAuthCode(code);
315
- this.logger.log('handleAuthorizationCodeGrant - Token pair generated for user:', authCode.user_id);
409
+ this.logger.debug('handleAuthorizationCodeGrant - Token pair generated for user:', authCode.user_id);
316
410
  return tokens;
317
411
  }
318
412
  async handleRefreshTokenGrant(refresh_token, clientCredentials) {
@@ -376,20 +470,6 @@ function createMcpOAuthController(endpoints = {}, options) {
376
470
  }
377
471
  return false;
378
472
  }
379
- parseRequestBody(body) {
380
- if (body && typeof body === 'object' && Object.keys(body).length > 0) {
381
- return body;
382
- }
383
- if (typeof body === 'string' && body.length > 0) {
384
- const params = new URLSearchParams(body);
385
- const parsedBody = {};
386
- for (const [key, value] of params.entries()) {
387
- parsedBody[key] = value;
388
- }
389
- return parsedBody;
390
- }
391
- return {};
392
- }
393
473
  };
394
474
  __decorate([
395
475
  OptionalGet(endpoints.wellKnownProtectedResourceMetadata, !options?.disableWellKnownProtectedResourceMetadata),
@@ -439,8 +519,9 @@ function createMcpOAuthController(endpoints = {}, options) {
439
519
  (0, common_1.HttpCode)(200),
440
520
  __param(0, (0, common_1.Body)()),
441
521
  __param(1, (0, common_1.Req)()),
522
+ __param(2, (0, common_1.Res)({ passthrough: true })),
442
523
  __metadata("design:type", Function),
443
- __metadata("design:paramtypes", [Object, Object]),
524
+ __metadata("design:paramtypes", [Object, Object, Object]),
444
525
  __metadata("design:returntype", Promise)
445
526
  ], McpOAuthController.prototype, "exchangeToken", null);
446
527
  McpOAuthController = McpOAuthController_1 = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-oauth.controller.js","sourceRoot":"","sources":["../../src/authz/mcp-oauth.controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BadRequestException,\n Body,\n Controller,\n Get,\n Header,\n HttpCode,\n Inject,\n Logger,\n Next,\n Post,\n Query,\n Req,\n Res,\n} from '@nestjs/common';\nimport { createHash, randomBytes } from 'crypto';\nimport { Request as ExpressRequest, NextFunction, Response } from 'express';\nimport passport from 'passport';\nimport { normalizeEndpoint } from '../mcp/utils/normalize-endpoint';\nimport {\n OAuthEndpointConfiguration,\n OAuthModuleOptions,\n OAuthSession,\n OAuthUserProfile,\n} from './providers/oauth-provider.interface';\nimport { ClientService } from './services/client.service';\nimport { JwtTokenService, TokenPair } from './services/jwt-token.service';\nimport { STRATEGY_NAME } from './services/oauth-strategy.service';\nimport { IOAuthStore } from './stores/oauth-store.interface';\n\ninterface OAuthCallbackRequest extends ExpressRequest {\n user?: {\n profile: OAuthUserProfile;\n accessToken: string;\n provider: string;\n };\n}\n\nexport function createMcpOAuthController(\n endpoints: OAuthEndpointConfiguration = {},\n options?: {\n disableWellKnownProtectedResourceMetadata?: boolean;\n disableWellKnownAuthorizationServerMetadata?: boolean;\n },\n) {\n // Optional decorator helpers\n const OptionalGet = (\n path: string | string[] | undefined,\n enabled: boolean,\n ): MethodDecorator => {\n return enabled && path\n ? (Get as unknown as (p?: any) => MethodDecorator)(path)\n : ((() => {}) as unknown as MethodDecorator);\n };\n const OptionalHeader = (\n name: string,\n value: string,\n enabled: boolean,\n ): MethodDecorator => {\n return enabled\n ? (Header as unknown as (n: string, v: string) => MethodDecorator)(\n name,\n value,\n )\n : ((() => {}) as unknown as MethodDecorator);\n };\n @Controller()\n class McpOAuthController {\n readonly logger = new Logger(McpOAuthController.name);\n readonly serverUrl: string;\n readonly isProduction: boolean;\n readonly options: OAuthModuleOptions;\n constructor(\n @Inject('OAUTH_MODULE_OPTIONS') options: OAuthModuleOptions,\n @Inject('IOAuthStore') readonly store: IOAuthStore,\n readonly jwtTokenService: JwtTokenService,\n readonly clientService: ClientService,\n ) {\n this.serverUrl = options.serverUrl;\n this.isProduction = options.cookieSecure;\n this.options = options;\n }\n\n @OptionalGet(\n endpoints.wellKnownProtectedResourceMetadata,\n !options?.disableWellKnownProtectedResourceMetadata,\n )\n @OptionalHeader(\n 'content-type',\n 'application/json',\n !options?.disableWellKnownProtectedResourceMetadata,\n )\n getProtectedResourceMetadata() {\n // The issuer URL of your authorization server.\n const authorizationServerIssuer = this.options.jwtIssuer;\n\n // The canonical URI of the MCP server resource itself.\n const resourceIdentifier = this.options.resource;\n\n const metadata = {\n /**\n * REQUIRED by MCP Spec.\n * A list of authorization server issuer URLs that can issue tokens for this resource.\n */\n authorization_servers: [authorizationServerIssuer],\n\n /**\n * RECOMMENDED by RFC 9728.\n * The identifier for this resource server.\n */\n resource: resourceIdentifier,\n\n /**\n * RECOMMENDED by RFC 9728.\n * A list of scopes that this resource server understands.\n */\n scopes_supported:\n this.options.protectedResourceMetadata.scopesSupported,\n\n /**\n * RECOMMENDED by RFC 9728.\n * A list of methods clients can use to present the access token.\n */\n bearer_methods_supported:\n this.options.protectedResourceMetadata.bearerMethodsSupported,\n\n /**\n * OPTIONAL but helpful custom metadata.\n * Declares which version of the MCP spec this server supports.\n */\n mcp_versions_supported:\n this.options.protectedResourceMetadata.mcpVersionsSupported,\n };\n\n return metadata;\n }\n\n // OAuth endpoints\n @OptionalGet(\n endpoints.wellKnownAuthorizationServerMetadata,\n !options?.disableWellKnownAuthorizationServerMetadata,\n )\n @OptionalHeader(\n 'content-type',\n 'application/json',\n !options?.disableWellKnownAuthorizationServerMetadata,\n )\n getAuthorizationServerMetadata() {\n return {\n issuer: this.serverUrl,\n authorization_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints.authorize}`,\n ),\n token_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints.token}`,\n ),\n registration_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints.register}`,\n ),\n response_types_supported:\n this.options.authorizationServerMetadata.responseTypesSupported,\n response_modes_supported:\n this.options.authorizationServerMetadata.responseModesSupported,\n grant_types_supported:\n this.options.authorizationServerMetadata.grantTypesSupported,\n token_endpoint_auth_methods_supported:\n this.options.authorizationServerMetadata\n .tokenEndpointAuthMethodsSupported,\n scopes_supported:\n this.options.authorizationServerMetadata.scopesSupported,\n revocation_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints?.revoke}`,\n ),\n code_challenge_methods_supported:\n this.options.authorizationServerMetadata\n .codeChallengeMethodsSupported,\n };\n }\n\n @Post(endpoints.register)\n async registerClient(@Body() registrationDto: any) {\n return await this.clientService.registerClient(registrationDto);\n }\n\n @Get(endpoints.authorize)\n async authorize(\n @Query() query: any,\n @Req()\n req: any,\n @Res() res: Response,\n @Next() next: NextFunction,\n ) {\n const {\n response_type,\n client_id,\n redirect_uri,\n code_challenge,\n code_challenge_method,\n state,\n scope,\n } = query;\n const resource = this.options.resource;\n if (response_type !== 'code') {\n throw new BadRequestException('Only response_type=code is supported');\n }\n\n if (!client_id) {\n throw new BadRequestException('Missing required parameters');\n }\n\n // Validate client and redirect URI\n const client = await this.clientService.getClient(client_id);\n if (!client) {\n throw new BadRequestException('Invalid client_id');\n }\n\n const validRedirect = await this.clientService.validateRedirectUri(\n client_id,\n redirect_uri,\n );\n if (!validRedirect) {\n throw new BadRequestException('Invalid redirect_uri');\n }\n\n // Create OAuth session\n const sessionId = randomBytes(32).toString('base64url');\n const sessionState = randomBytes(32).toString('base64url');\n\n const oauthSession: OAuthSession = {\n sessionId,\n state: sessionState,\n clientId: client_id,\n redirectUri: redirect_uri,\n codeChallenge: code_challenge,\n codeChallengeMethod: code_challenge_method || 'plain',\n oauthState: state,\n scope: scope,\n resource,\n expiresAt: Date.now() + this.options.oauthSessionExpiresIn,\n };\n\n await this.store.storeOAuthSession(sessionId, oauthSession);\n\n // Set session cookie\n res.cookie('oauth_session', sessionId, {\n httpOnly: true,\n secure: this.isProduction,\n maxAge: this.options.oauthSessionExpiresIn,\n });\n\n // Store state for passport\n res.cookie('oauth_state', sessionState, {\n httpOnly: true,\n secure: this.isProduction,\n maxAge: this.options.oauthSessionExpiresIn,\n });\n\n // Redirect to the provider's auth endpoint\n passport.authenticate(STRATEGY_NAME, {\n state: req.cookies?.oauth_state,\n })(req, res, next);\n }\n\n @Get(endpoints.callback)\n handleProviderCallback(\n @Req() req: OAuthCallbackRequest,\n @Res() res: Response,\n @Next() next: NextFunction,\n ) {\n // Use a custom callback to handle the authentication result\n passport.authenticate(\n STRATEGY_NAME,\n { session: false },\n async (err: any, user: any) => {\n try {\n if (err) {\n this.logger.error('OAuth callback error:', err);\n throw new BadRequestException('Authentication failed');\n }\n\n if (!user) {\n throw new BadRequestException('Authentication failed');\n }\n\n req.user = user;\n await this.processAuthenticationSuccess(req, res);\n } catch (error) {\n next(error);\n }\n },\n )(req, res, next);\n }\n\n async processAuthenticationSuccess(\n req: OAuthCallbackRequest,\n res: Response,\n ) {\n const user = req.user;\n if (!user) {\n throw new BadRequestException('Authentication failed');\n }\n\n const sessionId = req.cookies?.oauth_session;\n if (!sessionId) {\n throw new BadRequestException('Missing OAuth session');\n }\n\n const session = await this.store.getOAuthSession(sessionId);\n if (!session) {\n throw new BadRequestException('Invalid or expired OAuth session');\n }\n\n // Verify state\n const stateFromCookie = req.cookies?.oauth_state;\n if (session.state !== stateFromCookie) {\n throw new BadRequestException('Invalid state parameter');\n }\n\n // Generate JWT for UI access\n const jwt = this.jwtTokenService.generateUserToken(\n user.profile.username,\n user.profile,\n );\n\n // Set JWT token as cookie for UI endpoints\n res.cookie('auth_token', jwt, {\n httpOnly: true,\n secure: this.isProduction,\n maxAge: this.options.cookieMaxAge,\n });\n\n // Clear temporary cookies\n res.clearCookie('oauth_session');\n res.clearCookie('oauth_state');\n\n // Persist user profile and get stable profile_id\n const user_profile_id = await this.store.upsertUserProfile(\n user.profile,\n user.provider,\n );\n\n // Generate authorization code\n const authCode = randomBytes(32).toString('base64url');\n\n // Store the auth code\n await this.store.storeAuthCode({\n code: authCode,\n user_id: user.profile.username,\n client_id: session.clientId!,\n redirect_uri: session.redirectUri!,\n code_challenge: session.codeChallenge!,\n code_challenge_method: session.codeChallengeMethod!,\n expires_at: Date.now() + this.options.authCodeExpiresIn,\n resource: session.resource,\n scope: session.scope,\n github_access_token: '', // No longer provider-specific\n user_profile_id,\n });\n\n // Build redirect URL with authorization code\n const redirectUrl = new URL(session.redirectUri!);\n redirectUrl.searchParams.set('code', authCode);\n if (session.oauthState) {\n redirectUrl.searchParams.set('state', session.oauthState);\n }\n\n // Clean up session\n await this.store.removeOAuthSession(sessionId);\n\n res.redirect(redirectUrl.toString());\n }\n\n @Post(endpoints.token)\n @Header('content-type', 'application/json')\n @Header('Cache-Control', 'no-store')\n @Header('Pragma', 'no-cache')\n @HttpCode(200)\n async exchangeToken(\n @Body() body: any,\n @Req() req: any,\n ): Promise<TokenPair> {\n // Parse the body to handle both JSON and form-encoded data\n const parsedBody = this.parseRequestBody(body);\n\n const { grant_type, code, code_verifier, redirect_uri, refresh_token } =\n parsedBody;\n\n // Add debugging to help identify issues\n if (!grant_type) {\n this.logger.error('Missing grant_type in request body:', {\n bodyType: typeof body,\n bodyKeys: Object.keys(body || {}),\n parsedBodyKeys: Object.keys(parsedBody),\n contentType: req.headers['content-type'],\n rawBody: body,\n });\n throw new BadRequestException('Missing grant_type parameter');\n }\n\n switch (grant_type) {\n case 'authorization_code': {\n // Extract client credentials based on authentication method\n const clientCredentials = this.extractClientCredentials(\n req,\n parsedBody,\n );\n return await this.handleAuthorizationCodeGrant(\n code,\n code_verifier,\n redirect_uri,\n clientCredentials,\n );\n }\n case 'refresh_token': {\n // For refresh tokens, try to extract client credentials, but allow fallback to token-based extraction\n let clientCredentials: { client_id: string; client_secret?: string };\n try {\n clientCredentials = this.extractClientCredentials(req, parsedBody);\n } catch {\n // If we can't extract credentials, we'll try to get them from the refresh token\n clientCredentials = { client_id: '' }; // Will be filled from token\n }\n return await this.handleRefreshTokenGrant(\n refresh_token,\n clientCredentials,\n );\n }\n default:\n throw new BadRequestException(\n `Unsupported grant_type: ${grant_type}`,\n );\n }\n }\n\n /**\n * Extract client credentials from request based on authentication method\n */\n extractClientCredentials(\n req: any,\n body: any,\n ): { client_id: string; client_secret?: string } {\n // Parse the body using the shared utility function\n const parsedBody = this.parseRequestBody(body);\n\n // Try client_secret_basic first (Authorization header)\n const authHeader = req.headers?.authorization;\n if (authHeader && authHeader.startsWith('Basic ')) {\n const credentials = Buffer.from(authHeader.slice(6), 'base64').toString(\n 'utf-8',\n );\n const [client_id, client_secret] = credentials.split(':', 2);\n if (client_id) {\n return { client_id, client_secret };\n }\n }\n\n // Try client_secret_post (body parameters)\n if (parsedBody.client_id) {\n return {\n client_id: parsedBody.client_id,\n client_secret: parsedBody.client_secret,\n };\n }\n\n throw new BadRequestException('Missing client credentials');\n }\n\n /**\n * Validate client authentication based on the client's configured method\n */\n validateClientAuthentication(\n client: any,\n clientCredentials: { client_id: string; client_secret?: string },\n ): void {\n if (!client) {\n throw new BadRequestException('Invalid client_id');\n }\n\n const { token_endpoint_auth_method } = client;\n\n switch (token_endpoint_auth_method) {\n case 'client_secret_basic':\n case 'client_secret_post':\n if (!clientCredentials.client_secret) {\n throw new BadRequestException(\n 'Client secret required for this authentication method',\n );\n }\n if (client.client_secret !== clientCredentials.client_secret) {\n throw new BadRequestException('Invalid client credentials');\n }\n break;\n\n case 'none':\n // Public client - no secret required\n if (clientCredentials.client_secret) {\n throw new BadRequestException(\n 'Client secret not allowed for public clients',\n );\n }\n break;\n\n default:\n throw new BadRequestException(\n `Unsupported authentication method: ${token_endpoint_auth_method}`,\n );\n }\n }\n\n async handleAuthorizationCodeGrant(\n code: string,\n code_verifier: string,\n _redirect_uri: string,\n clientCredentials: { client_id: string; client_secret?: string },\n ): Promise<TokenPair> {\n this.logger.debug('handleAuthorizationCodeGrant - Params:', {\n code,\n client_id: clientCredentials.client_id,\n });\n\n // Get and validate the authorization code\n const authCode = await this.store.getAuthCode(code);\n if (!authCode) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - Invalid authorization code:',\n code,\n );\n throw new BadRequestException('Invalid authorization code');\n }\n if (authCode.expires_at < Date.now()) {\n await this.store.removeAuthCode(code);\n this.logger.error(\n 'handleAuthorizationCodeGrant - Authorization code expired:',\n code,\n );\n throw new BadRequestException('Authorization code has expired');\n }\n if (authCode.client_id !== clientCredentials.client_id) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - Client ID mismatch:',\n { expected: authCode.client_id, got: clientCredentials.client_id },\n );\n throw new BadRequestException('Client ID mismatch');\n }\n\n // Get client and validate authentication\n const client = await this.clientService.getClient(\n clientCredentials.client_id,\n );\n this.validateClientAuthentication(client, clientCredentials);\n if (authCode.code_challenge) {\n const isValid = this.validatePKCE(\n code_verifier,\n authCode.code_challenge,\n authCode.code_challenge_method,\n );\n if (!isValid) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - Invalid PKCE verification',\n );\n throw new BadRequestException('Invalid PKCE verification');\n }\n }\n if (!authCode.resource) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - No resource associated with code',\n );\n throw new BadRequestException(\n 'Authorization code is not associated with a resource',\n );\n }\n\n let userData: any | undefined = undefined;\n if (authCode.user_profile_id) {\n try {\n const profile = await this.store.getUserProfileById(\n authCode.user_profile_id,\n );\n if (profile) {\n // Avoid circular/large raw payloads if present\n userData = { ...profile };\n }\n } catch (e) {\n this.logger.warn('Failed to load user profile for token payload', e);\n }\n }\n\n const tokens = this.jwtTokenService.generateTokenPair(\n authCode.user_id,\n clientCredentials.client_id,\n authCode.scope,\n authCode.resource,\n {\n user_profile_id: authCode.user_profile_id,\n user_data: userData,\n },\n );\n await this.store.removeAuthCode(code);\n this.logger.log(\n 'handleAuthorizationCodeGrant - Token pair generated for user:',\n authCode.user_id,\n );\n return tokens;\n }\n\n async handleRefreshTokenGrant(\n refresh_token: string,\n clientCredentials: { client_id: string; client_secret?: string },\n ): Promise<TokenPair> {\n // Verify the refresh token first to get client_id from token if not provided\n const payload = this.jwtTokenService.validateToken(refresh_token);\n if (!payload || payload.type !== 'refresh') {\n throw new BadRequestException('Invalid or expired refresh token');\n }\n\n // Use client_id from token if not provided in credentials\n const clientId = clientCredentials.client_id || payload.client_id;\n if (!clientId) {\n throw new BadRequestException('Unable to determine client_id');\n }\n\n // Get client and validate authentication\n const client = await this.clientService.getClient(clientId);\n\n // For refresh token grants, we can be more lenient with client authentication\n // if the token already contains the client_id and the client is public\n if (client?.token_endpoint_auth_method !== 'none') {\n this.validateClientAuthentication(client, {\n ...clientCredentials,\n client_id: clientId,\n });\n }\n\n // Verify the refresh token belongs to the client\n if (payload.client_id !== clientId) {\n throw new BadRequestException(\n 'Invalid refresh token or token does not belong to this client',\n );\n }\n\n let newTokens: TokenPair | null = null;\n try {\n const payload = this.jwtTokenService.validateToken(refresh_token);\n if (!payload || payload.type !== 'refresh') {\n throw new BadRequestException('Invalid or expired refresh token');\n }\n\n let userData: any | undefined = undefined;\n if (payload.user_profile_id) {\n try {\n const profile = await this.store.getUserProfileById(\n payload.user_profile_id,\n );\n if (profile) userData = { ...profile };\n } catch (e) {\n this.logger.warn(\n 'Failed to load user profile for refreshed token payload',\n e,\n );\n }\n }\n\n newTokens = this.jwtTokenService.generateTokenPair(\n payload.sub,\n clientId,\n payload.scope,\n payload.resource,\n {\n user_profile_id: payload.user_profile_id,\n user_data: userData,\n },\n );\n } catch (e) {\n this.logger.warn(\n 'Refresh flow failed using enriched path, fallback',\n e,\n );\n newTokens = this.jwtTokenService.refreshAccessToken(refresh_token);\n }\n\n if (!newTokens) throw new BadRequestException('Failed to refresh token');\n return newTokens;\n }\n\n validatePKCE(\n code_verifier: string,\n code_challenge: string,\n method: string,\n ): boolean {\n if (method === 'plain') {\n return code_verifier === code_challenge;\n } else if (method === 'S256') {\n const hash = createHash('sha256')\n .update(code_verifier)\n .digest('base64url');\n return hash === code_challenge;\n }\n return false;\n }\n\n /**\n * Utility function to parse form-encoded or JSON bodies\n * Handles both string (raw form data) and object bodies\n */\n parseRequestBody(body: any): Record<string, any> {\n // If body is already a parsed object with properties, return it\n if (body && typeof body === 'object' && Object.keys(body).length > 0) {\n return body;\n }\n\n // If body is a string (raw form data), parse it\n if (typeof body === 'string' && body.length > 0) {\n const params = new URLSearchParams(body);\n const parsedBody: Record<string, any> = {};\n for (const [key, value] of params.entries()) {\n parsedBody[key] = value;\n }\n return parsedBody;\n }\n\n // Return empty object if no valid body\n return {};\n }\n }\n\n return McpOAuthController;\n}\n"]}
1
+ {"version":3,"file":"mcp-oauth.controller.js","sourceRoot":"","sources":["../../src/authz/mcp-oauth.controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BadRequestException,\n Body,\n Controller,\n Get,\n Header,\n HttpCode,\n Inject,\n Logger,\n Next,\n Post,\n Query,\n Req,\n Res,\n} from '@nestjs/common';\nimport { createHash, randomBytes } from 'crypto';\nimport { Request as ExpressRequest, NextFunction, Response } from 'express';\nimport passport from 'passport';\nimport { normalizeEndpoint } from '../mcp/utils/normalize-endpoint';\nimport {\n OAuthEndpointConfiguration,\n OAuthModuleOptions,\n OAuthSession,\n OAuthUserProfile,\n} from './providers/oauth-provider.interface';\nimport { ClientService } from './services/client.service';\nimport { JwtTokenService, TokenPair } from './services/jwt-token.service';\nimport { STRATEGY_NAME } from './services/oauth-strategy.service';\nimport { IOAuthStore } from './stores/oauth-store.interface';\n\ninterface OAuthCallbackRequest extends ExpressRequest {\n user?: {\n profile: OAuthUserProfile;\n accessToken: string;\n provider: string;\n };\n}\n\n// Add this interface to properly type the Express request with raw body\ninterface RequestWithRawBody extends ExpressRequest {\n rawBody?: Buffer;\n textBody?: string;\n}\n\nexport function createMcpOAuthController(\n endpoints: OAuthEndpointConfiguration = {},\n options?: {\n disableWellKnownProtectedResourceMetadata?: boolean;\n disableWellKnownAuthorizationServerMetadata?: boolean;\n },\n) {\n // Optional decorator helpers\n const OptionalGet = (\n path: string | string[] | undefined,\n enabled: boolean,\n ): MethodDecorator => {\n return enabled && path\n ? (Get as unknown as (p?: any) => MethodDecorator)(path)\n : ((() => {}) as unknown as MethodDecorator);\n };\n const OptionalHeader = (\n name: string,\n value: string,\n enabled: boolean,\n ): MethodDecorator => {\n return enabled\n ? (Header as unknown as (n: string, v: string) => MethodDecorator)(\n name,\n value,\n )\n : ((() => {}) as unknown as MethodDecorator);\n };\n\n @Controller()\n class McpOAuthController {\n readonly logger = new Logger(McpOAuthController.name);\n readonly serverUrl: string;\n readonly isProduction: boolean;\n readonly options: OAuthModuleOptions;\n\n constructor(\n @Inject('OAUTH_MODULE_OPTIONS') options: OAuthModuleOptions,\n @Inject('IOAuthStore') readonly store: IOAuthStore,\n readonly jwtTokenService: JwtTokenService,\n readonly clientService: ClientService,\n ) {\n this.serverUrl = options.serverUrl;\n this.isProduction = options.cookieSecure;\n this.options = options;\n }\n\n /**\n * Utility function to parse form-encoded or JSON bodies\n * Handles both string (raw form data) and object bodies\n */\n parseRequestBody(body: any, req?: RequestWithRawBody): Record<string, any> {\n // If body is already a parsed object with properties, return it\n if (body && typeof body === 'object' && Object.keys(body).length > 0) {\n return body;\n }\n\n // If body is a string (raw form data), parse it\n if (typeof body === 'string' && body.length > 0) {\n const params = new URLSearchParams(body);\n const parsedBody: Record<string, any> = {};\n for (const [key, value] of params.entries()) {\n parsedBody[key] = value;\n }\n return parsedBody;\n }\n\n // Check if we have a text body stored on the request (from our middleware)\n if (req?.textBody) {\n const params = new URLSearchParams(req.textBody);\n const parsedBody: Record<string, any> = {};\n for (const [key, value] of params.entries()) {\n parsedBody[key] = value;\n }\n return parsedBody;\n }\n\n // Check if we have a raw body buffer stored on the request\n if (req?.rawBody) {\n const bodyString = req.rawBody.toString('utf-8');\n if (bodyString) {\n const params = new URLSearchParams(bodyString);\n const parsedBody: Record<string, any> = {};\n for (const [key, value] of params.entries()) {\n parsedBody[key] = value;\n }\n return parsedBody;\n }\n }\n\n // Return empty object if no valid body\n return {};\n }\n\n /**\n * Middleware to capture raw body for form-encoded requests\n * This is needed when bodyParser is disabled in the main app\n */\n captureRawBody(req: RequestWithRawBody, res: Response, next: NextFunction) {\n if (\n req.headers['content-type']?.includes(\n 'application/x-www-form-urlencoded',\n )\n ) {\n let rawBody = '';\n\n req.on('data', (chunk: Buffer) => {\n rawBody += chunk.toString('utf-8');\n });\n\n req.on('end', () => {\n req.textBody = rawBody;\n // Also parse and set it as body for NestJS\n if (rawBody) {\n const params = new URLSearchParams(rawBody);\n const parsedBody: any = {};\n for (const [key, value] of params.entries()) {\n parsedBody[key] = value;\n }\n (req as any).body = parsedBody;\n }\n next();\n });\n\n req.on('error', (err) => {\n this.logger.error('Error reading request body:', err);\n next(err);\n });\n } else {\n next();\n }\n }\n\n @OptionalGet(\n endpoints.wellKnownProtectedResourceMetadata,\n !options?.disableWellKnownProtectedResourceMetadata,\n )\n @OptionalHeader(\n 'content-type',\n 'application/json',\n !options?.disableWellKnownProtectedResourceMetadata,\n )\n getProtectedResourceMetadata() {\n // The issuer URL of your authorization server.\n const authorizationServerIssuer = this.options.jwtIssuer;\n\n // The canonical URI of the MCP server resource itself.\n const resourceIdentifier = this.options.resource;\n\n const metadata = {\n /**\n * REQUIRED by MCP Spec.\n * A list of authorization server issuer URLs that can issue tokens for this resource.\n */\n authorization_servers: [authorizationServerIssuer],\n\n /**\n * RECOMMENDED by RFC 9728.\n * The identifier for this resource server.\n */\n resource: resourceIdentifier,\n\n /**\n * RECOMMENDED by RFC 9728.\n * A list of scopes that this resource server understands.\n */\n scopes_supported:\n this.options.protectedResourceMetadata.scopesSupported,\n\n /**\n * RECOMMENDED by RFC 9728.\n * A list of methods clients can use to present the access token.\n */\n bearer_methods_supported:\n this.options.protectedResourceMetadata.bearerMethodsSupported,\n\n /**\n * OPTIONAL but helpful custom metadata.\n * Declares which version of the MCP spec this server supports.\n */\n mcp_versions_supported:\n this.options.protectedResourceMetadata.mcpVersionsSupported,\n };\n\n return metadata;\n }\n\n // OAuth endpoints\n @OptionalGet(\n endpoints.wellKnownAuthorizationServerMetadata,\n !options?.disableWellKnownAuthorizationServerMetadata,\n )\n @OptionalHeader(\n 'content-type',\n 'application/json',\n !options?.disableWellKnownAuthorizationServerMetadata,\n )\n getAuthorizationServerMetadata() {\n return {\n issuer: this.serverUrl,\n authorization_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints.authorize}`,\n ),\n token_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints.token}`,\n ),\n registration_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints.register}`,\n ),\n response_types_supported:\n this.options.authorizationServerMetadata.responseTypesSupported,\n response_modes_supported:\n this.options.authorizationServerMetadata.responseModesSupported,\n grant_types_supported:\n this.options.authorizationServerMetadata.grantTypesSupported,\n token_endpoint_auth_methods_supported:\n this.options.authorizationServerMetadata\n .tokenEndpointAuthMethodsSupported,\n scopes_supported:\n this.options.authorizationServerMetadata.scopesSupported,\n revocation_endpoint: normalizeEndpoint(\n `${this.serverUrl}/${endpoints?.revoke}`,\n ),\n code_challenge_methods_supported:\n this.options.authorizationServerMetadata\n .codeChallengeMethodsSupported,\n };\n }\n\n @Post(endpoints.register)\n async registerClient(@Body() registrationDto: any) {\n return await this.clientService.registerClient(registrationDto);\n }\n\n @Get(endpoints.authorize)\n async authorize(\n @Query() query: any,\n @Req()\n req: any,\n @Res() res: Response,\n @Next() next: NextFunction,\n ) {\n const {\n response_type,\n client_id,\n redirect_uri,\n code_challenge,\n code_challenge_method,\n state,\n scope,\n } = query;\n const resource = this.options.resource;\n if (response_type !== 'code') {\n throw new BadRequestException('Only response_type=code is supported');\n }\n\n if (!client_id) {\n throw new BadRequestException('Missing required parameters');\n }\n\n // Validate client and redirect URI\n const client = await this.clientService.getClient(client_id);\n if (!client) {\n throw new BadRequestException('Invalid client_id');\n }\n\n const validRedirect = await this.clientService.validateRedirectUri(\n client_id,\n redirect_uri,\n );\n if (!validRedirect) {\n throw new BadRequestException('Invalid redirect_uri');\n }\n\n // Create OAuth session\n const sessionId = randomBytes(32).toString('base64url');\n const sessionState = randomBytes(32).toString('base64url');\n\n const oauthSession: OAuthSession = {\n sessionId,\n state: sessionState,\n clientId: client_id,\n redirectUri: redirect_uri,\n codeChallenge: code_challenge,\n codeChallengeMethod: code_challenge_method || 'plain',\n oauthState: state,\n scope: scope,\n resource,\n expiresAt: Date.now() + this.options.oauthSessionExpiresIn,\n };\n\n await this.store.storeOAuthSession(sessionId, oauthSession);\n\n // Set session cookie\n res.cookie('oauth_session', sessionId, {\n httpOnly: true,\n secure: this.isProduction,\n maxAge: this.options.oauthSessionExpiresIn,\n });\n\n // Store state for passport\n res.cookie('oauth_state', sessionState, {\n httpOnly: true,\n secure: this.isProduction,\n maxAge: this.options.oauthSessionExpiresIn,\n });\n\n // Redirect to the provider's auth endpoint\n passport.authenticate(STRATEGY_NAME, {\n state: req.cookies?.oauth_state,\n })(req, res, next);\n }\n\n @Get(endpoints.callback)\n handleProviderCallback(\n @Req() req: OAuthCallbackRequest,\n @Res() res: Response,\n @Next() next: NextFunction,\n ) {\n // Use a custom callback to handle the authentication result\n passport.authenticate(\n STRATEGY_NAME,\n { session: false },\n async (err: any, user: any) => {\n try {\n if (err) {\n this.logger.error('OAuth callback error:', err);\n throw new BadRequestException('Authentication failed');\n }\n\n if (!user) {\n throw new BadRequestException('Authentication failed');\n }\n\n req.user = user;\n await this.processAuthenticationSuccess(req, res);\n } catch (error) {\n next(error);\n }\n },\n )(req, res, next);\n }\n\n async processAuthenticationSuccess(\n req: OAuthCallbackRequest,\n res: Response,\n ) {\n const user = req.user;\n if (!user) {\n throw new BadRequestException('Authentication failed');\n }\n\n const sessionId = req.cookies?.oauth_session;\n if (!sessionId) {\n throw new BadRequestException('Missing OAuth session');\n }\n\n const session = await this.store.getOAuthSession(sessionId);\n if (!session) {\n throw new BadRequestException('Invalid or expired OAuth session');\n }\n\n // Verify state\n const stateFromCookie = req.cookies?.oauth_state;\n if (session.state !== stateFromCookie) {\n throw new BadRequestException('Invalid state parameter');\n }\n\n // Generate JWT for UI access\n const jwt = this.jwtTokenService.generateUserToken(\n user.profile.username,\n user.profile,\n );\n\n // Set JWT token as cookie for UI endpoints\n res.cookie('auth_token', jwt, {\n httpOnly: true,\n secure: this.isProduction,\n maxAge: this.options.cookieMaxAge,\n });\n\n // Clear temporary cookies\n res.clearCookie('oauth_session');\n res.clearCookie('oauth_state');\n\n // Persist user profile and get stable profile_id\n const user_profile_id = await this.store.upsertUserProfile(\n user.profile,\n user.provider,\n );\n\n // Generate authorization code\n const authCode = randomBytes(32).toString('base64url');\n\n // Store the auth code\n await this.store.storeAuthCode({\n code: authCode,\n user_id: user.profile.username,\n client_id: session.clientId!,\n redirect_uri: session.redirectUri!,\n code_challenge: session.codeChallenge!,\n code_challenge_method: session.codeChallengeMethod!,\n expires_at: Date.now() + this.options.authCodeExpiresIn,\n resource: session.resource,\n scope: session.scope,\n github_access_token: '', // No longer provider-specific\n user_profile_id,\n });\n\n // Build redirect URL with authorization code\n const redirectUrl = new URL(session.redirectUri!);\n redirectUrl.searchParams.set('code', authCode);\n if (session.oauthState) {\n redirectUrl.searchParams.set('state', session.oauthState);\n }\n\n // Clean up session\n await this.store.removeOAuthSession(sessionId);\n\n res.redirect(redirectUrl.toString());\n }\n\n @Post(endpoints.token)\n @Header('content-type', 'application/json')\n @Header('Cache-Control', 'no-store')\n @Header('Pragma', 'no-cache')\n @HttpCode(200)\n async exchangeToken(\n @Body() body: any,\n @Req() req: RequestWithRawBody,\n @Res({ passthrough: true }) res: Response,\n ): Promise<TokenPair> {\n // Apply middleware to capture raw body if needed\n const isFormUrlEncoded = req.headers['content-type']?.includes(\n 'application/x-www-form-urlencoded',\n );\n const isBodyEmpty =\n !body ||\n (typeof body === 'object' &&\n Object.keys(body as Record<string, unknown>).length === 0);\n\n if (isFormUrlEncoded && isBodyEmpty) {\n return new Promise((resolve, reject) => {\n this.captureRawBody(req, res, (err?: any) => {\n if (err) {\n reject(\n err instanceof Error ? err : new Error(String(err ?? 'error')),\n );\n return;\n }\n\n // Avoid returning a Promise from the callback; use an IIFE\n void (async () => {\n try {\n // Re-parse the body after middleware has captured it\n const parsedBody = this.parseRequestBody(req.body || body, req);\n const result = await this.processTokenExchange(parsedBody, req);\n resolve(result);\n } catch (error) {\n reject(\n error instanceof Error\n ? error\n : new Error(String(error ?? 'error')),\n );\n }\n })();\n });\n });\n }\n\n // Body is already parsed, process directly\n const parsedBody = this.parseRequestBody(body, req);\n return this.processTokenExchange(parsedBody, req);\n }\n\n async processTokenExchange(\n parsedBody: Record<string, any>,\n req: RequestWithRawBody,\n ): Promise<TokenPair> {\n const { grant_type, code, code_verifier, redirect_uri, refresh_token } =\n parsedBody;\n\n // Add debugging to help identify issues\n if (!grant_type) {\n this.logger.error('Missing grant_type in request body:', {\n parsedBodyKeys: Object.keys(parsedBody),\n contentType: req.headers['content-type'],\n textBody: req.textBody,\n parsedBody,\n });\n throw new BadRequestException('Missing grant_type parameter');\n }\n\n switch (grant_type) {\n case 'authorization_code': {\n // Extract client credentials based on authentication method\n const clientCredentials = this.extractClientCredentials(\n req,\n parsedBody,\n );\n return await this.handleAuthorizationCodeGrant(\n typeof code === 'string' ? code : String(code ?? ''),\n typeof code_verifier === 'string'\n ? code_verifier\n : String(code_verifier ?? ''),\n typeof redirect_uri === 'string'\n ? redirect_uri\n : String(redirect_uri ?? ''),\n clientCredentials,\n );\n }\n case 'refresh_token': {\n // For refresh tokens, try to extract client credentials, but allow fallback to token-based extraction\n let clientCredentials: { client_id: string; client_secret?: string };\n try {\n clientCredentials = this.extractClientCredentials(req, parsedBody);\n } catch {\n // If we can't extract credentials, we'll try to get them from the refresh token\n clientCredentials = { client_id: '' }; // Will be filled from token\n }\n return await this.handleRefreshTokenGrant(\n typeof refresh_token === 'string'\n ? refresh_token\n : String(refresh_token ?? ''),\n clientCredentials,\n );\n }\n default:\n throw new BadRequestException(\n `Unsupported grant_type: ${grant_type}`,\n );\n }\n }\n\n /**\n * Extract client credentials from request based on authentication method\n */\n extractClientCredentials(\n req: RequestWithRawBody,\n body: any,\n ): { client_id: string; client_secret?: string } {\n // Parse the body using the shared utility function\n const parsedBody = this.parseRequestBody(body, req);\n\n // Try client_secret_basic first (Authorization header)\n const authHeader = req.headers?.authorization;\n if (authHeader && authHeader.startsWith('Basic ')) {\n const credentials = Buffer.from(authHeader.slice(6), 'base64').toString(\n 'utf-8',\n );\n const [client_id, client_secret] = credentials.split(':', 2);\n if (client_id) {\n return { client_id, client_secret };\n }\n }\n\n // Try client_secret_post (body parameters)\n if (parsedBody.client_id) {\n return {\n client_id: parsedBody.client_id,\n client_secret: parsedBody.client_secret,\n };\n }\n\n throw new BadRequestException('Missing client credentials');\n }\n\n /**\n * Validate client authentication based on the client's configured method\n */\n validateClientAuthentication(\n client: any,\n clientCredentials: { client_id: string; client_secret?: string },\n ): void {\n if (!client) {\n throw new BadRequestException('Invalid client_id');\n }\n\n const { token_endpoint_auth_method } = client;\n\n switch (token_endpoint_auth_method) {\n case 'client_secret_basic':\n case 'client_secret_post':\n if (!clientCredentials.client_secret) {\n throw new BadRequestException(\n 'Client secret required for this authentication method',\n );\n }\n if (client.client_secret !== clientCredentials.client_secret) {\n throw new BadRequestException('Invalid client credentials');\n }\n break;\n\n case 'none':\n // Public client - no secret required\n if (clientCredentials.client_secret) {\n throw new BadRequestException(\n 'Client secret not allowed for public clients',\n );\n }\n break;\n\n default:\n throw new BadRequestException(\n `Unsupported authentication method: ${token_endpoint_auth_method}`,\n );\n }\n }\n\n async handleAuthorizationCodeGrant(\n code: string,\n code_verifier: string,\n _redirect_uri: string,\n clientCredentials: { client_id: string; client_secret?: string },\n ): Promise<TokenPair> {\n this.logger.debug('handleAuthorizationCodeGrant - Params:', {\n code,\n client_id: clientCredentials.client_id,\n });\n\n // Get and validate the authorization code\n const authCode = await this.store.getAuthCode(code);\n if (!authCode) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - Invalid authorization code:',\n code,\n );\n throw new BadRequestException('Invalid authorization code');\n }\n if (authCode.expires_at < Date.now()) {\n await this.store.removeAuthCode(code);\n this.logger.error(\n 'handleAuthorizationCodeGrant - Authorization code expired:',\n code,\n );\n throw new BadRequestException('Authorization code has expired');\n }\n if (authCode.client_id !== clientCredentials.client_id) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - Client ID mismatch:',\n { expected: authCode.client_id, got: clientCredentials.client_id },\n );\n throw new BadRequestException('Client ID mismatch');\n }\n\n // Get client and validate authentication\n const client = await this.clientService.getClient(\n clientCredentials.client_id,\n );\n this.validateClientAuthentication(client, clientCredentials);\n if (authCode.code_challenge) {\n const isValid = this.validatePKCE(\n code_verifier,\n authCode.code_challenge,\n authCode.code_challenge_method,\n );\n if (!isValid) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - Invalid PKCE verification',\n );\n throw new BadRequestException('Invalid PKCE verification');\n }\n }\n if (!authCode.resource) {\n this.logger.error(\n 'handleAuthorizationCodeGrant - No resource associated with code',\n );\n throw new BadRequestException(\n 'Authorization code is not associated with a resource',\n );\n }\n\n let userData: Record<string, unknown> | undefined = undefined;\n if (authCode.user_profile_id) {\n try {\n const profile = await this.store.getUserProfileById(\n authCode.user_profile_id,\n );\n if (profile) {\n // Avoid circular/large raw payloads if present\n userData = { ...profile };\n }\n } catch (e) {\n this.logger.warn('Failed to load user profile for token payload', e);\n }\n }\n\n const tokens = this.jwtTokenService.generateTokenPair(\n authCode.user_id,\n clientCredentials.client_id,\n authCode.scope,\n authCode.resource,\n {\n user_profile_id: authCode.user_profile_id,\n user_data: userData,\n },\n );\n await this.store.removeAuthCode(code);\n this.logger.debug(\n 'handleAuthorizationCodeGrant - Token pair generated for user:',\n authCode.user_id,\n );\n return tokens;\n }\n\n async handleRefreshTokenGrant(\n refresh_token: string,\n clientCredentials: { client_id: string; client_secret?: string },\n ): Promise<TokenPair> {\n // Verify the refresh token first to get client_id from token if not provided\n const payload = this.jwtTokenService.validateToken(refresh_token);\n if (!payload || payload.type !== 'refresh') {\n throw new BadRequestException('Invalid or expired refresh token');\n }\n\n // Use client_id from token if not provided in credentials\n const clientId = clientCredentials.client_id || payload.client_id;\n if (!clientId) {\n throw new BadRequestException('Unable to determine client_id');\n }\n\n // Get client and validate authentication\n const client = await this.clientService.getClient(clientId);\n\n // For refresh token grants, we can be more lenient with client authentication\n // if the token already contains the client_id and the client is public\n if (client?.token_endpoint_auth_method !== 'none') {\n this.validateClientAuthentication(client, {\n ...clientCredentials,\n client_id: clientId,\n });\n }\n\n // Verify the refresh token belongs to the client\n if (payload.client_id !== clientId) {\n throw new BadRequestException(\n 'Invalid refresh token or token does not belong to this client',\n );\n }\n\n let newTokens: TokenPair | null = null;\n try {\n const payload = this.jwtTokenService.validateToken(refresh_token);\n if (!payload || payload.type !== 'refresh') {\n throw new BadRequestException('Invalid or expired refresh token');\n }\n\n let userData: Record<string, unknown> | undefined = undefined;\n if (payload.user_profile_id) {\n try {\n const profile = await this.store.getUserProfileById(\n payload.user_profile_id,\n );\n if (profile) userData = { ...profile };\n } catch (e) {\n this.logger.warn(\n 'Failed to load user profile for refreshed token payload',\n e,\n );\n }\n }\n\n newTokens = this.jwtTokenService.generateTokenPair(\n payload.sub,\n clientId,\n payload.scope,\n payload.resource,\n {\n user_profile_id: payload.user_profile_id,\n user_data: userData,\n },\n );\n } catch (e) {\n this.logger.warn(\n 'Refresh flow failed using enriched path, fallback',\n e,\n );\n newTokens = this.jwtTokenService.refreshAccessToken(refresh_token);\n }\n\n if (!newTokens) throw new BadRequestException('Failed to refresh token');\n return newTokens;\n }\n\n validatePKCE(\n code_verifier: string,\n code_challenge: string,\n method: string,\n ): boolean {\n if (method === 'plain') {\n return code_verifier === code_challenge;\n } else if (method === 'S256') {\n const hash = createHash('sha256')\n .update(code_verifier)\n .digest('base64url');\n return hash === code_challenge;\n }\n return false;\n }\n }\n\n return McpOAuthController;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rekog/mcp-nest",
3
- "version": "1.7.4-alpha.13",
3
+ "version": "1.7.4-alpha.15",
4
4
  "description": "NestJS module for creating Model Context Protocol (MCP) servers",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",
@@ -36,6 +36,12 @@ interface OAuthCallbackRequest extends ExpressRequest {
36
36
  };
37
37
  }
38
38
 
39
+ // Add this interface to properly type the Express request with raw body
40
+ interface RequestWithRawBody extends ExpressRequest {
41
+ rawBody?: Buffer;
42
+ textBody?: string;
43
+ }
44
+
39
45
  export function createMcpOAuthController(
40
46
  endpoints: OAuthEndpointConfiguration = {},
41
47
  options?: {
@@ -64,12 +70,14 @@ export function createMcpOAuthController(
64
70
  )
65
71
  : ((() => {}) as unknown as MethodDecorator);
66
72
  };
73
+
67
74
  @Controller()
68
75
  class McpOAuthController {
69
76
  readonly logger = new Logger(McpOAuthController.name);
70
77
  readonly serverUrl: string;
71
78
  readonly isProduction: boolean;
72
79
  readonly options: OAuthModuleOptions;
80
+
73
81
  constructor(
74
82
  @Inject('OAUTH_MODULE_OPTIONS') options: OAuthModuleOptions,
75
83
  @Inject('IOAuthStore') readonly store: IOAuthStore,
@@ -81,6 +89,92 @@ export function createMcpOAuthController(
81
89
  this.options = options;
82
90
  }
83
91
 
92
+ /**
93
+ * Utility function to parse form-encoded or JSON bodies
94
+ * Handles both string (raw form data) and object bodies
95
+ */
96
+ parseRequestBody(body: any, req?: RequestWithRawBody): Record<string, any> {
97
+ // If body is already a parsed object with properties, return it
98
+ if (body && typeof body === 'object' && Object.keys(body).length > 0) {
99
+ return body;
100
+ }
101
+
102
+ // If body is a string (raw form data), parse it
103
+ if (typeof body === 'string' && body.length > 0) {
104
+ const params = new URLSearchParams(body);
105
+ const parsedBody: Record<string, any> = {};
106
+ for (const [key, value] of params.entries()) {
107
+ parsedBody[key] = value;
108
+ }
109
+ return parsedBody;
110
+ }
111
+
112
+ // Check if we have a text body stored on the request (from our middleware)
113
+ if (req?.textBody) {
114
+ const params = new URLSearchParams(req.textBody);
115
+ const parsedBody: Record<string, any> = {};
116
+ for (const [key, value] of params.entries()) {
117
+ parsedBody[key] = value;
118
+ }
119
+ return parsedBody;
120
+ }
121
+
122
+ // Check if we have a raw body buffer stored on the request
123
+ if (req?.rawBody) {
124
+ const bodyString = req.rawBody.toString('utf-8');
125
+ if (bodyString) {
126
+ const params = new URLSearchParams(bodyString);
127
+ const parsedBody: Record<string, any> = {};
128
+ for (const [key, value] of params.entries()) {
129
+ parsedBody[key] = value;
130
+ }
131
+ return parsedBody;
132
+ }
133
+ }
134
+
135
+ // Return empty object if no valid body
136
+ return {};
137
+ }
138
+
139
+ /**
140
+ * Middleware to capture raw body for form-encoded requests
141
+ * This is needed when bodyParser is disabled in the main app
142
+ */
143
+ captureRawBody(req: RequestWithRawBody, res: Response, next: NextFunction) {
144
+ if (
145
+ req.headers['content-type']?.includes(
146
+ 'application/x-www-form-urlencoded',
147
+ )
148
+ ) {
149
+ let rawBody = '';
150
+
151
+ req.on('data', (chunk: Buffer) => {
152
+ rawBody += chunk.toString('utf-8');
153
+ });
154
+
155
+ req.on('end', () => {
156
+ req.textBody = rawBody;
157
+ // Also parse and set it as body for NestJS
158
+ if (rawBody) {
159
+ const params = new URLSearchParams(rawBody);
160
+ const parsedBody: any = {};
161
+ for (const [key, value] of params.entries()) {
162
+ parsedBody[key] = value;
163
+ }
164
+ (req as any).body = parsedBody;
165
+ }
166
+ next();
167
+ });
168
+
169
+ req.on('error', (err) => {
170
+ this.logger.error('Error reading request body:', err);
171
+ next(err);
172
+ });
173
+ } else {
174
+ next();
175
+ }
176
+ }
177
+
84
178
  @OptionalGet(
85
179
  endpoints.wellKnownProtectedResourceMetadata,
86
180
  !options?.disableWellKnownProtectedResourceMetadata,
@@ -377,22 +471,66 @@ export function createMcpOAuthController(
377
471
  @HttpCode(200)
378
472
  async exchangeToken(
379
473
  @Body() body: any,
380
- @Req() req: any,
474
+ @Req() req: RequestWithRawBody,
475
+ @Res({ passthrough: true }) res: Response,
381
476
  ): Promise<TokenPair> {
382
- // Parse the body to handle both JSON and form-encoded data
383
- const parsedBody = this.parseRequestBody(body);
477
+ // Apply middleware to capture raw body if needed
478
+ const isFormUrlEncoded = req.headers['content-type']?.includes(
479
+ 'application/x-www-form-urlencoded',
480
+ );
481
+ const isBodyEmpty =
482
+ !body ||
483
+ (typeof body === 'object' &&
484
+ Object.keys(body as Record<string, unknown>).length === 0);
485
+
486
+ if (isFormUrlEncoded && isBodyEmpty) {
487
+ return new Promise((resolve, reject) => {
488
+ this.captureRawBody(req, res, (err?: any) => {
489
+ if (err) {
490
+ reject(
491
+ err instanceof Error ? err : new Error(String(err ?? 'error')),
492
+ );
493
+ return;
494
+ }
495
+
496
+ // Avoid returning a Promise from the callback; use an IIFE
497
+ void (async () => {
498
+ try {
499
+ // Re-parse the body after middleware has captured it
500
+ const parsedBody = this.parseRequestBody(req.body || body, req);
501
+ const result = await this.processTokenExchange(parsedBody, req);
502
+ resolve(result);
503
+ } catch (error) {
504
+ reject(
505
+ error instanceof Error
506
+ ? error
507
+ : new Error(String(error ?? 'error')),
508
+ );
509
+ }
510
+ })();
511
+ });
512
+ });
513
+ }
514
+
515
+ // Body is already parsed, process directly
516
+ const parsedBody = this.parseRequestBody(body, req);
517
+ return this.processTokenExchange(parsedBody, req);
518
+ }
384
519
 
520
+ async processTokenExchange(
521
+ parsedBody: Record<string, any>,
522
+ req: RequestWithRawBody,
523
+ ): Promise<TokenPair> {
385
524
  const { grant_type, code, code_verifier, redirect_uri, refresh_token } =
386
525
  parsedBody;
387
526
 
388
527
  // Add debugging to help identify issues
389
528
  if (!grant_type) {
390
529
  this.logger.error('Missing grant_type in request body:', {
391
- bodyType: typeof body,
392
- bodyKeys: Object.keys(body || {}),
393
530
  parsedBodyKeys: Object.keys(parsedBody),
394
531
  contentType: req.headers['content-type'],
395
- rawBody: body,
532
+ textBody: req.textBody,
533
+ parsedBody,
396
534
  });
397
535
  throw new BadRequestException('Missing grant_type parameter');
398
536
  }
@@ -405,9 +543,13 @@ export function createMcpOAuthController(
405
543
  parsedBody,
406
544
  );
407
545
  return await this.handleAuthorizationCodeGrant(
408
- code,
409
- code_verifier,
410
- redirect_uri,
546
+ typeof code === 'string' ? code : String(code ?? ''),
547
+ typeof code_verifier === 'string'
548
+ ? code_verifier
549
+ : String(code_verifier ?? ''),
550
+ typeof redirect_uri === 'string'
551
+ ? redirect_uri
552
+ : String(redirect_uri ?? ''),
411
553
  clientCredentials,
412
554
  );
413
555
  }
@@ -421,7 +563,9 @@ export function createMcpOAuthController(
421
563
  clientCredentials = { client_id: '' }; // Will be filled from token
422
564
  }
423
565
  return await this.handleRefreshTokenGrant(
424
- refresh_token,
566
+ typeof refresh_token === 'string'
567
+ ? refresh_token
568
+ : String(refresh_token ?? ''),
425
569
  clientCredentials,
426
570
  );
427
571
  }
@@ -436,11 +580,11 @@ export function createMcpOAuthController(
436
580
  * Extract client credentials from request based on authentication method
437
581
  */
438
582
  extractClientCredentials(
439
- req: any,
583
+ req: RequestWithRawBody,
440
584
  body: any,
441
585
  ): { client_id: string; client_secret?: string } {
442
586
  // Parse the body using the shared utility function
443
- const parsedBody = this.parseRequestBody(body);
587
+ const parsedBody = this.parseRequestBody(body, req);
444
588
 
445
589
  // Try client_secret_basic first (Authorization header)
446
590
  const authHeader = req.headers?.authorization;
@@ -570,7 +714,7 @@ export function createMcpOAuthController(
570
714
  );
571
715
  }
572
716
 
573
- let userData: any | undefined = undefined;
717
+ let userData: Record<string, unknown> | undefined = undefined;
574
718
  if (authCode.user_profile_id) {
575
719
  try {
576
720
  const profile = await this.store.getUserProfileById(
@@ -596,7 +740,7 @@ export function createMcpOAuthController(
596
740
  },
597
741
  );
598
742
  await this.store.removeAuthCode(code);
599
- this.logger.log(
743
+ this.logger.debug(
600
744
  'handleAuthorizationCodeGrant - Token pair generated for user:',
601
745
  authCode.user_id,
602
746
  );
@@ -645,7 +789,7 @@ export function createMcpOAuthController(
645
789
  throw new BadRequestException('Invalid or expired refresh token');
646
790
  }
647
791
 
648
- let userData: any | undefined = undefined;
792
+ let userData: Record<string, unknown> | undefined = undefined;
649
793
  if (payload.user_profile_id) {
650
794
  try {
651
795
  const profile = await this.store.getUserProfileById(
@@ -697,30 +841,6 @@ export function createMcpOAuthController(
697
841
  }
698
842
  return false;
699
843
  }
700
-
701
- /**
702
- * Utility function to parse form-encoded or JSON bodies
703
- * Handles both string (raw form data) and object bodies
704
- */
705
- parseRequestBody(body: any): Record<string, any> {
706
- // If body is already a parsed object with properties, return it
707
- if (body && typeof body === 'object' && Object.keys(body).length > 0) {
708
- return body;
709
- }
710
-
711
- // If body is a string (raw form data), parse it
712
- if (typeof body === 'string' && body.length > 0) {
713
- const params = new URLSearchParams(body);
714
- const parsedBody: Record<string, any> = {};
715
- for (const [key, value] of params.entries()) {
716
- parsedBody[key] = value;
717
- }
718
- return parsedBody;
719
- }
720
-
721
- // Return empty object if no valid body
722
- return {};
723
- }
724
844
  }
725
845
 
726
846
  return McpOAuthController;