summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/opencode/src/cli/cmd/auth.ts4
-rw-r--r--packages/opencode/src/provider/provider.ts19
-rw-r--r--packages/opencode/test/provider/amazon-bedrock.test.ts102
-rw-r--r--packages/web/src/content/docs/providers.mdx3
4 files changed, 97 insertions, 31 deletions
diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts
index 3dd7bcc35..bbaecfd8c 100644
--- a/packages/opencode/src/cli/cmd/auth.ts
+++ b/packages/opencode/src/cli/cmd/auth.ts
@@ -338,9 +338,9 @@ export const AuthLoginCommand = cmd({
prompts.log.info(
"Amazon Bedrock authentication priority:\n" +
" 1. Bearer token (AWS_BEARER_TOKEN_BEDROCK or /connect)\n" +
- " 2. AWS credential chain (profile, access keys, IAM roles)\n\n" +
+ " 2. AWS credential chain (profile, access keys, IAM roles, EKS IRSA)\n\n" +
"Configure via opencode.json options (profile, region, endpoint) or\n" +
- "AWS environment variables (AWS_PROFILE, AWS_REGION, AWS_ACCESS_KEY_ID).",
+ "AWS environment variables (AWS_PROFILE, AWS_REGION, AWS_ACCESS_KEY_ID, AWS_WEB_IDENTITY_TOKEN_FILE).",
)
}
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index 5374d76ee..c7559003e 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/packages/opencode/src/provider/provider.ts
@@ -197,16 +197,23 @@ export namespace Provider {
return undefined
})
- if (!profile && !awsAccessKeyId && !awsBearerToken) return { autoload: false }
+ const awsWebIdentityTokenFile = Env.get("AWS_WEB_IDENTITY_TOKEN_FILE")
- const { fromNodeProviderChain } = await import(await BunProc.install("@aws-sdk/credential-providers"))
-
- // Build credential provider options (only pass profile if specified)
- const credentialProviderOptions = profile ? { profile } : {}
+ if (!profile && !awsAccessKeyId && !awsBearerToken && !awsWebIdentityTokenFile) return { autoload: false }
const providerOptions: AmazonBedrockProviderSettings = {
region: defaultRegion,
- credentialProvider: fromNodeProviderChain(credentialProviderOptions),
+ }
+
+ // Only use credential chain if no bearer token exists
+ // Bearer token takes precedence over credential chain (profiles, access keys, IAM roles, web identity tokens)
+ if (!awsBearerToken) {
+ const { fromNodeProviderChain } = await import(await BunProc.install("@aws-sdk/credential-providers"))
+
+ // Build credential provider options (only pass profile if specified)
+ const credentialProviderOptions = profile ? { profile } : {}
+
+ providerOptions.credentialProvider = fromNodeProviderChain(credentialProviderOptions)
}
// Add custom endpoint if specified (endpoint takes precedence over baseURL)
diff --git a/packages/opencode/test/provider/amazon-bedrock.test.ts b/packages/opencode/test/provider/amazon-bedrock.test.ts
index 05f5bd01f..6b5cf681c 100644
--- a/packages/opencode/test/provider/amazon-bedrock.test.ts
+++ b/packages/opencode/test/provider/amazon-bedrock.test.ts
@@ -1,5 +1,6 @@
import { test, expect, mock } from "bun:test"
import path from "path"
+import { unlink } from "fs/promises"
// === Mocks ===
// These mocks are required because Provider.list() triggers:
@@ -118,29 +119,52 @@ test("Bedrock: loads when bearer token from auth.json is present", async () => {
})
const authPath = path.join(Global.Path.data, "auth.json")
- await Bun.write(
- authPath,
- JSON.stringify({
- "amazon-bedrock": {
- type: "api",
- key: "test-bearer-token",
- },
- }),
- )
- await Instance.provide({
- directory: tmp.path,
- init: async () => {
- Env.set("AWS_PROFILE", "")
- Env.set("AWS_ACCESS_KEY_ID", "")
- Env.set("AWS_BEARER_TOKEN_BEDROCK", "")
- },
- fn: async () => {
- const providers = await Provider.list()
- expect(providers["amazon-bedrock"]).toBeDefined()
- expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
- },
- })
+ // Save original auth.json if it exists
+ let originalAuth: string | undefined
+ try {
+ originalAuth = await Bun.file(authPath).text()
+ } catch {
+ // File doesn't exist, that's fine
+ }
+
+ try {
+ // Write test auth.json
+ await Bun.write(
+ authPath,
+ JSON.stringify({
+ "amazon-bedrock": {
+ type: "api",
+ key: "test-bearer-token",
+ },
+ }),
+ )
+
+ await Instance.provide({
+ directory: tmp.path,
+ init: async () => {
+ Env.set("AWS_PROFILE", "")
+ Env.set("AWS_ACCESS_KEY_ID", "")
+ Env.set("AWS_BEARER_TOKEN_BEDROCK", "")
+ },
+ fn: async () => {
+ const providers = await Provider.list()
+ expect(providers["amazon-bedrock"]).toBeDefined()
+ expect(providers["amazon-bedrock"].options?.region).toBe("eu-west-1")
+ },
+ })
+ } finally {
+ // Restore original or delete
+ if (originalAuth !== undefined) {
+ await Bun.write(authPath, originalAuth)
+ } else {
+ try {
+ await unlink(authPath)
+ } catch {
+ // Ignore errors if file doesn't exist
+ }
+ }
+ }
})
test("Bedrock: config profile takes precedence over AWS_PROFILE env var", async () => {
@@ -208,3 +232,37 @@ test("Bedrock: includes custom endpoint in options when specified", async () =>
},
})
})
+
+test("Bedrock: autoloads when AWS_WEB_IDENTITY_TOKEN_FILE is present", async () => {
+ await using tmp = await tmpdir({
+ init: async (dir) => {
+ await Bun.write(
+ path.join(dir, "opencode.json"),
+ JSON.stringify({
+ $schema: "https://opencode.ai/config.json",
+ provider: {
+ "amazon-bedrock": {
+ options: {
+ region: "us-east-1",
+ },
+ },
+ },
+ }),
+ )
+ },
+ })
+ await Instance.provide({
+ directory: tmp.path,
+ init: async () => {
+ Env.set("AWS_WEB_IDENTITY_TOKEN_FILE", "/var/run/secrets/eks.amazonaws.com/serviceaccount/token")
+ Env.set("AWS_ROLE_ARN", "arn:aws:iam::123456789012:role/my-eks-role")
+ Env.set("AWS_PROFILE", "")
+ Env.set("AWS_ACCESS_KEY_ID", "")
+ },
+ fn: async () => {
+ const providers = await Provider.list()
+ expect(providers["amazon-bedrock"]).toBeDefined()
+ expect(providers["amazon-bedrock"].options?.region).toBe("us-east-1")
+ },
+ })
+})
diff --git a/packages/web/src/content/docs/providers.mdx b/packages/web/src/content/docs/providers.mdx
index effdd8d3c..e1d684de0 100644
--- a/packages/web/src/content/docs/providers.mdx
+++ b/packages/web/src/content/docs/providers.mdx
@@ -211,12 +211,13 @@ To use Amazon Bedrock with OpenCode:
- **`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`**: Create an IAM user and generate access keys in the AWS Console
- **`AWS_PROFILE`**: Use named profiles from `~/.aws/credentials`. First configure with `aws configure --profile my-profile` or `aws sso login`
- **`AWS_BEARER_TOKEN_BEDROCK`**: Generate long-term API keys from the Amazon Bedrock console
+ - **`AWS_WEB_IDENTITY_TOKEN_FILE` / `AWS_ROLE_ARN`**: For EKS IRSA (IAM Roles for Service Accounts) or other Kubernetes environments with OIDC federation. These environment variables are automatically injected by Kubernetes when using service account annotations.
#### Authentication Precedence
Amazon Bedrock uses the following authentication priority:
1. **Bearer Token** - `AWS_BEARER_TOKEN_BEDROCK` environment variable or token from `/connect` command
- 2. **AWS Credential Chain** - Profile, access keys, shared credentials, IAM roles, instance metadata
+ 2. **AWS Credential Chain** - Profile, access keys, shared credentials, IAM roles, Web Identity Tokens (EKS IRSA), instance metadata
:::note
When a bearer token is set (via `/connect` or `AWS_BEARER_TOKEN_BEDROCK`), it takes precedence over all AWS credential methods including configured profiles.