Skip to content

Back to the catalog

Identity-aware AI data agents with AWS Lake Formation and Trusted Identity Propagation
aws.amazon.comwriteup

Identity-aware AI data agents with AWS Lake Formation and Trusted Identity Propagation

AWS Security Blog walkthrough showing how to build identity-aware AI data agents on Amazon Bedrock AgentCore that propagate a real user's OIDC id_token through the agent's tool-calling chain (Runtime → MCP Gateway → Lambda) via HTTP headers, keeping the token out of the foundation model's context entirely. A Lambda function performs a server-side token exchange using IAM Identity Center Trusted Identity Propagation, assumes a scoped TIP role, and runs the user's Athena query so AWS Lake Formation evaluates the real user's grants, producing per-user row/column access and CloudTrail audit records naming the actual human (onBehalfOf) without rewriting existing governance policies.

TL;DR A concrete AWS pattern for propagating per-user identity through an agent's tool-call chain (AgentCore Runtime/Gateway/Lambda) so Lake Formation enforces real-user data access without the token ever reaching the LLM.
Also seen from another angle🛡️ Security →
Document

What it claims

An agent's tools usually run under a shared IAM role, so Lake Formation sees that role and not the person asking. This post routes the user's id_token around the foundation model instead, using three Bedrock AgentCore settings. A Lambda function exchanges the token server-side and assumes a TIP role that holds no Lake Formation data grants. The role is "a session vehicle, not an authorization subject", so only the user's existing grants decide access. The agent, gateway and Lambda contain no authorization logic.

How it works

The decision that matters is where the token travels: on the HTTP transport, never in the tool contract.

flowchart LR
  UI -->|prompt in body| RT[AgentCore Runtime]
  UI -->|id_token custom header| RT
  RT --> FM[Foundation model]
  FM -->|tool args only| GW[AgentCore Gateway]
  RT -->|headers via agent code| GW
  GW -->|event = tool params| L[Lambda]
  GW -->|propagated headers| L
  L -->|CreateTokenWithIAM + AssumeRole| ATH[Athena / Lake Formation]

Three gates control propagation. Each is off by default:

  1. Runtime requestHeaderAllowlist. Allow Authorization (the access_token, validated by the JWT authorizer) and a custom header. Custom headers must use the prefix X-Amzn-Bedrock-AgentCore-Runtime-Custom-; the post uses X-Amzn-Bedrock-AgentCore-Runtime-Custom-IdToken. Agent code reads them from context.request_headers and attaches both to the MCP connection to the gateway.
  2. Gateway target metadataConfiguration.allowedRequestHeaders. List the id_token header here. The tool schema has no token parameter. If the token were a tool argument, the model would have to pass it, and it would end up in prompts, traces, memory and logs.
  3. Lambda. The token arrives in context.client_context.custom['bedrockAgentCorePropagatedHeaders'], not in event, which holds only the declared tool parameters.

Inside Lambda, the function:

  1. Validates the id_token.
  2. Calls sso-oidc:CreateTokenWithIAM to get an identityContext.
  3. Calls sts:AssumeRole with ProvidedContexts to get a short-lived boto3.Session.
  4. Runs the Athena query with that session.

The identityContext is never returned to the agent, gateway or UI.

How to use it

Prerequisites, none of which the post sets up:

  • An IAM Identity Center trusted token issuer (TTI) that accepts your IdP's tokens. Cognito is the demo IdP; the post says Auth0, Entra ID, Okta, Ping or any other OIDC provider works the same way.
  • An Identity Center OAuth application configured for CreateTokenWithIAM with the JWT Bearer grant.
  • Lake Formation governing the Glue Data Catalog, with grants on Identity Center users or groups. Existing grants and row/column filters work unchanged.
  • An Athena workgroup and an S3 bucket for query results.

Permissions the post lists for the TIP role:

  • athena:*
  • glue:*
  • lakeformation:GetDataAccess
  • S3 access to the Athena output bucket

To verify:

  • Run the same question as two users. User A has SELECT on trip_details and gets rows; User B has no grant and is refused. No code changes between the runs.
  • Check the CloudTrail AssumeRole event for an onBehalfOf block naming the human.

Limits

  • The code samples and configuration snippets did not appear in the fetched text. Expect to take them from the original post or its reference implementation.
  • The three header-propagation settings are specific to AgentCore. Other agent stacks such as Strands or LangGraph need AgentCore underneath or an equivalent transport path.
  • The listed TIP role permissions are wildcards (athena:*, glue:*). Data access is still bounded by Lake Formation, but you may want to narrow these.
  • The post does not discuss:
  • token expiry during long agent sessions,
  • whether the agent container and gateway logs capture the headers,
  • any latency or cost added by the per-call token exchange.

Organisations named here

  • Amazon Web Services
  • Okta
  • Auth0
  • Microsoft
  • Ping Identity
  • AWS
  • AWS Glue
  • LangGraph

Technologies named here

  • Amazon Bedrock AgentCore Runtime
  • Amazon Bedrock AgentCore Gateway
  • Strands
  • LangGraph
  • Model Context Protocol
  • AWS Lake Formation
  • AWS IAM Identity Center
  • AWS Lambda
  • Amazon Athena
  • AWS CloudTrail
  • Amazon Cognito
  • AWS Glue Data Catalog
  • Amazon S3
  • AWS
  • Amazon Bedrock AgentCore
  • AWS Glue
  • OAuth

Techniques named here

  • Trusted Identity Propagation
  • Server-side token exchange
  • Runtime request header allow list
  • Gateway metadata header propagation
  • Out-of-band identity transport
  • TIP role with no data grants