Enable enterprise MCP sign-in and account-scoped grant cleanup (#49276)

## Why

The app server previously rejected OAuth login for enterprise-managed MCP servers. These servers need an explicit sign-in flow that ties saved authorization to the active Codex user and workspace and prevents canceled or superseded attempts from saving credentials.

## What changed

- Enable enterprise sign-in through `mcpServer/oauth/login` for configured servers with a connected thread, trusted IdP registration, and `features.use_xaa` enabled. Scope shared enterprise credentials by user, workspace, issuer, and client ID.
- Add `loginId` to MCP OAuth login responses and completion notifications, preserving compatibility with older servers that omit it.
- Support enterprise login replacement and `account/login/cancel`, wait for callback listener shutdown before replacement, and recheck account and configuration before saving grants. Fence stale attempts across processes and account changes.
- Remove shared enterprise authorization during account logout and `codex mcp logout`, including when the feature is disabled. Continue account logout if enterprise cleanup fails, and invalidate MCP runtimes on account changes.

Start a fresh session after successful enterprise sign-in to use the saved grant.

## Testing

Add regression coverage for login replacement and cancellation, callback port reuse, account changes during setup, cross-process logout invalidation, persisted workspace policy selection, cleanup failures, and responses from older servers without `loginId`.

GitOrigin-RevId: 7ed4caaab4659ed1e67064262cf5cee48e3a26ac
This commit is contained in:
Nick Steele
2026-09-29 14:46:33 +00:00
committed by copyberry
parent 677b07eca0
commit 4994306e9f
43 changed files with 1994 additions and 147 deletions
+3 -1
View File
@@ -473,6 +473,7 @@ impl MessageProcessor {
thread_state_manager.clone(),
outgoing.clone(),
config_manager.clone(),
account_processor.clone(),
);
let plugin_processor = PluginRequestProcessor::new(
auth_manager.clone(),
@@ -1680,7 +1681,8 @@ impl MessageProcessor {
self.turn_processor.review_start(&request_id, params).await
}
ClientRequest::McpServerOauthLogin { params, .. } => {
self.mcp_processor.mcp_server_oauth_login(params).await
// Keep MCP discovery's large future from inflating every request.
Box::pin(self.mcp_processor.mcp_server_oauth_login(params)).await
}
ClientRequest::McpServerRefresh { params, .. } => {
self.mcp_processor.mcp_server_refresh(params).await