Sitemap
AWS Tip

Best AWS, DevOps, Serverless, and more from top Medium writers .

Getting Started with Amazon Bedrock AgentCore Identity

17 min readOct 19, 2026

--

Press enter or click to view image in full size

Introduction

AI agents that access sensitive data need identity and access control. A customer-facing assistant must know who is calling to decide what information to reveal. An internal tool must distinguish between employee access levels. Without authentication, any user could access any data — including confidential business information.

Amazon Bedrock AgentCore Identity provides inbound authentication for agents hosted on AgentCore Runtime. It validates JWT tokens directly at the Runtime endpoint, rejects unauthorized requests before they reach your code, and makes user claims (name, email, groups) available to the agent for fine-grained authorization decisions.

This article is part of a series I am writing about different aspects of Amazon Bedrock AgentCore. Here, we focus on how to use AgentCore Identity to implement role-based access control (RBAC) for agent users. For a broader understanding of AgentCore and its components, check out the other articles in this series:

Why AgentCore Runtime Instead of Harness?

In previous articles, we used the Harness (managed, declarative) to deploy our car dealership agent. The Harness is great for most use cases, but it does not give you access to the raw JWT token. You cannot write custom code that inspects user claims and conditionally registers tools.

For inbound authentication with RBAC, we need the Runtime: we bring our own container with custom code that reads the Authorization header, decodes the token, and decides which tools to expose based on the caller's Cognito group. The platform validates the token (signature, expiration, audience, groups) before it reaches our code — we only handle the business logic.

What We Are Building

We extend the used car dealership scenario from the series. The agent now has two access levels:

  • Customers can search the public vehicle inventory (make, model, year, price, mileage).
  • Employees can do everything customers can, plus access internal cost data (acquisition cost, repair cost, margin target).

The access control mechanism is deliberately blunt: a customer’s agent session is built without the cost tool at all. There is no code path that could leak internal data, no matter how the user phrases the question.

Architecture

Press enter or click to view image in full size
AgentCore Identity RBAC architecture: the browser authenticates with Cognito, sends the ID token to AgentCore Runtime, which validates it through AgentCore Identity and forwards it to the agent container, which then invokes Amazon Nova 2 Lite and reads the vehicles table for all users and the vehicle_costs table for employees only
AgentCore Identity RBAC architecture: the browser authenticates with Cognito, sends the ID token to AgentCore Runtime, which validates it through AgentCore Identity and forwards it to the agent container, which then invokes Amazon Nova 2 Lite and reads the vehicles table for all users and the vehicle_costs table for employees only

Authorization happens at two layers:

  1. Platform layer — AgentCore Identity rejects tokens that do not carry cognito:groups ∈ {customer, employee}.
  2. Agent layer — the code registers tools conditionally based on the group. The restricted tool simply does not exist for customers.

How the Token Reaches the Agent

Understanding the request flow is key to making this architecture work:

  1. The user authenticates with Cognito and receives an ID token — a JWT containing cognito:groups, name, email, and phone_number.
  2. The frontend sends the request to the AgentCore Runtime endpoint with the header Authorization: Bearer <id-token>.
  3. AgentCore Identity intercepts the request and validates the token: checks the signature against Cognito’s public keys, verifies the aud claim matches the allowed audience, and evaluates the custom_claim rule (group membership).
  4. If validation passes, the platform strips the Authorization header by default — the container never sees it unless you explicitly configure the Request Header Allowlist to forward it.
  5. With the allowlist configured, the Authorization header reaches the agent code. The agent decodes the JWT (without re-verifying the signature, since the platform already did that) and reads cognito:groups to decide which tools to register.

This last point is critical: without the Request Header Allowlist, the agent receives the request but has no way to know who the caller is. Every user would be treated as a customer by default, regardless of their actual group membership. The allowlist is what bridges the platform-level authentication with the application-level authorization. For more details on how custom headers work in AgentCore Runtime, see Pass custom headers to Amazon Bedrock AgentCore Runtime.

The following flowchart summarizes this decision logic, from authentication through the two authorization checkpoints (JWT validation at the platform and group evaluation in the agent code):

