diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index a4f75d1541..1cd7f6c5d5 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -1930,7 +1930,7 @@ Codex supports these authentication modes. The current mode is surfaced in `acco - `account/login/start` — begin login (`apiKey`, `chatgpt`, `chatgptDeviceCode`, `amazonBedrock`). - `account/login/completed` (notify) — emitted when a login attempt finishes (success or error). - `account/login/cancel` — cancel a pending managed ChatGPT login by `loginId`. -- `account/logout` — sign out; triggers `account/updated`. +- `account/logout` — sign out; triggers `account/updated` on success. - `account/updated` (notify) — emitted whenever auth mode changes (`authMode`: `apikey`, `bedrockApiKey`, `chatgpt`, `personalAccessToken`, or `null`) and includes the current ChatGPT `planType` when available. - `account/rateLimits/read` — fetch ChatGPT rate limits, an optional effective monthly credit limit, and the earned rate-limit resets currently available, including expiry details when provided by the backend. Rate-limit updates arrive via `account/rateLimits/updated` (notify); reset-credit data is snapshot-only. - `account/rateLimitResetCredit/consume` — consume one earned reset using a caller-provided idempotency key, optionally selecting a reset-credit ID returned by `account/rateLimits/read`. @@ -2060,7 +2060,7 @@ Codex stores the key and region as the primary Codex auth, replacing any previou { "method": "account/updated", "params": { "authMode": null, "planType": null } } ``` -When Codex-managed Amazon Bedrock auth is stored, logout removes that credential and clears the user-level `model_provider` only when it is still `"amazon-bedrock"`. A concurrent user config change is preserved. For AWS-managed Bedrock auth, logout is a no-op because the AWS SDK credential chain is managed outside Codex. +When Codex-managed Amazon Bedrock auth is stored, logout removes that credential and clears the user-level `model_provider` only when it is still `"amazon-bedrock"`. A concurrent user config change is preserved. When Amazon Bedrock uses AWS-managed credentials, logout returns an error because the AWS SDK credential chain is managed outside Codex. Manage those credentials through AWS, or switch model providers before logging out Codex authentication. ### 7) Rate limits (ChatGPT) diff --git a/codex-rs/app-server/src/request_processors/account_processor.rs b/codex-rs/app-server/src/request_processors/account_processor.rs index 79d077750e..a598542247 100644 --- a/codex-rs/app-server/src/request_processors/account_processor.rs +++ b/codex-rs/app-server/src/request_processors/account_processor.rs @@ -833,26 +833,23 @@ impl AccountRequestProcessor { } async fn logout_common(&self) -> std::result::Result, JSONRPCErrorError> { - // Cancel any active login attempt. - { - let mut guard = self.active_login.lock().await; - if let Some(active) = guard.take() { - drop(active); - } - } - let managed_bedrock_auth = matches!( self.auth_manager.auth_cached(), Some(CodexAuth::BedrockApiKey(_)) ); let config = self.load_latest_config().await; if config.model_provider.is_amazon_bedrock() && !managed_bedrock_auth { - return Ok(self - .auth_manager - .auth_cached() - .as_ref() - .map(CodexAuth::api_auth_mode) - .map(auth_mode_to_api)); + return Err(invalid_request( + "cannot log out while Amazon Bedrock is using AWS-managed credentials; manage those credentials through AWS or switch model providers before logging out Codex authentication", + )); + } + + // Cancel any active login attempt. + { + let mut guard = self.active_login.lock().await; + if let Some(active) = guard.take() { + drop(active); + } } match self.auth_manager.logout_with_revoke().await { diff --git a/codex-rs/app-server/tests/suite/v2/account.rs b/codex-rs/app-server/tests/suite/v2/account.rs index 909f099825..09a2bf7d95 100644 --- a/codex-rs/app-server/tests/suite/v2/account.rs +++ b/codex-rs/app-server/tests/suite/v2/account.rs @@ -1387,7 +1387,7 @@ async fn logout_managed_bedrock_restores_default_account() -> Result<()> { } #[tokio::test] -async fn logout_aws_managed_bedrock_preserves_openai_auth_and_config() -> Result<()> { +async fn logout_aws_managed_bedrock_errors_without_changing_auth_or_config() -> Result<()> { let codex_home = TempDir::new()?; create_config_toml(codex_home.path(), aws_managed_bedrock_config())?; login_with_api_key( @@ -1407,18 +1407,18 @@ async fn logout_aws_managed_bedrock_preserves_openai_auth_and_config() -> Result .await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; let request_id = mcp.send_logout_account_request().await?; - let response = timeout( + let error = timeout( DEFAULT_READ_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(request_id)), + mcp.read_stream_until_error_message(RequestId::Integer(request_id)), ) .await??; + assert_eq!(error.error.code, -32600); assert_eq!( - to_response::(response)?, - LogoutAccountResponse {} + error.error.message, + "cannot log out while Amazon Bedrock is using AWS-managed credentials; manage those credentials through AWS or switch model providers before logging out Codex authentication" ); assert_eq!(load_file_auth(codex_home.path())?, expected_auth); assert_eq!(read_config_toml(codex_home.path())?, expected_config); - assert_account_updated(&mut mcp, Some(AuthMode::ApiKey)).await?; Ok(()) }