n8n
Version:
n8n Workflow Automation Tool
234 lines • 10.6 kB
JavaScript
;
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