Token flow logic: the user authenticates with Cognito and receives an ID token, AgentCore Identity validates the JWT and rejects invalid tokens, the Request Header Allowlist decides whether the Authorization header reaches the agent, and the agent registers the full toolset for employees or the inventory-only toolset for customers
Token flow logic: the user authenticates with Cognito and receives an ID token, AgentCore Identity validates the JWT and rejects invalid tokens, the Request Header Allowlist decides whether the Authorization header reaches the agent, and the agent registers the full toolset for employees or the inventory-only toolset for customers

What the ID Token Looks Like

The Cognito ID token is a standard JWT with three parts (header.payload.signature). The payload is what the agent reads after decoding. Here is a real example from our demo user Jane (employee):

{
"sub": "b4c844b8-e0e1-70da-7962-0f839370f537",
"cognito:groups": ["employee"],
"email_verified": true,
"iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXXX",
"cognito:username": "jane",
"aud": "2bnnv2ng361p25ktnc1clc3ebo",
"token_use": "id",
"name": "Jane Doe",
"phone_number": "+12025550102",
"email": "jane.doe@example.com",
"exp": 1785989421,
"iat": 1785985821
}

The fields that matter for our RBAC logic:

  • cognito:groups — An array listing the user's group memberships. This is the authorization signal. The agent checks if "employee" is in this array to decide whether to register the restricted tool. A customer's token would show ["customer"] here.
  • aud — The App Client ID. The AgentCore authorizer validates this against allowed_audience to ensure the token was issued for this application.
  • name, email, phone_number — Profile attributes that the agent uses to personalize responses (and that would be needed for escalation notifications in a production scenario).
  • iss — The issuer URL, which the authorizer resolves from the Discovery URL to fetch public keys for signature verification.

You can decode any ID token yourself by pasting it into https://jwt.io or running JSON.parse(atob(token.split('.')[1])) in the browser console.

Prerequisites

  • AWS account with permissions to create Cognito, DynamoDB, ECR, IAM, and AgentCore resources
  • AWS CLI configured
  • Docker with buildx (for ARM64 image)
  • Python 3 (for the local test proxy)
  • AWS Region where AgentCore is available (us-east-1, us-west-2, ap-southeast-2, or eu-central-1)
  • Model access enabled for Amazon Nova 2 Lite in the Bedrock console

Step 1: Clone the Repository

The companion repository contains the agent code, frontend, and helper scripts that we will use throughout this tutorial. Clone it now:

git clone https://github.com/biagolini/PythonAwsBedrockAgentCoreIdentity.git
cd PythonAwsBedrockAgentCoreIdentity

We will use the following folders in upcoming steps:

  • agent/ — Container image source (Step 4)
  • seed/ — Scripts to create users and populate DynamoDB (Steps 2 and 3)
  • frontend/ — Browser-based chat UI for testing (Step 7)

Step 2: Create the Cognito User Pool

We need an identity provider that issues JWT tokens with group membership. Cognito does this natively via the cognito:groups claim in the ID token.

2.1 Create the User Pool

2.1.1. Navigate to Amazon Cognito > User pools and click Create user pool.

Press enter or click to view image in full size
Cognito Create User Pool initial screen
Cognito Create User Pool initial screen

2.1.2. You will see the “Set up resources for your application” page. Fill it in as follows:

Define your application:

  • Application type: Select Single-page application (SPA). This creates the App Client without a client secret, which is what we need for browser-based authentication.
  • Name your application: Enter agentcore-identity-demo.

Configure options:

  • Options for sign-in identifiers: Check Username only (leave Email and Phone number unchecked).
  • Self-registration: Uncheck Enable self-registration (we will create users manually via CLI).
  • Required attributes for sign-up: Select email, name, and phone_number from the dropdown.

Add a return URL:

  • Leave the Return URL field empty (we are not using the hosted UI — our frontend authenticates via the Cognito API directly).

2.1.3. Click Create user directory.

Press enter or click to view image in full size
Cognito User Pool configuration completed

2.1.4. A page will open showing an overview with code examples for React, Angular, and JavaScript. Scroll to the bottom and click Go to overview.

