{"record":{"id":"a56139997a5ebfca","repo":"conductor-oss/conductor","slug":"refusing-to-forward-credentials-across-an-mcp-redi","errorCode":null,"errorMessage":"Refusing to forward credentials across an MCP redirect","messagePattern":"Refusing to forward credentials across an MCP redirect","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java","lineNumber":394,"sourceCode":"    private ResponsePayload execute(Request initialRequest) throws Exception {\n        Request request = initialRequest;\n        for (int redirects = 0; redirects <= 5; redirects++) {\n            try (Response response = httpClient.newCall(request).execute()) {\n                if (!response.isRedirect()) {\n                    return new ResponsePayload(\n                            response.code(),\n                            response.header(\"Content-Type\", \"application/json\"),\n                            readBoundedBody(response.body()));\n                }\n                String location = response.header(\"Location\");\n                if (location == null || request.url().resolve(location) == null) {\n                    throw new RuntimeException(\n                            \"MCP server returned a redirect without a valid Location\");\n                }\n                String target = request.url().resolve(location).toString();\n                if (hasSensitiveHeaders(request)\n                        && !isSameOrigin(request.url().toString(), target)) {\n                    throw new RuntimeException(\n                            \"Refusing to forward credentials across an MCP redirect\");\n                }\n                request = request.newBuilder().url(target).build();\n            }\n        }\n        throw new RuntimeException(\"MCP server exceeded the redirect limit\");\n    }\n\n    private void addHeaders(Request.Builder builder, Map<String, String> headers) {\n        if (headers == null || headers.isEmpty()) {\n            return;\n        }\n        headers.forEach(\n                (name, value) -> {\n                    if (name == null\n                            || value == null\n                            || name.indexOf('\\r') >= 0\n                            || name.indexOf('\\n') >= 0","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java#L376-L412","documentation":"Security guard thrown by execute() when the request carries sensitive headers (Authorization, Cookie, or Proxy-Authorization) AND the redirect target is a different origin (scheme/host/port). The client deliberately withholds credentials across origins to prevent credential leakage to an attacker-controlled or unrelated host via a redirect. This is an intentional, non-bypassable protection.","triggerScenarios":"An authenticated request to origin A receives a 3xx redirect to origin B (different host/port/scheme); the client refuses to forward Authorization/Cookie/Proxy-Authorization to B. Common with SSO gateways that redirect to a different auth host, or with misconfigured/compromised servers attempting credential exfiltration via redirect.","commonSituations":"Server moved across hosts and the configured URL still hits the old host that redirects to the new one with a different origin; an open-redirect or SSRF-style setup where a redirect points elsewhere; legitimate cross-origin redirect flows that were not designed for.","solutions":["Update serverUrl to point directly at the final origin so no cross-origin redirect occurs.","If the cross-origin redirect is trusted and intentional, re-issue the call to the target origin yourself with credentials scoped appropriately (do not weaken this guard).","Investigate unexpected cross-origin redirects — they may indicate a misconfigured proxy or an attack.","Confirm the MCP server is not behind a redirect chain that crosses trust boundaries."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-resolve the final origin and call it directly, so no cross-origin redirect occurs.\n// Do NOT attempt to weaken or bypass this guard for untrusted servers.\njava.net.URI target = java.net.URI.create(serverUrl);\n// ensure serverUrl is already the terminal origin the credentials are intended for","typeGuard":null,"tryCatchPattern":"try {\n    mcpService.callTool(serverUrl, toolName, arguments, headers);\n} catch (RuntimeException e) {\n    if (e.getMessage().contains(\"Refusing to forward credentials\")) {\n        // SECURITY: do not bypass. Reconfigure serverUrl to the trusted final origin.\n        log.warn(\"Cross-origin credential redirect blocked for {}\", serverUrl);\n    }\n    throw e;\n}","preventionTips":["Never weaken or catch-and-continue this guard — it prevents credential leakage.","Configure serverUrl to the canonical final origin so redirects don't cross origins.","Treat unexpected cross-origin redirects as a possible attack/misconfiguration and investigate."],"tags":["mcp","security","http-redirect","credential-leakage","ssrf"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}