diff --git a/.changeset/bedrock-static-keys-session-token.md b/.changeset/bedrock-static-keys-session-token.md new file mode 100644 index 000000000000..690487c21bba --- /dev/null +++ b/.changeset/bedrock-static-keys-session-token.md @@ -0,0 +1,9 @@ +--- +'@ai-sdk/amazon-bedrock': patch +--- + +fix(amazon-bedrock): do not merge AWS_SESSION_TOKEN from env with explicit access keys + +When `accessKeyId` and `secretAccessKey` are both passed as options, session credentials now use only `options.sessionToken` or omit the token — avoiding workload tokens (e.g. EKS IRSA) invalidating SigV4 for static IAM user keys. + +Docs: align Amazon Bedrock provider page with this behavior. diff --git a/content/providers/01-ai-sdk-providers/08-amazon-bedrock.mdx b/content/providers/01-ai-sdk-providers/08-amazon-bedrock.mdx index 929b8b98b61a..df88aa361e44 100644 --- a/content/providers/01-ai-sdk-providers/08-amazon-bedrock.mdx +++ b/content/providers/01-ai-sdk-providers/08-amazon-bedrock.mdx @@ -133,12 +133,10 @@ const bedrock = createAmazonBedrock({ ``` - The credentials settings fall back to environment variable defaults described - below. These may be set by your serverless environment without your awareness, - which can lead to merged/conflicting credential values and provider errors - around failed authentication. If you're experiencing issues be sure you are - explicitly specifying all settings (even if `undefined`) to avoid any - defaults. + Omitted options use the environment variables below. When **both** + `accessKeyId` and `secretAccessKey` are strings, SigV4 uses a **`sessionToken` + only if you pass one**—not `AWS_SESSION_TOKEN` from the environment—so static + keys are not mixed with workload tokens (e.g. EKS IRSA). You can use the following optional settings to customize the Amazon Bedrock provider instance: @@ -160,8 +158,10 @@ You can use the following optional settings to customize the Amazon Bedrock prov - **sessionToken** _string_ - Optional. The AWS session token that you want to use for the API calls. - It uses the `AWS_SESSION_TOKEN` environment variable by default. + Optional. For temporary credentials. With **both** access keys set as strings, + pass the token here if needed; `AWS_SESSION_TOKEN` from the environment is not + used. If either key is taken from the environment, omitting `sessionToken` + allows `AWS_SESSION_TOKEN`. - **credentialProvider** _() => Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken?: string; }>_ @@ -1359,8 +1359,10 @@ You can use the following optional settings to customize the Bedrock Anthropic p - **sessionToken** _string_ - Optional. The AWS session token that you want to use for the API calls. - It uses the `AWS_SESSION_TOKEN` environment variable by default. + Optional. For temporary credentials. With **both** access keys set as strings, + pass the token here if needed; `AWS_SESSION_TOKEN` from the environment is not + used. If either key is taken from the environment, omitting `sessionToken` + allows `AWS_SESSION_TOKEN`. - **apiKey** _string_ @@ -1618,9 +1620,4 @@ dependency on the `@aws-sdk/client-bedrock-runtime` package. The `bedrockOptions` provider setting previously available has been removed. If you were using the `bedrockOptions` object, you should now use the `region`, `accessKeyId`, `secretAccessKey`, and `sessionToken` settings directly instead. - -Note that you may need to set all of these explicitly, e.g. even if you're not -using `sessionToken`, set it to `undefined`. If you're running in a serverless -environment, there may be default environment variables set by your containing -environment that the Amazon Bedrock provider will then pick up and could -conflict with the ones you're intending to use. +Static IAM user keys do not require `sessionToken`. diff --git a/packages/amazon-bedrock/src/bedrock-provider.test.ts b/packages/amazon-bedrock/src/bedrock-provider.test.ts index 1bdb3298e8d3..440ca36bfb81 100644 --- a/packages/amazon-bedrock/src/bedrock-provider.test.ts +++ b/packages/amazon-bedrock/src/bedrock-provider.test.ts @@ -391,6 +391,49 @@ describe('AmazonBedrockProvider', () => { ); }); + it('does not use AWS_SESSION_TOKEN from env when both access keys are passed as options', async () => { + mockLoadOptionalSetting.mockImplementation( + ({ settingValue, environmentVariableName }) => { + if (environmentVariableName === 'AWS_BEARER_TOKEN_BEDROCK') { + return settingValue; + } + if (environmentVariableName === 'AWS_SESSION_TOKEN') { + return 'env-session-token-should-not-be-used'; + } + return settingValue; + }, + ); + + createAmazonBedrock({ + region: 'us-east-1', + accessKeyId: 'from-options-ak', + secretAccessKey: 'from-options-sk', + }); + + const getCredentials = mockCreateSigV4FetchFunction.mock.calls[0][0]; + await expect(getCredentials()).resolves.toMatchObject({ + accessKeyId: 'from-options-ak', + secretAccessKey: 'from-options-sk', + sessionToken: undefined, + }); + }); + + it('uses options.sessionToken when both access keys are passed as options', async () => { + mockLoadOptionalSetting.mockImplementation(() => undefined); + + createAmazonBedrock({ + region: 'us-east-1', + accessKeyId: 'from-options-ak', + secretAccessKey: 'from-options-sk', + sessionToken: 'explicit-session', + }); + + const getCredentials = mockCreateSigV4FetchFunction.mock.calls[0][0]; + await expect(getCredentials()).resolves.toMatchObject({ + sessionToken: 'explicit-session', + }); + }); + it('should work with credential provider when no API key is provided', () => { // Mock loadOptionalSetting to return undefined (no API key) mockLoadOptionalSetting.mockImplementation(() => undefined); diff --git a/packages/amazon-bedrock/src/bedrock-provider.ts b/packages/amazon-bedrock/src/bedrock-provider.ts index 860c098927aa..bc2af54de882 100644 --- a/packages/amazon-bedrock/src/bedrock-provider.ts +++ b/packages/amazon-bedrock/src/bedrock-provider.ts @@ -74,8 +74,12 @@ export interface AmazonBedrockProviderSettings { secretAccessKey?: string; /** - * The AWS session token to use for the Bedrock provider. Defaults to the value of the - * `AWS_SESSION_TOKEN` environment variable. + * The AWS session token to use for the Bedrock provider. When `accessKeyId` and + * `secretAccessKey` are both passed explicitly as options, only this field is used + * for the session token — **`AWS_SESSION_TOKEN` is not read from the environment** + * (so workload tokens from e.g. EKS IRSA are not mixed with static access keys). + * If either access key field is omitted and resolved from the environment, the + * session token also falls back to `AWS_SESSION_TOKEN` when not set here. */ sessionToken?: string; @@ -221,10 +225,16 @@ export function createAmazonBedrock( environmentVariableName: 'AWS_SECRET_ACCESS_KEY', description: 'AWS secret access key', }), - sessionToken: loadOptionalSetting({ - settingValue: options.sessionToken, - environmentVariableName: 'AWS_SESSION_TOKEN', - }), + sessionToken: + typeof options.accessKeyId === 'string' && + typeof options.secretAccessKey === 'string' + ? typeof options.sessionToken === 'string' + ? options.sessionToken + : undefined + : loadOptionalSetting({ + settingValue: options.sessionToken, + environmentVariableName: 'AWS_SESSION_TOKEN', + }), }; } catch (error) { // Provide helpful error message for missing AWS credentials