After creation, note your User Pool ID (shown at the top of the pool details page, format: us-east-1_XXXXXXXXX).

2.2 Configure the App Client

The “Create user directory” step automatically created an App Client (without a client secret, since we chose SPA). Now we need to enable the USER_PASSWORD_AUTH flow, which is required for programmatic login from the frontend.

2.2.1. Click the hamburger menu (☰) in the top-left corner to expand the navigation panel. Then go to App integration > App clients.

2.2.2. Click on the app client that was created (named agentcore-identity-demo).

Press enter or click to view image in full size
App client details page
App client details page

2.2.3. Click Edit in the Authentication flows section.

Press enter or click to view image in full size
Edit authentication flows button
Edit authentication flows button

2.2.4. Enable ALLOW_USER_PASSWORD_AUTH. You will see other flows already enabled by default (ALLOW_USER_AUTH, ALLOW_USER_SRP_AUTH, ALLOW_REFRESH_TOKEN_AUTH) — keep them as they are. Click Save changes.

Press enter or click to view image in full size
Authentication flows with USER_PASSWORD_AUTH enabled
Authentication flows with USER_PASSWORD_AUTH enabled

2.2.5. Copy the Client ID (shown at the top of the app client page). You can also confirm that Client secret is empty, which means no SECRET_HASH is needed for authentication — you will need the Client ID later for the .env file and the Runtime authorizer configuration.

Press enter or click to view image in full size
App client showing Client ID and empty Client secret
App client showing Client ID and empty Client secret

2.3 Create Groups

2.3.1. In the left navigation panel, click Groups, then click Create group.

Press enter or click to view image in full size
Groups page with Create group button
Groups page with Create group button

2.3.2. Create the two groups, one at a time. For each group, fill in the Group name and click Create group:

  • First group: employee
  • Second group: customer
Press enter or click to view image in full size
Create group form
Create group form

Leave Precedence and IAM role empty for both groups. Here’s what they do (not needed for this demo):

  • Precedence — A numeric priority used when a user belongs to multiple groups. The group with the lowest number is considered the “preferred” group, and its IAM role (if any) is used for the cognito:preferred_role claim. Since our agent reads all groups from the array, precedence does not affect our RBAC logic. See GroupType API reference.
  • IAM role — An IAM role that Cognito can assign to users in this group when they authenticate through a Cognito Identity Pool (federated identities). We are not using Identity Pools — our agent reads the cognito:groups claim directly from the ID token and makes its own authorization decisions in code.

2.4 Create Demo Users

Create three users via the AWS CLI. Set your User Pool ID once at the top, then run the commands:

# Set your User Pool ID here (from Step 1.1.4)
export POOL_ID=us-east-1_XXXXXXXXX

# Customer user
aws cognito-idp admin-create-user \
--user-pool-id $POOL_ID \
--username john \
--user-attributes Name=name,Value="John Smith" Name=email,Value="john.smith@example.com" Name=email_verified,Value=true Name=phone_number,Value="+12025550101" Name=phone_number_verified,Value=true \
--message-action SUPPRESS
aws cognito-idp admin-set-user-password \
--user-pool-id $POOL_ID \
--username john \
--password "Customer1Pass!" \
--permanent
aws cognito-idp admin-add-user-to-group \
--user-pool-id $POOL_ID \
--username john \
--group-name customer
# Employee user
aws cognito-idp admin-create-user \
--user-pool-id $POOL_ID \
--username jane \
--user-attributes Name=name,Value="Jane Doe" Name=email,Value="jane.doe@example.com" Name=email_verified,Value=true Name=phone_number,Value="+12025550102" Name=phone_number_verified,Value=true \
--message-action SUPPRESS
aws cognito-idp admin-set-user-password \
--user-pool-id $POOL_ID \
--username jane \
--password "Employee1Pass!" \
--permanent
aws cognito-idp admin-add-user-to-group \
--user-pool-id $POOL_ID \
--username jane \
--group-name employee
# User with no group (to test platform-level rejection)
aws cognito-idp admin-create-user \
--user-pool-id $POOL_ID \
--username alex \
--user-attributes Name=name,Value="Alex Nobody" Name=email,Value="alex.nobody@example.com" Name=email_verified,Value=true Name=phone_number,Value="+12025550103" Name=phone_number_verified,Value=true \
--message-action SUPPRESS
aws cognito-idp admin-set-user-password \
--user-pool-id $POOL_ID \
--username alex \
--password "NoGroup1Pass!" \
--permanent

