mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: add Bedrock API key as a managed auth mode (#27443)
## Why Codex needs to manage Amazon Bedrock API key credentials through the existing auth lifecycle instead of introducing a separate auth manager or provider-specific credential file. Treating Bedrock API key login as a primary auth mode gives it the same persistence, keyring, reload, and logout behavior as the existing OpenAI API key and ChatGPT modes. The credential is valid only for the `amazon-bedrock` model provider. OpenAI-compatible providers must reject this auth mode rather than treating the Bedrock key as an OpenAI bearer token. ## What changed - Added `bedrockApiKey` as an app-server `AuthMode` and `CodexAuth::BedrockApiKey` as a primary `AuthManager` mode. - Added `BedrockApiKeyAuth`, containing the API key and AWS region, to the existing `AuthDotJson` payload stored in `$CODEX_HOME/auth.json` or the configured keyring backend. - Added `login_with_bedrock_api_key(...)`, parallel to `login_with_api_key(...)`, which replaces the current stored login with Bedrock credentials. - Reused generic auth reload and logout behavior instead of adding a Bedrock-specific auth manager or logout path. - Updated login restrictions, status reporting, diagnostics, telemetry classification, generated app-server schemas, and auth fixtures for the new mode. - Added explicit errors when Bedrock API key auth is selected with an OpenAI-compatible model provider. This PR establishes managed storage and auth-mode behavior. Routing the managed key and region into Amazon Bedrock requests will be in follow-up PRs.
This commit is contained in:
@@ -8,11 +8,15 @@ use codex_api::SharedAuthProvider;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_protocol::error::CodexErr;
|
||||
use http::HeaderMap;
|
||||
use http::HeaderValue;
|
||||
|
||||
use crate::bearer_auth_provider::BearerAuthProvider;
|
||||
|
||||
const BEDROCK_API_KEY_UNSUPPORTED_MESSAGE: &str =
|
||||
"Bedrock API key auth is only supported by the Amazon Bedrock model provider";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct AgentIdentityAuthProvider {
|
||||
auth: codex_login::auth::AgentIdentityAuth,
|
||||
@@ -79,6 +83,12 @@ pub(crate) fn resolve_provider_auth(
|
||||
auth: Option<&CodexAuth>,
|
||||
provider: &ModelProviderInfo,
|
||||
) -> codex_protocol::error::Result<SharedAuthProvider> {
|
||||
if matches!(auth, Some(CodexAuth::BedrockApiKey(_))) {
|
||||
return Err(CodexErr::UnsupportedOperation(
|
||||
BEDROCK_API_KEY_UNSUPPORTED_MESSAGE.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(auth) = bearer_auth_for_provider(provider)? {
|
||||
return Ok(Arc::new(auth));
|
||||
}
|
||||
@@ -109,6 +119,7 @@ pub fn auth_provider_from_auth(auth: &CodexAuth) -> SharedAuthProvider {
|
||||
CodexAuth::AgentIdentity(auth) => {
|
||||
Arc::new(AgentIdentityAuthProvider { auth: auth.clone() })
|
||||
}
|
||||
CodexAuth::BedrockApiKey(_) => unreachable!("{BEDROCK_API_KEY_UNSUPPORTED_MESSAGE}"),
|
||||
CodexAuth::ApiKey(_)
|
||||
| CodexAuth::Chatgpt(_)
|
||||
| CodexAuth::ChatgptAuthTokens(_)
|
||||
@@ -122,8 +133,10 @@ pub fn auth_provider_from_auth(auth: &CodexAuth) -> SharedAuthProvider {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use codex_login::auth::BedrockApiKeyAuth;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_model_provider_info::create_oss_provider_with_base_url;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -135,4 +148,21 @@ mod tests {
|
||||
|
||||
assert!(auth.to_auth_headers().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openai_provider_rejects_bedrock_api_key_auth() {
|
||||
let provider = ModelProviderInfo::create_openai_provider(/*base_url*/ None);
|
||||
let auth = CodexAuth::BedrockApiKey(BedrockApiKeyAuth {
|
||||
api_key: "bedrock-api-key-test".to_string(),
|
||||
region: "us-east-1".to_string(),
|
||||
});
|
||||
|
||||
match resolve_provider_auth(Some(&auth), &provider) {
|
||||
Err(CodexErr::UnsupportedOperation(message)) => {
|
||||
assert_eq!(message, BEDROCK_API_KEY_UNSUPPORTED_MESSAGE);
|
||||
}
|
||||
Err(err) => panic!("unexpected auth error: {err:?}"),
|
||||
Ok(_) => panic!("Bedrock API key auth should be rejected"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ pub struct ProviderAccountState {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProviderAccountError {
|
||||
MissingChatgptAccountDetails,
|
||||
UnsupportedBedrockApiKeyAuth,
|
||||
}
|
||||
|
||||
impl fmt::Display for ProviderAccountError {
|
||||
@@ -62,6 +63,12 @@ impl fmt::Display for ProviderAccountError {
|
||||
"email and plan type are required for chatgpt authentication"
|
||||
)
|
||||
}
|
||||
Self::UnsupportedBedrockApiKeyAuth => {
|
||||
write!(
|
||||
f,
|
||||
"Bedrock API key auth is only supported by the Amazon Bedrock model provider"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,6 +239,9 @@ impl ModelProvider for ConfiguredModelProvider {
|
||||
})
|
||||
.map(|auth| match &auth {
|
||||
CodexAuth::ApiKey(_) => Ok(ProviderAccount::ApiKey),
|
||||
CodexAuth::BedrockApiKey(_) => {
|
||||
Err(ProviderAccountError::UnsupportedBedrockApiKeyAuth)
|
||||
}
|
||||
CodexAuth::Chatgpt(_)
|
||||
| CodexAuth::ChatgptAuthTokens(_)
|
||||
| CodexAuth::AgentIdentity(_)
|
||||
@@ -287,6 +297,7 @@ impl ModelProvider for ConfiguredModelProvider {
|
||||
mod tests {
|
||||
use std::num::NonZeroU64;
|
||||
|
||||
use codex_login::auth::BedrockApiKeyAuth;
|
||||
use codex_model_provider_info::ModelProviderAwsAuthInfo;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_models_manager::manager::RefreshStrategy;
|
||||
@@ -374,6 +385,13 @@ mod tests {
|
||||
.expect("valid model")
|
||||
}
|
||||
|
||||
fn bedrock_api_key_auth() -> CodexAuth {
|
||||
CodexAuth::BedrockApiKey(BedrockApiKeyAuth {
|
||||
api_key: "bedrock-api-key-test".to_string(),
|
||||
region: "us-east-1".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_provider_uses_default_capabilities() {
|
||||
let provider = create_model_provider(
|
||||
@@ -491,6 +509,19 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openai_provider_rejects_bedrock_api_key_account_state() {
|
||||
let provider = create_model_provider(
|
||||
ModelProviderInfo::create_openai_provider(/*base_url*/ None),
|
||||
Some(AuthManager::from_auth_for_testing(bedrock_api_key_auth())),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
provider.account_state(),
|
||||
Err(ProviderAccountError::UnsupportedBedrockApiKeyAuth)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_non_openai_provider_returns_no_account_state() {
|
||||
let provider = create_model_provider(
|
||||
|
||||
Reference in New Issue
Block a user