@inkeep/agents-manage-api 0.1.1 → 0.1.6

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.
Files changed (114) hide show
  1. package/LICENSE.md +29 -17
  2. package/README.md +1 -1
  3. package/SUPPLEMENTAL_TERMS.md +40 -0
  4. package/dist/index.cjs +5083 -0
  5. package/dist/index.d.cts +15 -0
  6. package/dist/index.d.ts +15 -4
  7. package/dist/index.js +5046 -35
  8. package/package.json +15 -13
  9. package/dist/ManagementServer.d.ts +0 -28
  10. package/dist/ManagementServer.d.ts.map +0 -1
  11. package/dist/ManagementServer.js +0 -41
  12. package/dist/__tests__/setup.d.ts +0 -2
  13. package/dist/__tests__/setup.d.ts.map +0 -1
  14. package/dist/__tests__/setup.js +0 -26
  15. package/dist/__tests__/utils/testProject.d.ts +0 -18
  16. package/dist/__tests__/utils/testProject.d.ts.map +0 -1
  17. package/dist/__tests__/utils/testProject.js +0 -26
  18. package/dist/__tests__/utils/testRequest.d.ts +0 -2
  19. package/dist/__tests__/utils/testRequest.d.ts.map +0 -1
  20. package/dist/__tests__/utils/testRequest.js +0 -11
  21. package/dist/__tests__/utils/testTenant.d.ts +0 -64
  22. package/dist/__tests__/utils/testTenant.d.ts.map +0 -1
  23. package/dist/__tests__/utils/testTenant.js +0 -71
  24. package/dist/app.d.ts +0 -4
  25. package/dist/app.d.ts.map +0 -1
  26. package/dist/app.js +0 -140
  27. package/dist/data/conversations.d.ts +0 -59
  28. package/dist/data/conversations.d.ts.map +0 -1
  29. package/dist/data/conversations.js +0 -216
  30. package/dist/data/db/clean.d.ts +0 -6
  31. package/dist/data/db/clean.d.ts.map +0 -1
  32. package/dist/data/db/clean.js +0 -77
  33. package/dist/data/db/dbClient.d.ts +0 -3
  34. package/dist/data/db/dbClient.d.ts.map +0 -1
  35. package/dist/data/db/dbClient.js +0 -13
  36. package/dist/data/graphFull.d.ts +0 -11
  37. package/dist/data/graphFull.d.ts.map +0 -1
  38. package/dist/data/graphFull.js +0 -90
  39. package/dist/data/graphFullClient.d.ts +0 -22
  40. package/dist/data/graphFullClient.d.ts.map +0 -1
  41. package/dist/data/graphFullClient.js +0 -189
  42. package/dist/data/tools.d.ts +0 -81
  43. package/dist/data/tools.d.ts.map +0 -1
  44. package/dist/data/tools.js +0 -266
  45. package/dist/env.d.ts +0 -41
  46. package/dist/env.d.ts.map +0 -1
  47. package/dist/env.js +0 -59
  48. package/dist/index.d.ts.map +0 -1
  49. package/dist/logger.d.ts +0 -4
  50. package/dist/logger.d.ts.map +0 -1
  51. package/dist/logger.js +0 -32
  52. package/dist/middleware/auth.d.ts +0 -12
  53. package/dist/middleware/auth.d.ts.map +0 -1
  54. package/dist/middleware/auth.js +0 -36
  55. package/dist/openapi.d.ts +0 -2
  56. package/dist/openapi.d.ts.map +0 -1
  57. package/dist/openapi.js +0 -38
  58. package/dist/routes/agentArtifactComponents.d.ts +0 -4
  59. package/dist/routes/agentArtifactComponents.d.ts.map +0 -1
  60. package/dist/routes/agentArtifactComponents.js +0 -230
  61. package/dist/routes/agentDataComponents.d.ts +0 -4
  62. package/dist/routes/agentDataComponents.d.ts.map +0 -1
  63. package/dist/routes/agentDataComponents.js +0 -225
  64. package/dist/routes/agentGraph.d.ts +0 -4
  65. package/dist/routes/agentGraph.d.ts.map +0 -1
  66. package/dist/routes/agentGraph.js +0 -289
  67. package/dist/routes/agentRelations.d.ts +0 -4
  68. package/dist/routes/agentRelations.d.ts.map +0 -1
  69. package/dist/routes/agentRelations.js +0 -290
  70. package/dist/routes/agentToolRelations.d.ts +0 -4
  71. package/dist/routes/agentToolRelations.d.ts.map +0 -1
  72. package/dist/routes/agentToolRelations.js +0 -342
  73. package/dist/routes/agents.d.ts +0 -4
  74. package/dist/routes/agents.d.ts.map +0 -1
  75. package/dist/routes/agents.js +0 -213
  76. package/dist/routes/apiKeys.d.ts +0 -4
  77. package/dist/routes/apiKeys.d.ts.map +0 -1
  78. package/dist/routes/apiKeys.js +0 -236
  79. package/dist/routes/artifactComponents.d.ts +0 -4
  80. package/dist/routes/artifactComponents.d.ts.map +0 -1
  81. package/dist/routes/artifactComponents.js +0 -202
  82. package/dist/routes/contextConfigs.d.ts +0 -4
  83. package/dist/routes/contextConfigs.d.ts.map +0 -1
  84. package/dist/routes/contextConfigs.js +0 -181
  85. package/dist/routes/credentials.d.ts +0 -4
  86. package/dist/routes/credentials.d.ts.map +0 -1
  87. package/dist/routes/credentials.js +0 -219
  88. package/dist/routes/dataComponents.d.ts +0 -4
  89. package/dist/routes/dataComponents.d.ts.map +0 -1
  90. package/dist/routes/dataComponents.js +0 -188
  91. package/dist/routes/externalAgents.d.ts +0 -4
  92. package/dist/routes/externalAgents.d.ts.map +0 -1
  93. package/dist/routes/externalAgents.js +0 -216
  94. package/dist/routes/graphFull.d.ts +0 -4
  95. package/dist/routes/graphFull.d.ts.map +0 -1
  96. package/dist/routes/graphFull.js +0 -248
  97. package/dist/routes/index.d.ts +0 -4
  98. package/dist/routes/index.d.ts.map +0 -1
  99. package/dist/routes/index.js +0 -37
  100. package/dist/routes/oauth.d.ts +0 -14
  101. package/dist/routes/oauth.d.ts.map +0 -1
  102. package/dist/routes/oauth.js +0 -191
  103. package/dist/routes/projects.d.ts +0 -4
  104. package/dist/routes/projects.d.ts.map +0 -1
  105. package/dist/routes/projects.js +0 -221
  106. package/dist/routes/tools.d.ts +0 -4
  107. package/dist/routes/tools.d.ts.map +0 -1
  108. package/dist/routes/tools.js +0 -547
  109. package/dist/utils/auth-detection.d.ts +0 -22
  110. package/dist/utils/auth-detection.d.ts.map +0 -1
  111. package/dist/utils/auth-detection.js +0 -149
  112. package/dist/utils/oauth-service.d.ts +0 -88
  113. package/dist/utils/oauth-service.d.ts.map +0 -1
  114. package/dist/utils/oauth-service.js +0 -240