2.5 Note Your Discovery URL

The AgentCore JWT authorizer needs the OIDC discovery URL to fetch Cognito’s public keys and validate tokens. You can find it in the User Pool’s Overview page, under the OpenID Connect configuration URL field.

Press enter or click to view image in full size
OpenID Connect configuration URL in the Overview page
OpenID Connect configuration URL in the Overview page

The URL follows this pattern:

https://cognito-idp.<region>.amazonaws.com/<pool-id>/.well-known/openid-configuration

You can verify it works by pasting the URL in your browser — it should return a JSON document with issuer, jwks_uri, and other OIDC metadata.

Save this URL — you will need it in Step 5 when creating the AgentCore Runtime authorizer.

Step 3: Create DynamoDB Tables

We need two tables: one public (vehicles) and one restricted (vehicle costs).

3.1 Create the Vehicles Table

3.1.1. Navigate to DynamoDB > Tables and click Create table.

3.1.2. Table name: agentcore-identity-vehicles

3.1.3. Partition key: vehicle_id (String)

3.1.4. Use default settings (on-demand capacity). Click Create table.

Press enter or click to view image in full size
Creating the vehicles table
Creating the vehicles table

3.1.5. After the table is created, click on it to open its details. Go to the Indexes tab and click Create index.

Press enter or click to view image in full size
Indexes tab with Create index button
Indexes tab with Create index button

3.1.6. Configure the Global Secondary Index:

  • Index name: body-type-index
  • Partition key: Enter body_type in the Attribute field, Data type String
  • Sort key: Leave empty
  • Index capacity: Keep defaults (follows base table — on-demand)
  • Warm throughput: Keep default values
  • Attribute projections: Select All (all table attributes are projected into the index)

Click Create index and wait for its status to become Active.

Press enter or click to view image in full size
Create global secondary index form

3.2 Create the Vehicle Costs Table

3.2.1. Create another table:

  • Table name: agentcore-identity-vehicle-costs
  • Partition key: vehicle_id (String)
  • Default settings (on-demand)

This table does not need a Global Secondary Index — the agent only queries it by vehicle_id (the partition key).

3.3 Populate with Sample Data

From the cloned repository, configure and run the seed scripts:

cd seed
cp .env.example .env
# Edit .env: set your POOL_ID and confirm the table names
./seed_users.sh
./seed_data.sh

This creates the 3 demo users (with group assignments) and inserts 8 vehicles with their corresponding internal cost records.

Step 4: Create the ECR Repository and Push the Agent Image

We need an ECR repository to store the agent container image. The process is the same as in Getting Started with Amazon Bedrock AgentCore Harness and Runtime, using the agentcore/ namespace to group all agent-related repositories.

4.1 Create the Repository

4.1.1. Navigate to Amazon ECR > Repositories and click Create repository.

Get Carlos Biagolini’s stories in your inbox

Join Medium for free to get updates from this writer.

4.1.2. Visibility: Private

4.1.3. Repository name: agentcore/identity-agent

4.1.4. Leave all other settings at their defaults and click Create repository.

4.2 Build and Push the Agent Image

From the cloned repository:

cd agent

export AWS_REGION=us-east-1
export AWS_ACCOUNT_ID=<your-account-id>
export REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
export REPO_URI="${REGISTRY}/agentcore/identity-agent"
# Authenticate Docker with ECR
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $REGISTRY
# Build ARM64 image and push
docker buildx build --platform linux/arm64 --tag ${REPO_URI}:latest --push .

Copy the full image URI (e.g., 123456789012.dkr.ecr.us-east-1.amazonaws.com/agentcore-identity-agent:latest) — you need it for the Runtime creation.

Step 5: Create the IAM Execution Role

The AgentCore Runtime needs a role to pull the image, write logs, invoke the model, and read DynamoDB.

5.1 Create the Role

