Support managed AWS access keys for Amazon Bedrock (#40481)

## What changed

- Add the experimental `amazonBedrockAccessKeys` app-server login flow, persist its credentials in the configured auth store, and use them for SigV4-signed Bedrock requests.
- Report `bedrockAccessKeys` as a distinct auth mode and document access-key login alongside AWS profile and environment credential setup.
- Clear the selected Bedrock provider, AWS settings, and Bedrock-specific model on logout while leaving external AWS credentials and `.env` untouched.
- Reject managed access-key login when higher-precedence configuration would prevent its region or profile settings from taking effect.

## Testing

- Cover access-key login, storage, request signing, configuration conflicts, status reporting, and logout cleanup.

GitOrigin-RevId: bbf297887c6503d3651ceb2aad85c4d626e32723
This commit is contained in:
alexsong-oai
2026-08-24 21:39:48 +00:00
committed by copyberry
parent 219c203417
commit 4f6d43c267
60 changed files with 1464 additions and 389 deletions
+22 -1
View File
@@ -1416,6 +1416,7 @@ fn stored_auth_mode(auth: &codex_login::AuthDotJson) -> &'static str {
AuthMode::AgentIdentity => "agent_identity",
AuthMode::PersonalAccessToken => "personal_access_token",
AuthMode::BedrockApiKey => "bedrock_api_key",
AuthMode::BedrockAccessKeys => "bedrock_access_keys",
}
}
@@ -1427,6 +1428,8 @@ fn stored_auth_mode_value(auth: &AuthDotJson) -> AuthMode {
AuthMode::PersonalAccessToken
} else if auth.bedrock_api_key.is_some() {
AuthMode::BedrockApiKey
} else if auth.bedrock_access_keys.is_some() {
AuthMode::BedrockAccessKeys
} else if auth.openai_api_key.is_some() {
AuthMode::ApiKey
} else {
@@ -1509,6 +1512,17 @@ fn stored_auth_issues(
issues.push("Bedrock API key auth is missing a Bedrock API key");
}
}
AuthMode::BedrockAccessKeys => match auth.bedrock_access_keys.as_ref() {
Some(access_keys) => {
if access_keys.access_key_id.trim().is_empty() {
issues.push("Bedrock access key auth is missing an access key ID");
}
if access_keys.secret_access_key.trim().is_empty() {
issues.push("Bedrock access key auth is missing a secret access key");
}
}
None => issues.push("Bedrock access key auth is missing AWS access keys"),
},
}
issues
}
@@ -2495,6 +2509,7 @@ fn auth_mode_name(auth: &CodexAuth) -> &'static str {
AuthMode::AgentIdentity => "agent_identity",
AuthMode::PersonalAccessToken => "personal_access_token",
AuthMode::BedrockApiKey => "bedrock_api_key",
AuthMode::BedrockAccessKeys => "bedrock_access_keys",
}
}
@@ -2644,7 +2659,9 @@ fn provider_auth_reachability_mode_from_auth(
return ProviderAuthReachabilityMode::Chatgpt;
}
match stored_auth.map(stored_auth_mode_value) {
Some(AuthMode::ApiKey | AuthMode::BedrockApiKey) => ProviderAuthReachabilityMode::ApiKey,
Some(AuthMode::ApiKey | AuthMode::BedrockApiKey | AuthMode::BedrockAccessKeys) => {
ProviderAuthReachabilityMode::ApiKey
}
Some(
AuthMode::Chatgpt
| AuthMode::ChatgptAuthTokens
@@ -3566,6 +3583,7 @@ mod tests {
agent_identity: None,
personal_access_token: None,
bedrock_api_key: None,
bedrock_access_keys: None,
};
assert_eq!(
@@ -3585,6 +3603,7 @@ mod tests {
agent_identity: None,
personal_access_token: None,
bedrock_api_key: None,
bedrock_access_keys: None,
};
assert_eq!(
@@ -3606,6 +3625,7 @@ mod tests {
agent_identity: None,
personal_access_token: Some("at-test".to_string()),
bedrock_api_key: None,
bedrock_access_keys: None,
};
assert_eq!(stored_auth_mode(&auth), "personal_access_token");
@@ -3629,6 +3649,7 @@ mod tests {
agent_identity: None,
personal_access_token: None,
bedrock_api_key: None,
bedrock_access_keys: None,
};
assert_eq!(
+35 -11
View File
@@ -9,6 +9,8 @@
use codex_config::types::AuthCredentialsStoreMode;
use codex_core::config::Config;
use codex_core::config::edit::ConfigEdit;
use codex_core::config::edit::ConfigEditsBuilder;
use codex_login::AuthKeyringBackendKind;
use codex_login::AuthManager;
use codex_login::AuthRouteConfig;
@@ -489,6 +491,10 @@ pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
eprintln!("Logged in using Amazon Bedrock API key");
std::process::exit(0);
}
AuthMode::BedrockAccessKeys => {
eprintln!("Logged in using Amazon Bedrock AWS access keys");
std::process::exit(0);
}
},
Ok(None) => {
eprintln!("Not logged in");
@@ -505,7 +511,7 @@ pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides).await;
let auth_route_config = config.auth_route_config();
match logout_with_revoke(
let logged_out = match logout_with_revoke(
&config.codex_home,
config.cli_auth_credentials_store_mode,
config.auth_keyring_backend_kind(),
@@ -513,19 +519,37 @@ pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
)
.await
{
Ok(true) => {
eprintln!("Successfully logged out");
std::process::exit(0);
}
Ok(false) => {
eprintln!("Not logged in");
std::process::exit(0);
}
Err(e) => {
eprintln!("Error logging out: {e}");
Ok(logged_out) => logged_out,
Err(err) => {
eprintln!("Error logging out: {err}");
std::process::exit(1);
}
};
let cleared_bedrock_config =
if let Some(paths) = ConfigEditsBuilder::bedrock_provider_config_paths_to_clear(&config) {
let edits = paths
.into_iter()
.map(|segments| ConfigEdit::ClearPath { segments });
if let Err(err) = ConfigEditsBuilder::for_config(&config)
.with_edits(edits)
.apply()
.await
{
eprintln!("Error clearing Amazon Bedrock configuration after logout: {err}");
std::process::exit(1);
}
true
} else {
false
};
if logged_out || cleared_bedrock_config {
eprintln!("Successfully logged out");
} else {
eprintln!("Not logged in");
}
std::process::exit(0);
}
async fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config {