UNPKG

n8n

Version:

n8n Workflow Automation Tool

234 lines • 10.6 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OAuthController = void 0; const authorize_js_1 = require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"); const register_js_1 = require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"); const revoke_js_1 = require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"); const token_js_1 = require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"); const backend_common_1 = require("@n8n/backend-common"); const config_1 = require("@n8n/config"); const constants_1 = require("@n8n/constants"); const decorators_1 = require("@n8n/decorators"); const di_1 = require("@n8n/di"); const protected_resource_registry_1 = require("../../services/protected-resource.registry"); const url_service_1 = require("../../services/url.service"); const oauth_server_config_1 = require("./oauth-server.config"); const oauth_server_service_1 = require("./oauth-server.service"); const oauth_errors_1 = require("./oauth.errors"); const oauth_helpers_1 = require("./oauth.helpers"); const oauthServerService = di_1.Container.get(oauth_server_service_1.OAuthServerService); const globalConfig = di_1.Container.get(config_1.GlobalConfig); const oauthServerConfig = di_1.Container.get(oauth_server_config_1.OAuthServerConfig); const logger = di_1.Container.get(backend_common_1.Logger); const urlService = di_1.Container.get(url_service_1.UrlService); const oauthClientLimitGuard = async (_req, res, next) => { if (await oauthServerService.isClientLimitReached()) { const limit = globalConfig.endpoints.mcpMaxRegisteredClients; logger.warn('OAuth client registration rejected: instance limit reached (pre-check)', { limit, }); res.status(503).json({ error: 'server_error', error_description: (0, oauth_errors_1.buildOAuthClientLimitReachedMessage)(limit), }); return; } next(); }; const rfc9207IssuerParam = (_req, res, next) => { const originalLocation = res.location.bind(res); res.location = (url) => originalLocation(oauth_helpers_1.OAuthHelpers.setIssuerParam(url, urlService.getInstanceBaseUrl())); next(); }; const registerRouter = (0, register_js_1.clientRegistrationHandler)({ clientsStore: oauthServerService.clientsStore, }); const authorizeRouter = (0, authorize_js_1.authorizationHandler)({ provider: oauthServerService }); const tokenRouter = (0, token_js_1.tokenHandler)({ provider: oauthServerService }); const revokeRouter = (0, revoke_js_1.revocationHandler)({ provider: oauthServerService }); const sharedEndpointRouters = (basePath) => [ { path: `${basePath}/register`, router: registerRouter, skipAuth: true, middlewares: [oauthClientLimitGuard], ipRateLimit: (0, decorators_1.createIpRateLimit)(oauthServerConfig.rateLimitRegister, 5 * constants_1.Time.minutes.toMilliseconds), }, { path: `${basePath}/authorize`, router: authorizeRouter, skipAuth: true, middlewares: [rfc9207IssuerParam], ipRateLimit: (0, decorators_1.createIpRateLimit)(oauthServerConfig.rateLimitAuthorize, 5 * constants_1.Time.minutes.toMilliseconds), }, { path: `${basePath}/token`, router: tokenRouter, skipAuth: true, ipRateLimit: (0, decorators_1.createIpRateLimit)(oauthServerConfig.rateLimitToken, 5 * constants_1.Time.minutes.toMilliseconds), }, { path: `${basePath}/revoke`, router: revokeRouter, skipAuth: true, ipRateLimit: (0, decorators_1.createIpRateLimit)(oauthServerConfig.rateLimitRevoke, 5 * constants_1.Time.minutes.toMilliseconds), }, ]; const wellKnownIpRateLimit = (0, decorators_1.createIpRateLimit)(oauthServerConfig.rateLimitWellKnown, 5 * constants_1.Time.minutes.toMilliseconds); let OAuthController = class OAuthController { constructor(urlService, resourceRegistry) { this.urlService = urlService; this.resourceRegistry = resourceRegistry; } setCorsHeaders(res) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type'); } metadataOptions(_req, res) { this.setCorsHeaders(res); res.status(204).end(); } metadata(_req, res) { this.setCorsHeaders(res); const baseUrl = this.urlService.getInstanceBaseUrl(); const allScopes = this.resourceRegistry.getAllScopes(); const metadata = { issuer: baseUrl, authorization_endpoint: `${baseUrl}/mcp-oauth/authorize`, token_endpoint: `${baseUrl}/mcp-oauth/token`, registration_endpoint: `${baseUrl}/mcp-oauth/register`, revocation_endpoint: `${baseUrl}/mcp-oauth/revoke`, response_types_supported: ['code'], grant_types_supported: ['authorization_code', 'refresh_token'], token_endpoint_auth_methods_supported: ['none', 'client_secret_post', 'client_secret_basic'], code_challenge_methods_supported: ['S256'], authorization_response_iss_parameter_supported: true, }; if (allScopes.length > 0) { metadata.scopes_supported = allScopes; } res.json(metadata); } protectedResourceMetadataOptions(_req, res) { this.setCorsHeaders(res); res.status(204).end(); } async protectedResourceMetadata(req, res) { this.setCorsHeaders(res); const resourcePath = '/' + (Array.isArray(req.params.resourcePath) ? req.params.resourcePath.join('/') : req.params.resourcePath); const resource = await this.resourceRegistry.getByResourcePath(resourcePath); if (!resource) { res.status(404).json({ message: 'Unknown protected resource' }); return; } res.json(this.buildProtectedResourceMetadata(resource)); } defaultProtectedResourceMetadataOptions(_req, res) { this.setCorsHeaders(res); res.status(204).end(); } defaultProtectedResourceMetadata(_req, res) { this.setCorsHeaders(res); const resource = this.resourceRegistry.getDefaultResource(); if (!resource) { res.status(404).json({ message: 'Unknown protected resource' }); return; } res.json(this.buildProtectedResourceMetadata(resource)); } buildProtectedResourceMetadata(resource) { const baseUrl = this.urlService.getInstanceBaseUrl(); const metadata = { resource: resource.getResourceUrl(), bearer_methods_supported: ['header'], authorization_servers: [baseUrl], }; if (resource.scopes.length > 0) { metadata.scopes_supported = resource.scopes; } return metadata; } }; exports.OAuthController = OAuthController; OAuthController.routers = [ ...sharedEndpointRouters('/mcp-oauth'), ...sharedEndpointRouters('/oauth'), ]; __decorate([ (0, decorators_1.Options)('/.well-known/oauth-authorization-server', { skipAuth: true, usesTemplates: true, ipRateLimit: wellKnownIpRateLimit, }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", void 0) ], OAuthController.prototype, "metadataOptions", null); __decorate([ (0, decorators_1.Get)('/.well-known/oauth-authorization-server', { skipAuth: true, usesTemplates: true, ipRateLimit: wellKnownIpRateLimit, }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", void 0) ], OAuthController.prototype, "metadata", null); __decorate([ (0, decorators_1.Options)('/.well-known/oauth-protected-resource/*resourcePath', { skipAuth: true, usesTemplates: true, ipRateLimit: wellKnownIpRateLimit, }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", void 0) ], OAuthController.prototype, "protectedResourceMetadataOptions", null); __decorate([ (0, decorators_1.Get)('/.well-known/oauth-protected-resource/*resourcePath', { skipAuth: true, usesTemplates: true, ipRateLimit: wellKnownIpRateLimit, }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], OAuthController.prototype, "protectedResourceMetadata", null); __decorate([ (0, decorators_1.Options)('/.well-known/oauth-protected-resource', { skipAuth: true, usesTemplates: true, ipRateLimit: wellKnownIpRateLimit, }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", void 0) ], OAuthController.prototype, "defaultProtectedResourceMetadataOptions", null); __decorate([ (0, decorators_1.Get)('/.well-known/oauth-protected-resource', { skipAuth: true, usesTemplates: true, ipRateLimit: wellKnownIpRateLimit, }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", void 0) ], OAuthController.prototype, "defaultProtectedResourceMetadata", null); exports.OAuthController = OAuthController = __decorate([ (0, decorators_1.RootLevelController)('/'), __metadata("design:paramtypes", [url_service_1.UrlService, protected_resource_registry_1.ProtectedResourceRegistry]) ], OAuthController); //# sourceMappingURL=oauth.controller.js.map