5.1.1. Navigate to IAM > Roles and click Create role.

5.1.2. Trusted entity: Custom trust policy. Paste:

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": { "Service": "bedrock-agentcore.amazonaws.com" },
"Condition": {
"StringEquals": { "aws:SourceAccount": "<your-account-id>" }
}
}]
}

5.1.3. Name it agentcore-identity-runtime-role.

5.2 Attach Policies

After creation, add an inline policy with the following permissions (replace account ID and region):

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECR",
"Effect": "Allow",
"Action": ["ecr:GetAuthorizationToken"],
"Resource": "*"
},
{
"Sid": "ECRImage",
"Effect": "Allow",
"Action": ["ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"],
"Resource": "arn:aws:ecr:us-east-1:<account-id>:repository/agentcore/identity-agent"
},
{
"Sid": "Logs",
"Effect": "Allow",
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams", "logs:DescribeLogGroups"],
"Resource": "arn:aws:logs:us-east-1:<account-id>:log-group:/aws/bedrock-agentcore/runtimes/*"
},
{
"Sid": "XRay",
"Effect": "Allow",
"Action": ["xray:PutTraceSegments", "xray:PutTelemetryRecords", "xray:GetSamplingRules", "xray:GetSamplingTargets"],
"Resource": "*"
},
{
"Sid": "Metrics",
"Effect": "Allow",
"Action": ["cloudwatch:PutMetricData"],
"Resource": "*",
"Condition": { "StringEquals": { "cloudwatch:namespace": "bedrock-agentcore" } }
},
{
"Sid": "BedrockNova",
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
"Resource": [
"arn:aws:bedrock:*::foundation-model/amazon.nova-*",
"arn:aws:bedrock:us-east-1:<account-id>:inference-profile/*amazon.nova-*"
]
},
{
"Sid": "DynamoDB",
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan"],
"Resource": [
"arn:aws:dynamodb:us-east-1:<account-id>:table/agentcore-identity-vehicles",
"arn:aws:dynamodb:us-east-1:<account-id>:table/agentcore-identity-vehicles/index/*",
"arn:aws:dynamodb:us-east-1:<account-id>:table/agentcore-identity-vehicle-costs"
]
},
{
"Sid": "WorkloadIdentity",
"Effect": "Allow",
"Action": ["bedrock-agentcore:GetWorkloadAccessToken", "bedrock-agentcore:GetWorkloadAccessTokenForJWT"],
"Resource": [
"arn:aws:bedrock-agentcore:us-east-1:<account-id>:workload-identity-directory/default",
"arn:aws:bedrock-agentcore:us-east-1:<account-id>:workload-identity-directory/default/workload-identity/*"
]
}
]
}

Note: The Bedrock permissions are restricted to amazon.nova-* on purpose. Amazon-owned models are billed as native AWS usage, while third-party models (Anthropic, Meta) are billed through AWS Marketplace, which most promotional credits do not cover.

Step 6: Create the AgentCore Runtime

This is the central step — where AgentCore Identity is configured.

6.1 Create the Runtime

6.1.1. Navigate to Amazon Bedrock AgentCore > Runtime and click Create runtime.

Press enter or click to view image in full size
AgentCore Runtime console with Create runtime button
AgentCore Runtime console with Create runtime button

6.1.2. Runtime details:

  • Name: agentcore_identity_demo (letters, numbers, and underscores only — no hyphens).

6.1.3. Agent source:

  • Source type: Select ECR Container.
  • Image URI: Paste your image URI from Step 4 (e.g. <your-account-id>.dkr.ecr.us-east-1.amazonaws.com/agentcore/identity-agent:latest).

6.1.4. Permissions:

  • IAM permissions: Select Use another role and choose the role created in Step 5 (e.g. agentcore-identity-runtime-role).

6.1.5. Inbound Auth:

  • Protocol: Select HTTP.
  • Inbound Auth Type: Select Use JSON Web Tokens (JWT).
  • JWT schema configuration: Select Use existing Identity provider configurations.
  • Discovery URL: Paste your Cognito OIDC URL from Step 2.5.