@@ -1,149 +0,0 @@
1
- /**
2
- * Centralized authentication detection utilities for MCP tools
3
- */
4
- import { getLogger } from '../logger.js';
5
- const logger = getLogger('auth-detection');
6
- /**
7
- * Helper function to construct well-known OAuth endpoint URLs
8
- */
9
- const getWellKnownUrls = (baseUrl) => [
10
- `${baseUrl}/.well-known/oauth-authorization-server`,
11
- `${baseUrl}/.well-known/openid-configuration`,
12
- ];
13
- /**
14
- * Helper function to validate OAuth metadata for PKCE support
15
- */
16
- const validateOAuthMetadata = (metadata) => {
17
- return metadata.code_challenge_methods_supported?.includes('S256');
18
- };
19
- /**
20
- * Helper function to construct OAuthConfig from metadata
21
- */
22
- const buildOAuthConfig = (metadata) => ({
23
- authorizationUrl: metadata.authorization_endpoint,
24
- tokenUrl: metadata.token_endpoint,
25
- registrationUrl: metadata.registration_endpoint,
26
- supportsDynamicRegistration: !!metadata.registration_endpoint,
27
- });
28
- /**
29
- * Helper function to try OAuth discovery at well-known endpoints
30
- */
31
- const tryWellKnownEndpoints = async (baseUrl) => {
32
- const wellKnownUrls = getWellKnownUrls(baseUrl);
33
- for (const wellKnownUrl of wellKnownUrls) {
34
- try {
35
- const response = await fetch(wellKnownUrl);
36
- if (response.ok) {
37
- const metadata = await response.json();
38
- if (validateOAuthMetadata(metadata)) {
39
- logger.debug({ baseUrl, wellKnownUrl }, 'OAuth 2.1/PKCE support detected');
40
- return buildOAuthConfig(metadata);
41
- }
42
- }
43
- }
44
- catch (error) {
45
- logger.debug({ wellKnownUrl, error }, 'OAuth endpoint check failed');
46
- }
47
- }
48
- return null;
49
- };
50
- /**
51
- * Check if a server supports OAuth 2.1/PKCE endpoints (simple boolean)
52
- */
53
- const checkForOAuthEndpoints = async (serverUrl) => {
54
- const config = await discoverOAuthEndpoints(serverUrl);
55
- return config !== null;
56
- };
57
- /**
58
- * Full OAuth endpoint discovery with complete configuration
59
- */
60
- export const discoverOAuthEndpoints = async (serverUrl) => {
61
- try {
62
- // 1. Try direct 401 response first
63
- const response = await fetch(serverUrl, {
64
- method: 'POST',
65
- headers: { 'Content-Type': 'application/json' },
66
- body: JSON.stringify({}),
67
- });
68
- if (response.status === 401) {
69
- const wwwAuth = response.headers.get('WWW-Authenticate');
70
- if (wwwAuth) {
71
- // Parse Protected Resource Metadata URL from WWW-Authenticate header
72
- const metadataMatch = wwwAuth.match(/as_uri="([^"]+)"/);
73
- if (metadataMatch) {
74
- const metadataResponse = await fetch(metadataMatch[1]);
75
- if (metadataResponse.ok) {
76
- const metadata = await metadataResponse.json();
77
- if (metadata.authorization_servers?.length > 0) {
78
- return await tryWellKnownEndpoints(metadata.authorization_servers[0]);
79
- }
80
- }
81
- }
82
- }
83
- }
84
- }
85
- catch (_error) {
86
- // Continue to well-known endpoints
87
- }
88
- // 2. Try well-known endpoints
89
- const url = new URL(serverUrl);
90
- const baseUrl = `${url.protocol}//${url.host}`;
91
- return await tryWellKnownEndpoints(baseUrl);
92
- };
93
- /**
94
- * Detect if OAuth 2.1/PKCE authentication is specifically required for a tool
95
- */
96
- export const detectAuthenticationRequired = async (tool, error) => {
97
- const serverUrl = tool.config.mcp.server.url;
98
- // 1. First, try OAuth 2.1/PKCE endpoint discovery (most reliable for our use case)
99
- let hasOAuthEndpoints = false;
100
- try {
101
- hasOAuthEndpoints = await checkForOAuthEndpoints(serverUrl);
102
- if (hasOAuthEndpoints) {
103
- logger.info({ toolId: tool.id, serverUrl }, 'OAuth 2.1/PKCE support confirmed via endpoint discovery');
104
- return true; // Server supports OAuth 2.1/PKCE, prioritize this
105
- }
106
- }
107
- catch (discoveryError) {
108
- logger.debug({ toolId: tool.id, discoveryError }, 'OAuth endpoint discovery failed');
109
- }
110
- // 2. Check for 401 with OAuth-specific WWW-Authenticate header
111
- try {
112
- const response = await fetch(serverUrl, {
113
- method: 'POST',
114
- headers: { 'Content-Type': 'application/json' },
115
- body: JSON.stringify({
116
- jsonrpc: '2.0',
117
- method: 'initialize',
118
- id: 1,
119
- params: { protocolVersion: '2024-11-05', capabilities: {} },
120
- }),
121
- });
122
- // Check for 401 with OAuth-specific WWW-Authenticate header
123
- if (response.status === 401) {
124
- const wwwAuth = response.headers.get('WWW-Authenticate');
125
- // Only return true for OAuth-specific auth schemes (not Basic, API Key, etc.)
126
- if (wwwAuth &&
127
- (wwwAuth.toLowerCase().includes('bearer') ||
128
- wwwAuth.toLowerCase().includes('oauth') ||
129
- wwwAuth.toLowerCase().includes('authorization_uri'))) {
130
- logger.info({ toolId: tool.id, wwwAuth }, 'OAuth authentication detected via WWW-Authenticate header');
131
- return true;
132
- }
133
- }
134
- }
135
- catch (fetchError) {
136
- logger.debug({ toolId: tool.id, fetchError }, 'Direct fetch authentication check failed');
137
- }
138
- // 3. Check if 401 error message AND OAuth endpoints exist together
139
- if (error.message.includes('401') || error.message.toLowerCase().includes('unauthorized')) {
140
- if (hasOAuthEndpoints) {
141
- logger.info({ toolId: tool.id, error: error.message }, 'OAuth required: 401 error + OAuth endpoints detected');
142
- return true; // Only return true if BOTH 401 AND OAuth endpoints exist
143
- }
144
- }
145
- // If none of the OAuth-specific checks pass, return false
146
- // (This means we won't trigger OAuth flow for Basic Auth, API keys, etc.)
147
- logger.debug({ toolId: tool.id, error: error.message }, 'No OAuth authentication requirement detected');
148
- return false;
149
- };
@@ -1,88 +0,0 @@
1
- /**
2
- * Centralized OAuth service for MCP tools
3
- * Handles the complete OAuth 2.1/PKCE flow for MCP tool authentication
4
- */
5
- import type { McpTool } from '@inkeep/agents-core';
6
- import { type OAuthConfig } from './auth-detection.js';
7
- /**
8
- * Retrieve and remove PKCE verifier
9
- */
10
- export declare function retrievePKCEVerifier(state: string): {
11
- codeVerifier: string;
12
- toolId: string;
13
- tenantId: string;
14
- projectId: string;
15
- clientId: string;
16
- } | null;
17
- /**
18
- * OAuth client configuration
19
- */
20
- interface OAuthClientConfig {
21
- defaultClientId?: string;
22
- clientName?: string;
23
- clientUri?: string;
24
- logoUri?: string;
25
- redirectBaseUrl?: string;
26
- }
27
- /**
28
- * OAuth flow initiation result
29
- */
30
- interface OAuthInitiationResult {
31
- redirectUrl: string;
32
- state: string;
33
- }
34
- /**
35
- * Token exchange result
36
- */
37
- interface TokenExchangeResult {
38
- tokens: any;
39
- oAuthConfig: OAuthConfig;
40
- }
41
- /**
42
- * OAuth service class that handles the complete OAuth flow
43
- */
44
- declare class OAuthService {
45
- private defaultConfig;
46
- constructor(config?: OAuthClientConfig);
47
- /**
48
- * Initiate OAuth flow for an MCP tool
49
- */
50
- initiateOAuthFlow(params: {
51
- tool: McpTool;
52
- tenantId: string;
53
- projectId: string;
54
- toolId: string;
55
- }): Promise<OAuthInitiationResult>;
56
- /**
57
- * Exchange authorization code for access tokens
58
- */
59
- exchangeCodeForTokens(params: {
60
- code: string;
61
- codeVerifier: string;
62
- clientId: string;
63
- tool: McpTool;
64
- }): Promise<TokenExchangeResult>;
65
- /**
66
- * Perform dynamic client registration
67
- */
68
- private performDynamicClientRegistration;
69
- /**
70
- * Build authorization URL
71
- */
72
- private buildAuthorizationUrl;
73
- /**
74
- * Exchange code using openid-client library
75
- */
76
- private exchangeWithOpenIdClient;
77
- /**
78
- * Internal PKCE generation
79
- */
80
- private generatePKCEInternal;
81
- /**
82
- * Manual token exchange fallback
83
- */
84
- private exchangeManually;
85
- }
86
- export declare const oauthService: OAuthService;
87
- export {};
88
- //# sourceMappingURL=oauth-service.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth-service.d.ts","sourceRoot":"","sources":["../../src/utils/oauth-service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAsC/E;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAOP;AAED;;GAEG;AACH,UAAU,iBAAiB;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,UAAU,qBAAqB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,UAAU,mBAAmB;IAC3B,MAAM,EAAE,GAAG,CAAC;IACZ,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;GAEG;AACH,cAAM,YAAY;IAChB,OAAO,CAAC,aAAa,CAA8B;gBAEvC,MAAM,GAAE,iBAAsB;IAe1C;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE;QAC9B,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA6ClC;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,OAAO,CAAC;KACf,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA2ChC;;OAEG;YACW,gCAAgC;IAiD9C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAsB7B;;OAEG;YACW,wBAAwB;IAiCtC;;OAEG;YACW,oBAAoB;IAgBlC;;OAEG;YACW,gBAAgB;CA4C/B;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -1,240 +0,0 @@
1
- /**
2
- * Centralized OAuth service for MCP tools
3
- * Handles the complete OAuth 2.1/PKCE flow for MCP tool authentication
4
- */
5
- import { getLogger } from '../logger.js';
6
- import { discoverOAuthEndpoints } from './auth-detection.js';
7
- const logger = getLogger('oauth-service');
8
- // PKCE storage (TODO: Use Redis or database in production)
9
- const pkceStore = new Map();
10
- /**
11
- * Store PKCE verifier for later use in token exchange
12
- */
13
- function storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId) {
14
- pkceStore.set(state, { codeVerifier, toolId, tenantId, projectId, clientId });
15
- // Clean up after 10 minutes (OAuth flows should complete quickly)
16
- setTimeout(() => {
17
- pkceStore.delete(state);
18
- }, 10 * 60 * 1000);
19
- }
20
- /**
21
- * Retrieve and remove PKCE verifier
22
- */
23
- export function retrievePKCEVerifier(state) {
24
- const data = pkceStore.get(state);
25
- if (data) {
26
- pkceStore.delete(state); // One-time use
27
- return data;
28
- }
29
- return null;
30
- }
31
- /**
32
- * OAuth service class that handles the complete OAuth flow
33
- */
34
- class OAuthService {
35
- defaultConfig;
36
- constructor(config = {}) {
37
- this.defaultConfig = {
38
- defaultClientId: config.defaultClientId || process.env.DEFAULT_OAUTH_CLIENT_ID || 'mcp-client',
39
- clientName: config.clientName || process.env.OAUTH_CLIENT_NAME || 'Inkeep Agent Framework',
40
- clientUri: config.clientUri || process.env.OAUTH_CLIENT_URI || 'https://inkeep.com',
41
- logoUri: config.logoUri ||
42
- process.env.OAUTH_CLIENT_LOGO_URI ||
43
- 'https://storage.googleapis.com/inkeep-static-assets/Favicon_gray_large.png',
44
- redirectBaseUrl: config.redirectBaseUrl || process.env.OAUTH_REDIRECT_BASE_URL || 'http://localhost:3002',
45
- };
46
- }
47
- /**
48
- * Initiate OAuth flow for an MCP tool
49
- */
50
- async initiateOAuthFlow(params) {
51
- const { tool, tenantId, projectId, toolId } = params;
52
- // 1. Detect OAuth requirements
53
- const oAuthConfig = await discoverOAuthEndpoints(tool.config.mcp.server.url);
54
- if (!oAuthConfig) {
55
- throw new Error('OAuth not supported by this server');
56
- }
57
- // 2. Generate PKCE parameters
58
- const { codeVerifier, codeChallenge } = await this.generatePKCEInternal();
59
- // 3. Handle dynamic client registration if supported
60
- const redirectUri = `${this.defaultConfig.redirectBaseUrl}/oauth/callback`;
61
- let clientId = this.defaultConfig.defaultClientId;
62
- if (oAuthConfig.supportsDynamicRegistration && oAuthConfig.registrationUrl) {
63
- clientId = await this.performDynamicClientRegistration(oAuthConfig.registrationUrl, redirectUri);
64
- }
65
- // 4. Build OAuth URL
66
- const state = `tool_${toolId}`;
67
- const authUrl = this.buildAuthorizationUrl({
68
- oAuthConfig,
69
- clientId,
70
- redirectUri,
71
- state,
72
- codeChallenge,
73
- resource: tool.config.mcp.server.url,
74
- });
75
- // 5. Store PKCE verifier for callback handling
76
- storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId);
77
- logger.info({ toolId, oAuthConfig, tenantId, projectId }, 'OAuth flow initiated successfully');
78
- return {
79
- redirectUrl: authUrl,
80
- state,
81
- };
82
- }
83
- /**
84
- * Exchange authorization code for access tokens
85
- */
86
- async exchangeCodeForTokens(params) {
87
- const { code, codeVerifier, clientId, tool } = params;
88
- // Discover OAuth server endpoints from MCP server
89
- const oAuthConfig = await discoverOAuthEndpoints(tool.config.mcp.server.url);
90
- if (!oAuthConfig?.tokenUrl) {
91
- throw new Error('Could not discover OAuth token endpoint');
92
- }
93
- const redirectUri = `${this.defaultConfig.redirectBaseUrl}/oauth/callback`;
94
- let tokens;
95
- try {
96
- // Try openid-client first (more robust)
97
- tokens = await this.exchangeWithOpenIdClient({
98
- oAuthConfig,
99
- clientId,
100
- code,
101
- codeVerifier,
102
- redirectUri,
103
- });
104
- logger.info({ tokenType: tokens.token_type }, 'Token exchange successful with openid-client');
105
- }
106
- catch (error) {
107
- logger.warn({ error: error instanceof Error ? error.message : error }, 'openid-client failed, falling back to manual token exchange');
108
- // Fallback to manual token exchange
109
- tokens = await this.exchangeManually({
110
- oAuthConfig,
111
- clientId,
112
- code,
113
- codeVerifier,
114
- redirectUri,
115
- });
116
- logger.info({ tokenType: tokens.token_type }, 'Manual token exchange successful');
117
- }
118
- return { tokens, oAuthConfig };
119
- }
120
- /**
121
- * Perform dynamic client registration
122
- */
123
- async performDynamicClientRegistration(registrationUrl, redirectUri) {
124
- logger.info({ registrationUrl }, 'Attempting dynamic client registration');
125
- try {
126
- const registrationResponse = await fetch(registrationUrl, {
127
- method: 'POST',
128
- headers: {
129
- 'Content-Type': 'application/json',
130
- Accept: 'application/json',
131
- },
132
- body: JSON.stringify({
133
- client_name: this.defaultConfig.clientName,
134
- client_uri: this.defaultConfig.clientUri,
135
- logo_uri: this.defaultConfig.logoUri,
136
- redirect_uris: [redirectUri],
137
- grant_types: ['authorization_code'],
138
- response_types: ['code'],
139
- token_endpoint_auth_method: 'none', // PKCE only, no client secret
140
- application_type: 'native', // For PKCE flows
141
- }),
142
- });
143
- if (registrationResponse.ok) {
144
- const registration = await registrationResponse.json();
145
- logger.info({ clientId: registration.client_id }, 'Dynamic client registration successful');
146
- return registration.client_id;
147
- }
148
- else {
149
- const errorText = await registrationResponse.text();
150
- logger.warn({
151
- status: registrationResponse.status,
152
- errorText,
153
- }, 'Dynamic client registration failed, using default client_id');
154
- }
155
- }
156
- catch (regError) {
157
- logger.warn({ error: regError }, 'Dynamic client registration error, using default client_id');
158
- }
159
- return this.defaultConfig.defaultClientId;
160
- }
161
- /**
162
- * Build authorization URL
163
- */
164
- buildAuthorizationUrl(params) {
165
- const { oAuthConfig, clientId, redirectUri, state, codeChallenge, resource } = params;
166
- const authUrl = new URL(oAuthConfig.authorizationUrl);
167
- authUrl.searchParams.set('response_type', 'code');
168
- authUrl.searchParams.set('client_id', clientId);
169
- authUrl.searchParams.set('redirect_uri', redirectUri);
170
- authUrl.searchParams.set('state', state);
171
- authUrl.searchParams.set('code_challenge', codeChallenge);
172
- authUrl.searchParams.set('code_challenge_method', 'S256');
173
- authUrl.searchParams.set('resource', resource); // Required by MCP spec
174
- return authUrl.toString();
175
- }
176
- /**
177
- * Exchange code using openid-client library
178
- */
179
- async exchangeWithOpenIdClient(params) {
180
- const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
181
- // Import openid-client for proper OAuth handling
182
- const oauth = await import('openid-client');
183
- // Extract OAuth server base URL from token URL
184
- const tokenUrl = new URL(oAuthConfig.tokenUrl);
185
- const oauthServerUrl = `${tokenUrl.protocol}//${tokenUrl.host}`;
186
- logger.info({ oauthServerUrl, clientId }, 'Attempting openid-client discovery');
187
- const config = await oauth.discovery(new URL(oauthServerUrl), clientId, undefined // No client secret for PKCE
188
- );
189
- const callbackUrl = new URL(`${redirectUri}?${new URLSearchParams({ code, state: 'unused' }).toString()}`);
190
- return await oauth.authorizationCodeGrant(config, callbackUrl, {
191
- pkceCodeVerifier: codeVerifier,
192
- });
193
- }
194
- /**
195
- * Internal PKCE generation
196
- */
197
- async generatePKCEInternal() {
198
- const codeVerifier = Buffer.from(Array.from(crypto.getRandomValues(new Uint8Array(32)))).toString('base64url');
199
- const encoder = new TextEncoder();
200
- const data = encoder.encode(codeVerifier);
201
- const hash = await crypto.subtle.digest('SHA-256', data);
202
- const codeChallenge = Buffer.from(hash).toString('base64url');
203
- return { codeVerifier, codeChallenge };
204
- }
205
- /**
206
- * Manual token exchange fallback
207
- */
208
- async exchangeManually(params) {
209
- const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
210
- logger.info({ tokenUrl: oAuthConfig.tokenUrl }, 'Attempting manual token exchange');
211
- const tokenResponse = await fetch(oAuthConfig.tokenUrl, {
212
- method: 'POST',
213
- headers: {
214
- 'Content-Type': 'application/x-www-form-urlencoded',
215
- Accept: 'application/json',
216
- },
217
- body: new URLSearchParams({
218
- grant_type: 'authorization_code',
219
- code,
220
- redirect_uri: redirectUri,
221
- client_id: clientId,
222
- code_verifier: codeVerifier, // PKCE verification
223
- }),
224
- });
225
- if (!tokenResponse.ok) {
226
- const errorText = await tokenResponse.text();
227
- logger.error({
228
- status: tokenResponse.status,
229
- statusText: tokenResponse.statusText,
230
- ...(process.env.NODE_ENV === 'development' && { errorText }),
231
- clientId,
232
- tokenUrl: oAuthConfig.tokenUrl,
233
- }, 'Token exchange failed');
234
- throw new Error('Authentication failed. Please try again or contact support.');
235
- }
236
- return await tokenResponse.json();
237
- }
238
- }
239
- // Default instance for convenience
240
- export const oauthService = new OAuthService();