Prerequisites
Description
When re-authorizing an existing OAuth-based MCP client (auth_type='oauth' or per_user_oauth), the frontend <OAuth2Authorizer /> prematurely closes the browser authorization popup on the very first polling tick (~2s) and fires complete-oauth. The backend rejects complete-oauth with HTTP 409 Conflict because the in-flight authorization flow is still pending, but the UI misinterprets the 409 as a successful race condition and shows a false "Success" toast.
As a result:
- The user's authorization popup is closed before they can approve consent at the upstream IdP.
- No authorization code is exchanged, and the expired/revoked OAuth token is never updated.
- The MCP client remains disconnected in
needs_reauth status despite the UI indicating success.
Root Cause
- Stale status polling in
getOAuthConfigStatus:
GET /api/oauth/config/{id}/status (transports/bifrost-http/handlers/mcpoauth2.go:175) reads oauth_configs.status. For an existing client that was authorized in the past, oauth_configs.status is permanently "authorized" (it never regresses to "pending" during reauth).
OAuth2Authorizer.tsx polling interval:
startPolling in ui/app/workspace/mcp-registry/views/oauth2Authorizer.tsx:49-65 polls getOAuthStatus(oauthConfigId) every 2 seconds. On the first tick, it receives status: "authorized" from oauth_configs. It immediately calls popupRef.current.close() and triggers handleOAuthComplete().
- Backend correctly detects premature completion:
completeMCPClientOAuth (transports/bifrost-http/handlers/mcp.go:3321) checks isPrematureOAuthCompletion(pendingFlow, time.Now()). Because the reauth flow in mcp_oauth_flows is still in status "pending", it returns 409 Conflict: "Authorization has not completed yet: the browser flow may still be open...".
- UI false success masking:
onConflict in ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx:1178-1193 catches the 409 error, treats it as "flow completion raced / double submit", and shows a green success toast while dismissing the modal.
Steps to reproduce
- Configure an MCP client using OAuth authentication (e.g. Notion or any OAuth server) and complete initial setup.
- Invalidate or let the credential expire so the client transitions to
needs_reauth.
- Click Re-authenticate in the MCP registry table.
- Click Continue in the confirmation dialog to open the OAuth popup window.
- Wait 2 seconds without interacting with the popup.
- Observe that the popup window automatically closes and the UI shows a green success toast ("MCP client re-authorized successfully"), while the client remains in
needs_reauth and cannot connect.
Expected behavior
The status endpoint should reflect the active mcp_oauth_flows state (or the authorizer should poll the specific flow / await the oauth_success postMessage callback from the popup before completing), and the popup should remain open until authentication is completed by the user.
Actual behavior
The popup closes after 2 seconds, complete-oauth returns 409, the UI displays a false success toast, and tokens are never refreshed.
Affected area(s)
- Transports (HTTP)
- UI (React)
- Framework
Version
dev @ 9dd8cfa16318433bd409c52c891caec551815dca (2026-08-24)
Severity
High (major functionality broken)
Prerequisites
Description
When re-authorizing an existing OAuth-based MCP client (
auth_type='oauth'orper_user_oauth), the frontend<OAuth2Authorizer />prematurely closes the browser authorization popup on the very first polling tick (~2s) and firescomplete-oauth. The backend rejectscomplete-oauthwithHTTP 409 Conflictbecause the in-flight authorization flow is stillpending, but the UI misinterprets the 409 as a successful race condition and shows a false "Success" toast.As a result:
needs_reauthstatus despite the UI indicating success.Root Cause
getOAuthConfigStatus:GET /api/oauth/config/{id}/status(transports/bifrost-http/handlers/mcpoauth2.go:175) readsoauth_configs.status. For an existing client that was authorized in the past,oauth_configs.statusis permanently"authorized"(it never regresses to"pending"during reauth).OAuth2Authorizer.tsxpolling interval:startPollinginui/app/workspace/mcp-registry/views/oauth2Authorizer.tsx:49-65pollsgetOAuthStatus(oauthConfigId)every 2 seconds. On the first tick, it receivesstatus: "authorized"fromoauth_configs. It immediately callspopupRef.current.close()and triggershandleOAuthComplete().completeMCPClientOAuth(transports/bifrost-http/handlers/mcp.go:3321) checksisPrematureOAuthCompletion(pendingFlow, time.Now()). Because the reauth flow inmcp_oauth_flowsis still in status"pending", it returns409 Conflict:"Authorization has not completed yet: the browser flow may still be open...".onConflictinui/app/workspace/mcp-registry/views/mcpClientsTable.tsx:1178-1193catches the 409 error, treats it as "flow completion raced / double submit", and shows a green success toast while dismissing the modal.Steps to reproduce
needs_reauth.needs_reauthand cannot connect.Expected behavior
The status endpoint should reflect the active
mcp_oauth_flowsstate (or the authorizer should poll the specific flow / await theoauth_successpostMessage callback from the popup before completing), and the popup should remain open until authentication is completed by the user.Actual behavior
The popup closes after 2 seconds,
complete-oauthreturns 409, the UI displays a false success toast, and tokens are never refreshed.Affected area(s)
Version
dev @
9dd8cfa16318433bd409c52c891caec551815dca(2026-08-24)Severity
High (major functionality broken)