Amazon Bedrock AgentCore is in preview release and is subject to change.
Authenticate and authorize with Inbound Auth and Outbound Auth
This section shows you how to implement authentication and authorization for your agent runtime using OAuth and JWT bearer tokens with AgentCore Identity. You'll learn how to set up Cognito user pools, configure your agent runtime for JWT authentication (Inbound Auth), and implement OAuth-based access to third-party resources (outbound Auth).
For a complete example, see https://github.com/awslabs/amazon-bedrock-agentcore-samples/
For information about using OAuth to with an MCP server, see Deploy MCP servers in AgentCore Runtime.
Amazon Bedrock AgentCore runtime provides two authentication mechanisms for hosted agents:
- IAM SigV4 Authentication
-
The default authentication and authorization mechanism that works automatically without additional configuration, similar to other AWS APIs.
If your solution requires the hosted agent to retrieve OAuth tokens on behalf of end users (using Authorization Code Grant), you can specify the user identifier by including the
X-Amzn-Bedrock-AgentCore-Runtime-User-Id
header in your requests. - JWT Bearer Token Authentication
-
You can configure your agent runtime to accept JWT bearer tokens by providing authorizer configuration during agent creation.
This configuration requires:
-
Discovery URL - A string that must match the pattern
^.+/\.well-known/openid-configuration$
for OpenID Connect discovery URLs -
Allowed audiences - A list of permitted audiences that will be validated against the aud claim in the JWT token
-
Allowed clients - A list of permitted client identifiers that will be validated against the client_id claim in the JWT token
-
Note
A runtime can only support either IAM SigV4 or JWT Bearer Token based inbound auth. You can always create custom endpoints for your runtime and configure them for different inbound auth types.
When you create a runtime with Amazon Bedrock AgentCore, a Workload Identity is created automatically for your runtime with AgentCore Identity service.
Prerequisites
Before you begin, make sure you have:
-
An AWS account with appropriate permissions
-
Basic understanding of Python programming
-
Familiarity with Docker containers (for advanced deployment)
-
Set up a basic agent with runtime successfully
-
Basic understanding of OAuth authorization, mainly JWT bearer tokens, claims, and the various grant flows
JWT inbound authorization and OAuth outbound access sample
This guide walks you through the process of setting up your agent runtime to be invoked with an OAuth compliant access token using JWT format. The sample agent will be authorized using AWS Cognito access tokens. Later, you'll also learn how the agent code can fetch Google tokens on behalf of the user to check Google Drive and fetch contents.
What you'll learn
In this guide, you'll learn how to:
-
Set up Cognito user pool, add a user, and get a bearer token for the user
-
Set up your agent runtime to use the Cognito user pool for authorization
-
Set up your agent code to fetch OAuth tokens on behalf of the user to call tools
Step 1: Prepare your agent
Start by creating a basic agent with the following structure:
## Project Folder Structure your_project_directory/ ├── agent_example.py # Your main agent code ├── requirements.txt # Dependencies for your agent └── __init__.py # Makes the directory a Python package
Create the following files with their respective contents:
agent_example.py
This is your main agent code:
from strands import Agent from bedrock_agentcore.runtime import BedrockAgentCoreApp agent = Agent() app = BedrockAgentCoreApp() @app.entrypoint def invoke(payload): """Process user input and return a response""" user_message = payload.get("prompt", "Hello") response = agent(user_message) return str(response) # response should be json serializable if __name__ == "__main__": app.run()
requirements.txt
This file lists the dependencies for your agent:
strands-agents bedrock-agentcore
Step 2: Set up AWS Cognito user pool and add a user
To set up a Cognito user pool and create a user, you'll use a shell script that automates the process.
For more information, see Step 2: Set up an OAuth 2.0 Credential Provider.
To set up Cognito user pool and create a user
-
Create a file named
setup_cognito.sh
with the following content:#!/bin/bash # Create User Pool and capture Pool ID directly export POOL_ID=$(aws cognito-idp create-user-pool \ --pool-name "MyUserPool" \ --policies '{"PasswordPolicy":{"MinimumLength":8}}' \ --region us-east-1 | jq -r '.UserPool.Id') # Create App Client and capture Client ID directly export CLIENT_ID=$(aws cognito-idp create-user-pool-client \ --user-pool-id $POOL_ID \ --client-name "MyClient" \ --no-generate-secret \ --explicit-auth-flows "ALLOW_USER_PASSWORD_AUTH" "ALLOW_REFRESH_TOKEN_AUTH" \ --region us-east-1 | jq -r '.UserPoolClient.ClientId') # Create User aws cognito-idp admin-create-user \ --user-pool-id $POOL_ID \ --username "testuser" \ --temporary-password "Temp123!" \ --region us-east-1 \ --message-action SUPPRESS > /dev/null # Set Permanent Password aws cognito-idp admin-set-user-password \ --user-pool-id $POOL_ID \ --username "testuser" \ --password "MyPassword123!" \ --region us-east-1 \ --permanent > /dev/null # Authenticate User and capture Access Token export BEARER_TOKEN=$(aws cognito-idp initiate-auth \ --client-id "$CLIENT_ID" \ --auth-flow USER_PASSWORD_AUTH \ --auth-parameters USERNAME='testuser',PASSWORD='MyPassword123!' \ --region us-east-1 | jq -r '.AuthenticationResult.AccessToken') # Output the required values echo "Pool id: $POOL_ID" echo "Discovery URL: https://cognito-idp.us-east-1.amazonaws.com/$POOL_ID/.well-known/openid-configuration" echo "Client ID: $CLIENT_ID" echo "Bearer Token: $BEARER_TOKEN"
-
Run the script to create the Cognito resources:
source setup_cognito.sh
-
Note the output values, which will look similar to:
Pool id: us-east-1_poolid Discovery URL: https://cognito-idp.us-east-1.amazonaws.com/us-east-1_userpoolid/.well-known/openid-configuration Client ID: clientid Bearer Token: bearertoken
You'll need these values in the next steps.
This script creates a Cognito user pool, a user pool client, adds a user, and generates a bearer token for the user. The token is valid for 60 minutes by default.
Step 3: Deploy your agent
Now you'll deploy your agent with JWT authorization using the Cognito user pool you created.
Step 4: Use bearer token to invoke your agent
Now that your agent is deployed with JWT authorization, you can invoke it using the bearer token.
Make sure your agent’s execution role has permissions to access the workload identity.
"Sid": "GetAgentAccessToken", "Effect": "Allow", "Action": [ "bedrock-agentcore:GetWorkloadAccessToken", "bedrock-agentcore:GetWorkloadAccessTokenForJWT", "bedrock-agentcore:GetWorkloadAccessTokenForUserId" ], # point to the workload identity for the runtime; the workload identity can be found in # the GetAgentRuntime response and has your agent name in it. "Resource": [ "arn:aws:bedrock-agentcore:
region
:account-id
:workload-identity-directory/default", "arn:aws:bedrock-agentcore:region
:account-id
:workload-identity-directory/default/workload-identity/agentname
-*" ] }
Fetch a bearer token for the user you created with Amazon Cognito.
# use the password and other details used when you created the cognito user export TOKEN=$(aws cognito-idp initiate-auth \ --client-id "$CLIENT_ID" \ --auth-flow USER_PASSWORD_AUTH \ --auth-parameters USERNAME='testuser',PASSWORD='MyPassword123!' \ --region us-east-1 | jq -r '.AuthenticationResult.AccessToken')
Using Curl
// Invoke with OAuth token export BEDROCK_AGENT_CORE_ENDPOINT_URL="https://bedrock-agentcore.us-west-2.amazonaws.com" curl -v -X POST "${BEDROCK_AGENT_CORE_ENDPOINT_URL}/runtimes/${ESCAPED_AGENT_ARN}/invocations?qualifier=DEFAULT" \ -H "Authorization: Bearer ${TOKEN}" \ -H "X-Amzn-Trace-Id:
your-trace-id
" \ -H "Content-Type: application/json" \ -H "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id:your-session-id
" \ -d ${PAYLOAD}
Using Python
Since boto3 doesn't support invocation with bearer tokens, you'll need to use an HTTP client like the requests library in Python.
To invoke your agent with a bearer token
-
Create a Python script named
invoke_agent.py
with the following content:import requests import urllib.parse import json import os # Configuration Constants REGION_NAME = "us-east-1" # === Agent Invocation Demo === invoke_agent_arn = "YOUR_AGENT_ARN_HERE" auth_token = os.environ.get('TOKEN') print(f"Using Agent ARN from environment: {invoke_agent_arn}") # URL encode the agent ARN escaped_agent_arn = urllib.parse.quote(invoke_agent_arn, safe='') # Construct the URL url = f"https://bedrock-agentcore.{REGION_NAME}.amazonaws.com/runtimes/{escaped_agent_arn}/invocations?qualifier=DEFAULT" # Set up headers headers = { "Authorization": f"Bearer {auth_token}", "X-Amzn-Trace-Id": "your-trace-id", "Content-Type": "application/json", "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": "testsession123" } # Enable verbose logging for requests import logging logging.basicConfig(level=logging.DEBUG) logging.getLogger("urllib3.connectionpool").setLevel(logging.DEBUG) invoke_response = requests.post( url, headers=headers, data=json.dumps({"payload": '{"prompt": "Hello"}'}) ) # Print response in a safe manner print(f"Status Code: {invoke_response.status_code}") print(f"Response Headers: {dict(invoke_response.headers)}") # Handle response based on status code if invoke_response.status_code == 200: response_data = invoke_response.json() print("Response JSON:") print(json.dumps(response_data, indent=2)) elif invoke_response.status_code >= 400: print(f"Error Response ({invoke_response.status_code}):") error_data = invoke_response.json() print(json.dumps(error_data, indent=2)) else: print(f"Unexpected status code: {invoke_response.status_code}") print("Response text:") print(invoke_response.text[:500])
-
Replace
YOUR_AGENT_ARN_HERE
with your actual agent runtime ARN from Step 3. -
Run the script:
python invoke_agent.py
Step 5: Set up your agent to access tools using OAuth
In this section, you'll learn how to connect your agent code with AgentCore Credential Providers for secure access to external resources using OAuth2 authentication.
The example below demonstrates how your agent running in Agent Runtime can request OAuth consent from users, enabling them to authenticate with their Google account and authorize the agent to access their Google Drive contents.
For more information about setting up identity, see Getting started with Amazon Bedrock AgentCore Identity.
Step 5.1: Set up Credential Providers
To set up a Google Credential Provider, you need to:
-
Register your application with Google to obtain client ID and client secret
-
Create an OAuth credential provider using the AWS CLI:
aws agent-credential-provider create-oauth2-credential-provider \ --provider-type "google" \ --name "google-provider" \ --scopes '["https://www.googleapis.com/auth/drive.metadata.readonly"]' \ --google-config '{\ "clientId": "your-client-id",\ "clientSecret": "your-client-secret"\ }'
Make sure your invocation role has the necessary permissions for accessing the credential provider.
Step 5.2: Enable agent to read Google Drive contents
Create a tool with agent core SDK annotations as shown below to automatically initiate the three-legged OAuth process. When your agent invokes this tool, users will be prompted to open the authorization URL in their browser and grant consent for the agent to access their Google Drive.
import asyncio from bedrock_agentcore.identity.auth import requires_access_token, requires_api_key # This annotation helps agent developer to obtain access tokens from external applications @requires_access_token( provider_name="google-provider", scopes=["https://www.googleapis.com/auth/drive.metadata.readonly"], # Google OAuth2 scopes auth_flow="USER_FEDERATION", # 3LO flow on_auth_url=lambda x: print("Copy and paste this authorization url to your browser", x), # prints authorization URL to console force_authentication=True, ) async def read_from_google_drive(*, access_token: str): print(access_token) #You can see the access_token # Make API calls... main(access_token) asyncio.run(read_from_google_drive(access_token=""))
What happens behind the scenes
When this code runs, the following process occurs:
-
Agent Runtime authorizes the inbound token according to the configured authorizer.
-
Agent Runtime exchanges this token for a Workload Access Token via
bedrock-agentcore:GetWorkloadAccessTokenForJWT
API and delivers it to your agent code via the payload headerWorkloadAccessToken
. -
During tool invocation, your agent uses this Workload Access Token to call Token Vault API
bedrock-agentcore:GetResourceOauth2Token
and generate a 3LO authentication URL. -
Your agent sends this URL to the client application as specified in the
on_auth_url
method. -
The client application presents this URL to the user, who grants consent for the agent to access their Google Drive.
-
AgentCore Identity service securely receives and caches the Google access token until it expires, enabling subsequent requests from the user to use this token without needing the user to provide consent for every request.
Note
AgentCore Identity Service stores the Google access token in the AgentCore Token Vault using the agent workload identity and user ID (from the inbound JWT token, such as AWS Cognito token) as the binding key, eliminating repeated consent requests until the Google token expires.
Troubleshooting
How to debug token related issues
If you encounter issues with token authentication, you can decode the token to inspect its contents:
echo "$TOKEN" | cut -d '.' -f2 | tr '_-' '/+' | awk '{ l=4 - length($0)%4; if (l<4) printf "%s", $0; for (i=0; i<l; i++) printf "="; print "" }' | base64 -D | jq
This will output the token's payload, which looks similar to:
{ "sub": "subid", "iss": "https://cognito-idp.us-east-1.amazonaws.com/userpoolid", "client_id": "clientid", "origin_jti": "originjti", "event_id": "eventid", "token_use": "access", "scope": "aws.cognito.signin.user.admin", "auth_time": 1752275688, "exp": 1752279288, "iat": 1752275688, "jti": "jti", "username": "username" }
When troubleshooting token issues, check the following:
-
Authorizer discovery URL: The discovery URL in the agent configuration should match the issuer claim (
iss
) in the token.Click on the discovery URL you provided in the authorizer configuration when you created the agent (e.g.,
https://cognito-idp.us-east-1.amazonaws.com/us-east-1_userpoolid/.well-known/openid-configuration
) and check that the issuer URL matches theiss
claim value in the token. -
Authorizer client_id: The client_id in the agent configuration should match the client_id claim in the token.
-
Token expiration: Tokens are only valid for several minutes (default Cognito expiry being 60 minutes). Fetch a new token as needed.