@grebyn/toolflow-mcp-server
Version:
MCP server for managing other MCP servers - discover, install, organize into bundles, and automate with workflows. Uses StreamableHTTP transport with dual OAuth/API key authentication.
181 lines • 7.7 kB
JavaScript
/**
* OAuth Discovery Endpoints Handler
* Implements MCP Authorization specification (RFC 9728) endpoints
*/
export class OAuthEndpointsHandler {
config;
constructor(config) {
this.config = config;
}
/**
* Handle OAuth Protected Resource Metadata endpoint
* GET /.well-known/oauth-protected-resource
*/
handleProtectedResourceMetadata(req, res) {
const host = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host;
const protocol = Array.isArray(req.headers['x-forwarded-proto'])
? req.headers['x-forwarded-proto'][0]
: req.headers['x-forwarded-proto'] || 'http';
const baseUrl = `${protocol}://${host || 'localhost'}`;
const metadata = {
resource: `${baseUrl}/mcp`,
authorization_servers: [baseUrl] // Point to this server, not the Next.js app
};
this.sendJsonResponse(res, 200, metadata);
}
/**
* Handle OAuth Authorization Server Metadata endpoint
* GET /.well-known/oauth-authorization-server
*/
handleAuthorizationServerMetadata(req, res) {
const host = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host;
const protocol = Array.isArray(req.headers['x-forwarded-proto'])
? req.headers['x-forwarded-proto'][0]
: req.headers['x-forwarded-proto'] || 'http';
const baseUrl = `${protocol}://${host || 'localhost'}`;
const metadata = {
issuer: `${this.config.oauthBaseUrl}/auth/v1`,
authorization_endpoint: `${this.config.oauthBaseUrl}/api/oauth/authorize`,
token_endpoint: `${this.config.oauthBaseUrl}/api/oauth/token`,
jwks_uri: `${this.config.oauthBaseUrl}/.well-known/jwks.json`,
grant_types_supported: ["authorization_code", "refresh_token"],
code_challenge_methods_supported: ["S256"],
response_types_supported: ["code"],
scopes_supported: ["openid", "email", "profile"],
token_endpoint_auth_methods_supported: ["client_secret_post", "none"],
registration_endpoint: `${baseUrl}/.well-known/oauth-dynamic-client-registration`,
resource_indicators_supported: true,
require_pushed_authorization_requests: false
};
this.sendJsonResponse(res, 200, metadata);
}
/**
* Handle OAuth Dynamic Client Registration endpoint
* POST /.well-known/oauth-dynamic-client-registration
*/
handleDynamicClientRegistration(req, res) {
if (req.method !== 'POST') {
if (req.method === 'OPTIONS') {
this.sendCorsResponse(res, 204);
return;
}
this.sendJsonResponse(res, 405, {
error: 'method_not_allowed',
error_description: 'Only POST method is supported'
});
return;
}
const host = Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host;
const protocol = Array.isArray(req.headers['x-forwarded-proto'])
? req.headers['x-forwarded-proto'][0]
: req.headers['x-forwarded-proto'] || 'http';
// Generate comprehensive MCP-compliant OAuth redirect URIs
const redirectUris = this.generateSecureRedirectUris(protocol, host || 'localhost');
const clientRegistration = {
client_id: "mcp-client",
client_secret: "", // Empty for public client compatibility
token_endpoint_auth_method: "none",
grant_types: ["authorization_code", "refresh_token"],
response_types: ["code"],
redirect_uris: redirectUris,
scope: "openid email profile",
client_name: "MCP Client",
application_type: "native"
};
this.sendJsonResponse(res, 201, clientRegistration);
}
/**
* Generate secure, comprehensive redirect URIs following MCP and OAuth 2.1 standards
* Supports all major MCP clients while maintaining security
*/
generateSecureRedirectUris(protocol, host) {
const redirectUris = [];
// 1. Production HTTPS callback (MCP compliant)
if (protocol === 'https') {
redirectUris.push(`${protocol}://${host}/oauth/callback`);
}
// 2. Localhost patterns with common ports (RFC 8252 compliant)
// These cover development and local testing scenarios
const localhostPorts = [3000, 3001, 5000, 5173, 8080, 8000, 4000, 6274];
const localhostPaths = ['/oauth/callback', '/callback', '/auth/callback'];
for (const port of localhostPorts) {
for (const path of localhostPaths) {
redirectUris.push(`http://localhost:${port}${path}`);
redirectUris.push(`http://127.0.0.1:${port}${path}`);
}
}
// 3. IDE Custom URI Schemes (MCP specification allows these)
const ideSchemes = [
// VS Code family
'vscode://oauth/callback',
'vscode://auth/callback',
'vscode://callback',
'vscode-insiders://oauth/callback',
'vscode-insiders://auth/callback',
// Cursor AI Editor
'cursor://oauth/callback',
'cursor://auth/callback',
'cursor://callback',
'cursor://mcp/oauth/callback',
'cursor://mcp/auth/callback',
// JetBrains family
'jetbrains://oauth/callback',
'jetbrains://auth/callback',
'intellij://oauth/callback',
'intellij://auth/callback',
'pycharm://oauth/callback',
'webstorm://oauth/callback',
'phpstorm://oauth/callback',
'rubymine://oauth/callback',
'clion://oauth/callback',
'datagrip://oauth/callback',
'goland://oauth/callback',
'rider://oauth/callback',
// Other popular editors/IDEs
'atom://oauth/callback',
'sublime://oauth/callback',
'brackets://oauth/callback',
'nova://oauth/callback',
'zed://oauth/callback',
// MCP-specific schemes
'mcp://oauth/callback',
'mcp://auth/callback',
// Generic AI/Agent tools
'agent://oauth/callback',
'ai-tool://oauth/callback'
];
redirectUris.push(...ideSchemes);
// 4. Mobile app schemes (common patterns)
const mobileSchemes = [
'com.anthropic.claude://oauth/callback',
'com.openai.chatgpt://oauth/callback',
'app://oauth/callback'
];
redirectUris.push(...mobileSchemes);
return redirectUris;
}
/**
* Send JSON response with proper CORS headers
*/
sendJsonResponse(res, statusCode, data) {
res.writeHead(statusCode, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, mcp-protocol-version',
});
res.end(JSON.stringify(data));
}
/**
* Send CORS response for OPTIONS requests
*/
sendCorsResponse(res, statusCode) {
res.writeHead(statusCode, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, mcp-protocol-version',
});
res.end();
}
}
//# sourceMappingURL=oauth-endpoints.js.map