From ec31e78eca2b31dfb1ec0baa128e3a7dd6d08927 Mon Sep 17 00:00:00 2001 From: "Maxence - Meca.lu" Date: Fri, 14 Nov 2025 15:53:53 +0100 Subject: [PATCH] feat: add AWS profile support for Bedrock credentials - Add BEDROCK_AWS_PROFILE environment variable support - Implement AWS SDK credential provider chain for automatic refresh - Update credential loading logic to support profiles, static env vars, and user-provided credentials - Add logging for credential source transparency - Update .env.example with profile configuration documentation Follows S3 implementation pattern for credential handling. Enables users to configure AWS profiles with optional credential_process for automatic token refresh. --- .env.example | 23 ++++++++ .../services/Endpoints/bedrock/options.js | 55 ++++++++++++------- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index b98b7ac62f..fcba6ffde8 100644 --- a/.env.example +++ b/.env.example @@ -134,8 +134,31 @@ ANTHROPIC_API_KEY=user_provided #=================# # AWS Bedrock # #=================# +# AWS Bedrock Credentials Configuration +# +# Option 1: Use AWS Profile (RECOMMENDED) +# 1. Configure credentials in ~/.aws/credentials or ~/.aws/config +# 2. Set BEDROCK_AWS_PROFILE to your profile name +# The AWS SDK will automatically use the profile's credentials +# +# Optional: Enable auto-refresh by configuring credential_process in ~/.aws/config +# Example: +# [profile your-profile-name] +# region = us-west-2 +# credential_process = your-command-to-fetch-credentials --format json +# +# See: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html +# For auto-refresh: https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-sourcing-external.html +# +# Option 2: Use static environment variables (NOT recommended for temporary credentials) +# Set BEDROCK_AWS_ACCESS_KEY_ID, BEDROCK_AWS_SECRET_ACCESS_KEY, BEDROCK_AWS_SESSION_TOKEN # BEDROCK_AWS_DEFAULT_REGION=us-east-1 # A default region must be provided + +# AWS Profile (Option 1 - RECOMMENDED) +# BEDROCK_AWS_PROFILE=your-profile-name + +# Static Credentials (Option 2 - use only if not using profiles) # BEDROCK_AWS_ACCESS_KEY_ID=someAccessKey # BEDROCK_AWS_SECRET_ACCESS_KEY=someSecretAccessKey # BEDROCK_AWS_SESSION_TOKEN=someSessionToken diff --git a/api/server/services/Endpoints/bedrock/options.js b/api/server/services/Endpoints/bedrock/options.js index 0d02d09b07..c20e146078 100644 --- a/api/server/services/Endpoints/bedrock/options.js +++ b/api/server/services/Endpoints/bedrock/options.js @@ -7,12 +7,14 @@ const { removeNullishValues, } = require('librechat-data-provider'); const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService'); +const { logger } = require('@librechat/data-schemas'); const getOptions = async ({ req, overrideModel, endpointOption }) => { const { BEDROCK_AWS_SECRET_ACCESS_KEY, BEDROCK_AWS_ACCESS_KEY_ID, BEDROCK_AWS_SESSION_TOKEN, + BEDROCK_AWS_PROFILE, BEDROCK_REVERSE_PROXY, BEDROCK_AWS_DEFAULT_REGION, PROXY, @@ -20,28 +22,38 @@ const getOptions = async ({ req, overrideModel, endpointOption }) => { const expiresAt = req.body.key; const isUserProvided = BEDROCK_AWS_SECRET_ACCESS_KEY === AuthType.USER_PROVIDED; - let credentials = isUserProvided - ? await getUserKey({ userId: req.user.id, name: EModelEndpoint.bedrock }) - : { - accessKeyId: BEDROCK_AWS_ACCESS_KEY_ID, - secretAccessKey: BEDROCK_AWS_SECRET_ACCESS_KEY, - ...(BEDROCK_AWS_SESSION_TOKEN && { sessionToken: BEDROCK_AWS_SESSION_TOKEN }), - }; + let credentials; - if (!credentials) { - throw new Error('Bedrock credentials not provided. Please provide them again.'); - } + if (isUserProvided) { + // User-provided credentials from database + credentials = await getUserKey({ userId: req.user.id, name: EModelEndpoint.bedrock }); - if ( - !isUserProvided && - (credentials.accessKeyId === undefined || credentials.accessKeyId === '') && - (credentials.secretAccessKey === undefined || credentials.secretAccessKey === '') - ) { + if (!credentials) { + throw new Error('Bedrock credentials not provided. Please provide them again.'); + } + + if (expiresAt) { + checkUserKeyExpiry(expiresAt, EModelEndpoint.bedrock); + } + } else if (BEDROCK_AWS_ACCESS_KEY_ID && BEDROCK_AWS_SECRET_ACCESS_KEY) { + // Explicit credentials from environment variables + credentials = { + accessKeyId: BEDROCK_AWS_ACCESS_KEY_ID, + secretAccessKey: BEDROCK_AWS_SECRET_ACCESS_KEY, + ...(BEDROCK_AWS_SESSION_TOKEN && { sessionToken: BEDROCK_AWS_SESSION_TOKEN }), + }; + logger.info('[Bedrock] Using explicit credentials from environment variables'); + } else { + // Use AWS SDK default credential provider chain + // This supports: AWS profiles, IAM roles, EC2/ECS metadata, SSO, etc. credentials = undefined; - } - - if (expiresAt && isUserProvided) { - checkUserKeyExpiry(expiresAt, EModelEndpoint.bedrock); + if (BEDROCK_AWS_PROFILE) { + logger.info( + `[Bedrock] Using AWS credential provider chain with profile: ${BEDROCK_AWS_PROFILE}`, + ); + } else { + logger.info('[Bedrock] Using AWS credential provider chain with default profile'); + } } /* @@ -84,6 +96,11 @@ const getOptions = async ({ req, overrideModel, endpointOption }) => { llmConfig.credentials = credentials; } + // Pass AWS profile to the SDK if specified and no explicit credentials + if (!credentials && BEDROCK_AWS_PROFILE) { + llmConfig.profile = BEDROCK_AWS_PROFILE; + } + if (BEDROCK_REVERSE_PROXY) { llmConfig.endpointHost = BEDROCK_REVERSE_PROXY; }