6.1.6. JWT Authorization Configuration:

  • Check Allowed audiences and enter your Cognito App Client ID (from Step 2.2.5).
  • Uncheck Allowed clients (leave empty — ID tokens do not carry a client_id claim, so enabling this would reject every request).
  • Uncheck Allowed scopes.
  • Check Custom claims and configure: Name = cognito:groups, String type = String Array, Operator = Contains any, Value = customer,employee.
  • Uncheck Allowed gateways.

6.1.7. Additional configurations (VPC security):

  • VPC security: Select None (public endpoint).

6.1.8. Advanced configurations:

  • Security: Select Public.
  • Environment variables: Click Add new variable three times and add: VEHICLES_TABLE = agentcore-identity-vehicles, VEHICLE_COSTS_TABLE = agentcore-identity-vehicle-costs, BEDROCK_MODEL_ID = us.amazon.nova-2-lite-v1:0.
  • Lifecycle configurations: Keep defaults (Session idle timeout: 15 minutes, Runtime lifecycle: 8 hours).

6.1.9. Click Create runtime and wait for the status to reach Ready.

Press enter or click to view image in full size
AgentCore Runtime creation form

6.2 Configure the Request Header Allowlist

After the runtime is created, you need to forward the Authorization header to the container. As explained in the architecture section, without this configuration the agent code cannot read the caller's identity from the JWT — the platform strips the header after validation.

As of August 2026, during the development of this POC, we did not find this option in the AWS console UI. The AWS documentation on custom headers describes how to configure it via the AgentCore CLI (agentcore.json) or the AWS SDK (update_agent_runtime), but does not mention a console-based configuration. Since this may change in future console updates, check the documentation link above for the latest instructions.

To configure it via the AWS CLI, run the following command (replacing placeholders with your values from previous steps):

aws bedrock-agentcore-control update-agent-runtime \
--agent-runtime-id <your-runtime-id> \
--role-arn arn:aws:iam::<your-account-id>:role/agentcore-identity-runtime-role \
--agent-runtime-artifact '{"containerConfiguration":{"containerUri":"<your-image-uri>"}}' \
--network-configuration '{"networkMode":"PUBLIC"}' \
--authorizer-configuration '{"customJWTAuthorizer":{"discoveryUrl":"<your-discovery-url>","allowedAudience":["<your-client-id>"],"customClaims":[{"inboundTokenClaimName":"cognito:groups","inboundTokenClaimValueType":"STRING_ARRAY","authorizingClaimMatchValue":{"claimMatchValue":{"matchValueStringList":["customer","employee"]},"claimMatchOperator":"CONTAINS_ANY"}}]}}' \
--request-header-configuration '{"requestHeaderAllowlist":["Authorization"]}' \
--environment-variables '{"VEHICLES_TABLE":"agentcore-identity-vehicles","VEHICLE_COSTS_TABLE":"agentcore-identity-vehicle-costs","BEDROCK_MODEL_ID":"us.amazon.nova-2-lite-v1:0"}' \
--region us-east-1

Important: The update-agent-runtime command is a full PUT operation. You must include all fields (role, artifact, network, authorizer, environment variables) even if they have not changed. Omitting a field will reset it to empty.

Wait for the runtime status to return to Ready:

aws bedrock-agentcore-control get-agent-runtime \
--agent-runtime-id <your-runtime-id> \
--query "status" --output text \
--region us-east-1

6.3 Copy the Invoke URL

The invoke URL is not displayed directly in the console. You build it from the Runtime ARN (visible in the runtime details page under “Agent and tool details”) using this pattern:

Press enter or click to view image in full size
Runtime ARN location in the console
Runtime ARN location in the console
https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-arn>/invocations?qualifier=DEFAULT

To URL-encode the ARN, replace : with %3A and / with %2F. For example, if your ARN is:

arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/agentcore_identity_demo-XXXXXXXX

The invoke URL becomes:

https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-east-1%3A123456789012%3Aruntime%2Fagentcore_identity_demo-XXXXXXXX/invocations?qualifier=DEFAULT

Save this URL — it goes in your .env file.

Step 7: Test with the Frontend

7.1 Configure the Frontend

From the cloned repository:

