Fixes #32671: relay IdP OAuth error callbacks to the MCP client redirect_uri - #32735
Conversation
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
✅ Playwright Results — workflow succeededValidated commit ✅ 4476 passed · ❌ 0 failed · 🟡 7 flaky · ⏭️ 1 skipped · 🧰 0 lifecycle flaky PerformanceBlocking targets: ✅ met · Optimization targets: 🟡 in progress Shard-job maxima below are not the full workflow wall time; the linked run includes build, fixture, planning, and reporting. 🕒 Full workflow signal wall (to summary) 3h 13m 0s ⏱️ Max setup 4m 20s · max shard execution 21m 49s · max shard-job elapsed before upload 25m 49s · reporting 22s 🌐 217.03 requests/attempt · 2.31 app boots/UI scenario · 41.48% common-shard skew Optimization targets still in progress:
🟡 7 flaky test(s) (passed on retry)
How to debug locally# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip # view trace |
Detail bug report: View on Detail
Describe your changes:
Fixes #32671
When an IdP returned an error callback for an MCP login (
login_required,access_denied,server_error),McpCallbackServletran the web-SSOhandleCallbackagainst aBufferedServletResponseWrapper. The wrapper swallowed the IdP error, so the MCP client never got theerror=...&state=<mcp_state>redirect to its ownredirect_urithat RFC 6749 §4.1.2.1 requires, and the user got an opaque500 Authentication Failedpage instead.Root cause:
AuthCallbackServletforwards/callbackto/mcp/callbackonisMcpState(state)alone, without inspectingerror. On that forwarded pathhandleCallback'ssendRedirect("/signin")and its non-silent-error throw both land on the buffered wrapper, which leavesstatusCode == 200and never setsOIDC_CREDENTIAL_PROFILE, soMcpCallbackServletfalls through tocredentials == null.Type of change:
High-level design:
N/A, small change.
handleCallbackand relay it to the MCP clientMcpCallbackServlet.doGethandleSSOErrorCallback: re-validate the clientredirect_uri, build the OAuth error response (error,error_description,state, RFC 9207iss), serve a page that redirects to the client callback, clean up the pending requestUserSSOOAuthProviderserver_error. The free-texterror_descriptionis not relayed (OPTIONAL per spec)UserSSOOAuthProvider.canonicalizeIdpErrorCodeUserSSOOAuthProvider.serveErrorPageprompt=noneon the MCP path. Silent auth is a web-SSO optimization and can only returnlogin_requiredto a client that just opened a browser for the user to log in.login/consent/select_accountstill applyAuthenticationCodeFlowHandler.handleLogin302on a bufferedsendRedirectsostatusCodeis not a misleading200BufferedServletResponseWrapperTests:
Unit tests
McpCallbackServletTest(+7): IdP error routed to the relay forlogin_required/access_denied/ null description; emptyerrorfalls through; missing pending request keeps the400 ERR_STATE_NOT_FOUND; a success callback never hits the relay; bufferedsendRedirectrecords302.UserSSOOAuthProviderTest(+12, new file): error redirect carrieserror/state/iss;stateandissomitted rather than sent asnull; a known code (access_denied) is relayed verbatim and an unknown one (vendor_weird_code) asserver_error; pending request deleted after the relay; unregisteredredirect_urifails safe with nothing written and no cleanup; missing pending request and missing client throw; null/blank error code throws; the IdP-supplied description appears in no form in the page, not rendered, not escaped, not percent-encoded.AuthenticationCodeFlowHandlerTest(+3):prompt=nonedropped for MCP, kept for web login, andprompt=consentkept for MCP.Verified locally: 632/632 in
openmetadata-mcp, 94/94 acrossAuthenticationCodeFlowHandlerTest/AuthCallbackServletTest/SecurityUtilTest,spotless:checkclean. Reverting each fix turns the matching tests red.Backend integration tests
McpOAuthIT.Ingestion integration tests
Playwright (UI) tests
Manual testing performed
McpOAuthITagainst a local Testcontainers stack (MySQL + Elasticsearch).McpOAuthITruns a basic-auth server andMcpCallbackServletreturns 503 unless OIDC is configured at startup, the Playwrightsso-authspecs self-skip without SSO credentials, and the bundled Keycloak fixture is SAML-only while this bug is OIDC-specific. An OIDC fixture is the follow-up.UI screen recording / screenshots:
Not applicable.
Checklist:
Fixes <issue-number>: <short explanation>Fixes #<issue-number>above.Automatic Fixes PRs can be configured here.