diff --git a/mcp/packages/server/src/moneta/cognito.ts b/mcp/packages/server/src/moneta/cognito.ts index 737f03863b3..20136121b36 100644 --- a/mcp/packages/server/src/moneta/cognito.ts +++ b/mcp/packages/server/src/moneta/cognito.ts @@ -172,13 +172,14 @@ export class CognitoClient { private async tokenRequest(params: Record): Promise { const discovery = await this.discovery(); + const tokenEndpoint = this.config.upstreamTokenUrl ?? discovery.token_endpoint; const body = new URLSearchParams({ ...params, client_id: this.config.clientId }); const headers: Record = { "Content-Type": "application/x-www-form-urlencoded" }; if (this.config.clientSecret) { const basic = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString("base64"); headers["Authorization"] = `Basic ${basic}`; } - const response = await fetch(discovery.token_endpoint, { method: "POST", headers, body }); + const response = await fetch(tokenEndpoint, { method: "POST", headers, body }); if (!response.ok) { const detail = await response.text().catch(() => ""); throw new Error(`Cognito token endpoint returned ${response.status}: ${detail.slice(0, 300)}`); @@ -261,7 +262,8 @@ export class CognitoClient { /** Builds the upstream authorize redirect with our own callback, state and PKCE pair. */ async authorizeUrl(state: string, codeChallenge: string): Promise { const discovery = await this.discovery(); - const url = new URL(discovery.authorization_endpoint); + const baseEndpoint = this.config.upstreamAuthUrl ?? discovery.authorization_endpoint; + const url = new URL(baseEndpoint); url.searchParams.set("response_type", "code"); url.searchParams.set("client_id", this.config.clientId); url.searchParams.set("redirect_uri", this.config.callbackUrl); @@ -269,6 +271,9 @@ export class CognitoClient { url.searchParams.set("state", state); url.searchParams.set("code_challenge", codeChallenge); url.searchParams.set("code_challenge_method", "S256"); + if (this.config.identityProvider) { + url.searchParams.set("identity_provider", this.config.identityProvider); + } return url.href; } } diff --git a/mcp/packages/server/src/moneta/config.ts b/mcp/packages/server/src/moneta/config.ts index 6cda2889b63..63bf742cb91 100644 --- a/mcp/packages/server/src/moneta/config.ts +++ b/mcp/packages/server/src/moneta/config.ts @@ -48,6 +48,10 @@ export interface MonetaAuthConfig { storageUrl: string | undefined; /** Key material for at-rest encryption of OAuth state in Valkey. */ encryptionSecret: string | undefined; + /** Override Cognito's authorization_endpoint with a custom auth proxy URL (e.g. mpass-auth-proxy). */ + upstreamAuthUrl: string | undefined; + /** Override Cognito's token_endpoint with a custom auth proxy URL (e.g. mpass-auth-proxy). */ + upstreamTokenUrl: string | undefined; isProduction: boolean; } @@ -96,6 +100,9 @@ export function loadMonetaAuthConfig(): MonetaAuthConfig { ); } + const upstreamAuthUrl = (process.env.COGNITO_UPSTREAM_AUTH_URL ?? "").trim() || undefined; + const upstreamTokenUrl = (process.env.COGNITO_UPSTREAM_TOKEN_URL ?? "").trim() || undefined; + const origins = csv(process.env.MCP_ALLOWED_ORIGINS); const redirectUris = csv(process.env.MCP_ALLOWED_CLIENT_REDIRECT_URIS); @@ -111,6 +118,8 @@ export function loadMonetaAuthConfig(): MonetaAuthConfig { allowedClientRedirectUris: redirectUris.length > 0 ? redirectUris : null, storageUrl, encryptionSecret, + upstreamAuthUrl, + upstreamTokenUrl, isProduction: (process.env.MCP_ENV ?? "production") === "production", }; }