Add AWS Bedrock auth mode with per-project configuration
All checks were successful
Build Container / build-container (push) Successful in 3m29s
All checks were successful
Build Container / build-container (push) Successful in 3m29s
Introduces a third auth mode alongside Login and API Key, allowing projects to authenticate Claude Code via AWS Bedrock. Includes support for static credentials, profile-based, and bearer-token auth methods with full UI controls. Also adds a URL accumulator to the terminal to reassemble long OAuth URLs split across hard newlines, and installs the AWS CLI v2 in the container image. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,15 @@ pub async fn start_project_container(
|
||||
// Auth state persists in the .claude config volume.
|
||||
None
|
||||
}
|
||||
AuthMode::Bedrock => {
|
||||
// Bedrock mode: no Anthropic API key needed, uses AWS credentials.
|
||||
let bedrock = project.bedrock_config.as_ref()
|
||||
.ok_or_else(|| "Bedrock auth mode selected but no Bedrock configuration found.".to_string())?;
|
||||
if bedrock.aws_region.is_empty() {
|
||||
return Err("AWS region is required for Bedrock auth mode.".to_string());
|
||||
}
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// Update status to starting
|
||||
|
||||
@@ -6,7 +6,7 @@ use bollard::models::{ContainerSummary, HostConfig, Mount, MountTypeEnum};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::client::get_docker;
|
||||
use crate::models::{container_config, ContainerInfo, Project};
|
||||
use crate::models::{container_config, AuthMode, BedrockAuthMethod, ContainerInfo, Project};
|
||||
|
||||
pub async fn find_existing_container(project: &Project) -> Result<Option<String>, String> {
|
||||
let docker = get_docker()?;
|
||||
@@ -78,6 +78,46 @@ pub async fn create_container(
|
||||
env_vars.push(format!("GIT_USER_EMAIL={}", email));
|
||||
}
|
||||
|
||||
// Bedrock configuration
|
||||
if project.auth_mode == AuthMode::Bedrock {
|
||||
if let Some(ref bedrock) = project.bedrock_config {
|
||||
env_vars.push("CLAUDE_CODE_USE_BEDROCK=1".to_string());
|
||||
env_vars.push(format!("AWS_REGION={}", bedrock.aws_region));
|
||||
|
||||
match bedrock.auth_method {
|
||||
BedrockAuthMethod::StaticCredentials => {
|
||||
if let Some(ref key_id) = bedrock.aws_access_key_id {
|
||||
env_vars.push(format!("AWS_ACCESS_KEY_ID={}", key_id));
|
||||
}
|
||||
if let Some(ref secret) = bedrock.aws_secret_access_key {
|
||||
env_vars.push(format!("AWS_SECRET_ACCESS_KEY={}", secret));
|
||||
}
|
||||
if let Some(ref token) = bedrock.aws_session_token {
|
||||
env_vars.push(format!("AWS_SESSION_TOKEN={}", token));
|
||||
}
|
||||
}
|
||||
BedrockAuthMethod::Profile => {
|
||||
if let Some(ref profile) = bedrock.aws_profile {
|
||||
env_vars.push(format!("AWS_PROFILE={}", profile));
|
||||
}
|
||||
}
|
||||
BedrockAuthMethod::BearerToken => {
|
||||
if let Some(ref token) = bedrock.aws_bearer_token {
|
||||
env_vars.push(format!("AWS_BEARER_TOKEN_BEDROCK={}", token));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref model) = bedrock.model_id {
|
||||
env_vars.push(format!("ANTHROPIC_MODEL={}", model));
|
||||
}
|
||||
|
||||
if bedrock.disable_prompt_caching {
|
||||
env_vars.push("DISABLE_PROMPT_CACHING=1".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut mounts = vec![
|
||||
// Project directory -> /workspace
|
||||
Mount {
|
||||
@@ -108,6 +148,26 @@ pub async fn create_container(
|
||||
});
|
||||
}
|
||||
|
||||
// AWS config mount (read-only, for profile-based auth)
|
||||
if project.auth_mode == AuthMode::Bedrock {
|
||||
if let Some(ref bedrock) = project.bedrock_config {
|
||||
if bedrock.auth_method == BedrockAuthMethod::Profile {
|
||||
if let Some(home) = dirs::home_dir() {
|
||||
let aws_dir = home.join(".aws");
|
||||
if aws_dir.exists() {
|
||||
mounts.push(Mount {
|
||||
target: Some("/home/claude/.aws".to_string()),
|
||||
source: Some(aws_dir.to_string_lossy().to_string()),
|
||||
typ: Some(MountTypeEnum::BIND),
|
||||
read_only: Some(true),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Docker socket (only if allowed)
|
||||
if project.allow_docker_access {
|
||||
mounts.push(Mount {
|
||||
|
||||
@@ -8,6 +8,7 @@ pub struct Project {
|
||||
pub container_id: Option<String>,
|
||||
pub status: ProjectStatus,
|
||||
pub auth_mode: AuthMode,
|
||||
pub bedrock_config: Option<BedrockConfig>,
|
||||
pub allow_docker_access: bool,
|
||||
pub ssh_key_path: Option<String>,
|
||||
pub git_token: Option<String>,
|
||||
@@ -30,11 +31,13 @@ pub enum ProjectStatus {
|
||||
/// How the project authenticates with Claude.
|
||||
/// - `Login`: User runs `claude login` inside the container (OAuth, persisted via config volume)
|
||||
/// - `ApiKey`: Uses the API key stored in the OS keychain
|
||||
/// - `Bedrock`: Uses AWS Bedrock with per-project AWS credentials
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AuthMode {
|
||||
Login,
|
||||
ApiKey,
|
||||
Bedrock,
|
||||
}
|
||||
|
||||
impl Default for AuthMode {
|
||||
@@ -43,6 +46,35 @@ impl Default for AuthMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// How Bedrock authenticates with AWS.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BedrockAuthMethod {
|
||||
StaticCredentials,
|
||||
Profile,
|
||||
BearerToken,
|
||||
}
|
||||
|
||||
impl Default for BedrockAuthMethod {
|
||||
fn default() -> Self {
|
||||
Self::StaticCredentials
|
||||
}
|
||||
}
|
||||
|
||||
/// AWS Bedrock configuration for a project.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BedrockConfig {
|
||||
pub auth_method: BedrockAuthMethod,
|
||||
pub aws_region: String,
|
||||
pub aws_access_key_id: Option<String>,
|
||||
pub aws_secret_access_key: Option<String>,
|
||||
pub aws_session_token: Option<String>,
|
||||
pub aws_profile: Option<String>,
|
||||
pub aws_bearer_token: Option<String>,
|
||||
pub model_id: Option<String>,
|
||||
pub disable_prompt_caching: bool,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn new(name: String, path: String) -> Self {
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
@@ -53,6 +85,7 @@ impl Project {
|
||||
container_id: None,
|
||||
status: ProjectStatus::Stopped,
|
||||
auth_mode: AuthMode::default(),
|
||||
bedrock_config: None,
|
||||
allow_docker_access: false,
|
||||
ssh_key_path: None,
|
||||
git_token: None,
|
||||
|
||||
Reference in New Issue
Block a user