Files
codex/codex-rs/app-server/src/request_processors/bedrock_auth.rs
T
Celia Chen 6e215e0db5 Handle Amazon Bedrock credentials during logout (#33175)
## Why

Amazon Bedrock can use either a Codex-managed API key or credentials managed by
AWS, so logout must not remove or misrepresent credentials that Codex does not
control.

## What changed

- Remove the managed Bedrock key on logout and clear `model_provider` only when
  its user-config value is still `amazon-bedrock`.
- Reject logout for AWS-managed Bedrock credentials without changing existing
  authentication or configuration.
- Add a `test-logout` app-server test-client command that waits for the
  resulting `account/updated` notification.

## Testing

Add coverage for managed and AWS-managed Bedrock logout, concurrent provider
changes, config reload failures, and conditional user-config cleanup.

GitOrigin-RevId: a003c0ec27ad7b36d499fe7b63ab4a0dd6369b4f
2026-07-14 21:54:24 +00:00

87 lines
3.2 KiB
Rust

use super::config_processor::map_error as map_config_error;
use crate::config_manager::ConfigManager;
use crate::error_code::internal_error;
use crate::error_code::invalid_request;
use codex_app_server_protocol::ConfigValueWriteParams;
use codex_app_server_protocol::ConfigWriteErrorCode;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::MergeStrategy;
use codex_config::CONFIG_TOML_FILE;
use codex_config::ConfigLayerSource;
use codex_config::format_config_layer_source;
use codex_model_provider::AMAZON_BEDROCK_PROVIDER_ID;
pub(super) async fn set_user_model_provider_to_bedrock(
config_manager: &ConfigManager,
) -> Result<(), JSONRPCErrorError> {
let layers = config_manager
.load_config_layers(/*cwd*/ None)
.await
.map_err(|err| internal_error(format!("failed to load configuration layers: {err}")))?;
let user_precedence = match layers.get_active_user_layer() {
Some(layer) => layer.name.precedence(),
None => ConfigLayerSource::User {
file: config_manager.user_config_path().map_err(|err| {
internal_error(format!("failed to resolve user config path: {err}"))
})?,
profile: None,
}
.precedence(),
};
if let Some((overriding_layer, effective_provider)) = layers
.layers_high_to_low()
.into_iter()
.filter(|layer| layer.name.precedence() > user_precedence)
.find_map(|layer| {
layer
.config
.get("model_provider")
.map(|value| (layer, value))
})
&& effective_provider.as_str() != Some(AMAZON_BEDROCK_PROVIDER_ID)
{
let source = format_config_layer_source(&overriding_layer.name, CONFIG_TOML_FILE);
return Err(invalid_request(format!(
"Amazon Bedrock login cannot select `{AMAZON_BEDROCK_PROVIDER_ID}` because {source} sets `model_provider` to {effective_provider}"
)));
}
config_manager
.write_value(ConfigValueWriteParams {
key_path: "model_provider".to_string(),
value: serde_json::json!(AMAZON_BEDROCK_PROVIDER_ID),
merge_strategy: MergeStrategy::Replace,
file_path: None,
expected_version: None,
})
.await
.map(|_| ())
.map_err(map_config_error)
}
pub(super) async fn clear_user_model_provider_if_bedrock(
config_manager: &ConfigManager,
) -> Result<(), JSONRPCErrorError> {
let result = config_manager
.clear_user_value_if_matches(
"model_provider",
serde_json::json!(AMAZON_BEDROCK_PROVIDER_ID),
)
.await;
if let Err(err) = &result
&& err.write_error_code() == Some(ConfigWriteErrorCode::ConfigVersionConflict)
{
tracing::warn!(
"configuration changed while clearing the managed Amazon Bedrock model provider; retrying once"
);
return config_manager
.clear_user_value_if_matches(
"model_provider",
serde_json::json!(AMAZON_BEDROCK_PROVIDER_ID),
)
.await
.map_err(map_config_error);
}
result.map_err(map_config_error)
}