cd frontend
cp .env.example .env

Edit .env with the values you collected:

INVOKE_URL=https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-east-1%3A123456789012%3Aruntime%2Fagentcore_identity_demo-XXXXXXXX/invocations?qualifier=DEFAULT
COGNITO_CLIENT_ID=your-client-id-from-step-1
AWS_REGION=us-east-1

7.2 Start the Proxy

python3 proxy.py

Open http://localhost:8080 in your browser.

7.3 Test Each Scenario

Sign in as john (customer, password: Customer1Pass!)

Try these messages:

  • What SUVs do you have available under $30,000? → Should list vehicles from public table.
  • What was the acquisition cost of the Ford Explorer? → Should politely decline (the tool doesn't exist).
Press enter or click to view image in full size
Customer test results

Sign in as jane (employee, password: Employee1Pass!)

Try the same messages to see the difference in behavior:

  • What SUVs do you have available under $30,000? → Should list vehicles (same as customer).
  • What was the acquisition cost of the Ford Explorer? → Should return $21,000 and the margin target (employee has access).
Press enter or click to view image in full size
Employee test results

Sign in as alex (no group, password: NoGroup1Pass!)

  • Any message → Should immediately show Error 401: Authorization denied. The platform-level custom_claim rule rejected the token before the container was invoked.
Press enter or click to view image in full size
Employee test results
Employee test results

Step 8: Clean Up

To avoid ongoing charges, delete the resources in this order:

  1. AgentCore Runtime — Amazon Bedrock AgentCore > Runtime > select and delete
  2. ECR Repository — Amazon ECR > Repositories > select and delete (including images)
  3. DynamoDB Tables — DynamoDB > Tables > delete both tables
  4. Cognito User Pool — Amazon Cognito > User Pools > select and delete
  5. IAM Role — IAM > Roles > search for agentcore-identity-runtime-role and delete

How the Agent Code Works

The agent code (agent/agent.py) implements the RBAC logic. Here's what happens on each request:

# 1. Extract the JWT from the Authorization header (already validated by platform)
claims = jwt.decode(token, options={"verify_signature": False})

# 2. Resolve the caller's role from Cognito groups
groups = claims.get("cognito:groups", [])
is_employee = "employee" in groups

# 3. Register tools conditionally
tools = [search_vehicles] # everyone gets this
if is_employee:
tools.append(get_vehicle_costs) # only employees get this

# 4. Build the agent with the appropriate tools
agent = Agent(model=model, system_prompt=prompt, tools=tools)

The restricted tool (get_vehicle_costs) is not "denied when called" — it is absent from the customer's session entirely. There is no code path that could leak internal data regardless of prompt injection attempts.

Key Takeaways

  • AgentCore Identity validates tokens at the platform level — signature, expiration, audience, and custom claims are all checked before your code runs. Invalid requests never reach the container.
  • custom_claim provides defense in depth — even if your agent code had a bug, a user without a recognized group would still be blocked at the platform level.
  • The ID token carries everything — Cognito groups (cognito:groups), display name, email, and phone number are all in the same token. No extra API calls needed.
  • allowed_audience (not allowed_clients) — Cognito ID tokens carry the client ID in the aud claim, not client_id. Using allowed_clients would reject every ID token.
  • request_header_allowlist is mandatory — Without adding Authorization to the allowlist, the platform strips the header after validation and the agent cannot read user claims.
  • Amazon Nova keeps costs predictable — First-party models are billed as native AWS usage (covered by promotional credits). Third-party models go through AWS Marketplace.

References

Stay Connected

If you found this article helpful and want to learn more about AWS, cloud architecture, AI, infrastructure as code, and cloud security, follow the author for future content and tutorials:

Stay tuned for more practical walkthroughs and solutions for cloud infrastructure and DevOps challenges.

Happy building on AWS! 🚀☁️

--

--

Carlos Biagolini
Carlos Biagolini

Written by Carlos Biagolini

Cloud Architect | Writing about AWS, AI/ML, and hands-on cloud solutions | AWS Community Builder

AWS Tip
AWS Tip

Published in AWS Tip

Best AWS, DevOps, Serverless, and more from top Medium writers .