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
@@ -2584,6 +2584,13 @@ class McpServerOauthLoginCompletedNotification(BaseModel):
populate_by_name=True,
)
error: str | None = None
login_id: Annotated[
str | None,
Field(
alias="loginId",
description="Identifies the explicit login attempt. Older servers omit this field.",
),
] = None
name: str
success: bool
thread_id: Annotated[str | None, Field(alias="threadId")] = None
@@ -2611,6 +2618,13 @@ class McpServerOauthLoginResponse(BaseModel):
populate_by_name=True,
)
authorization_url: Annotated[str, Field(alias="authorizationUrl")]
login_id: Annotated[
str | None,
Field(
alias="loginId",
description="Identifies this login attempt across the response and completion notification. Older servers omit this field; current servers always return it.",
),
] = None
class McpServerRefreshResponse(BaseModel):