AI brokers in manufacturing require safe entry to exterior companies. Amazon Bedrock AgentCore Identification, out there as a standalone service, secures how your AI brokers entry exterior companies whether or not they run on compute platforms like Amazon ECS, Amazon EKS, AWS Lambda, or on-premises.
An earlier publish coated AgentCore Identification credential administration for AI brokers. Working brokers on compute environments like ECS raises two questions: Methods to construct an application-owned Session Binding endpoint, and how one can handle workload entry token lifecycle?
This publish implements Authorization Code Grant (3-legged OAuth) on Amazon ECS with safe session binding and scoped tokens. This publish offers a working implementation with:
- Safe session binding that forestalls CSRF and browser-swapping assaults
- Auth tokens scoped to every person session, following least-privilege ideas
- Separation of issues between the agent workload and session binding service
Authentication and authorization with OAuth 2.0 and OIDC
This resolution makes use of OAuth 2.0 (RFC 6749) and OpenID Join (OIDC). OIDC authenticates customers (who they’re), and OAuth 2.0 authorizes their actions (what they will do).
We concentrate on the Authorization Code Grant for user-delegated entry. The person authenticates with an identification supplier and grants consent. The appliance then exchanges an authorization code for an entry token, which creates an audit path.On this move, the person authenticates with an identification supplier and grants consent for the agent to entry particular sources on their behalf. The appliance exchanges the ensuing authorization code for a scoped entry token which Amazon Bedrock AgentCore Identification secures in its token vault. As a result of every token is sure to a selected person identification with express consent, the answer maintains an auditable chain from person authentication by means of to agent motion.
The Authorization Code Grant is fitted to agentic workloads that act on behalf of customers as a result of it offers person consent earlier than the agent can act, session binding that verifies the person who initiated the authorization request is similar person who granted consent, and scoped delegation that limits the agent to solely the permissions the person authorised.
Callback URL vs. session binding URL
On this context, the Authorization Code Grant move makes use of two URLs which are typically confused:
- Callback URL: Robotically generated when creating an OAuth consumer in AgentCore Identification. It factors to AgentCore Identification and have to be registered with the Authorization Server because the redirect goal the place the authorization code is distributed after person authentication.
- Session Binding URL: The URL pointing again to a customer-managed service that completes the session binding between the authenticated person and the OAuth move. This endpoint is carried out and hosted by the client.
Answer overview
This structure diagram reveals how AgentCore Identification secures a self-hosted AI agent on Amazon ECS. This walkthrough makes use of Microsoft Entra ID because the identification supplier, however different OIDC-compliant suppliers are supported. The entire supply code and conditions for this walkthrough can be found within the accompanying GitHub repository.
The answer deploys two companies on Amazon ECS behind an Software Load Balancer. The Agentic Workload runs the AI agent and handles person requests. The Session Binding Service processes OAuth callbacks to hyperlink person periods with third-party entry tokens. Each companies use Amazon Bedrock AgentCore Identification to authenticate customers inbound through OIDC and authorize outbound actions on their behalf. The numbered annotations within the diagram correspond to the next descriptions.
- Inbound authentication and site visitors routing: Requests arrive at an Amazon Software Load Balancer (ALB), which authenticates the person by means of the ALB’s built-in OIDC authentication move. Site visitors is encrypted with HTTPS utilizing a certificates from AWS Certificates Supervisor, and an alias A report in an Amazon Route 53 public hosted zone routes site visitors to the load balancer. After authenticating the person by means of OIDC, the ALB forwards the request to the Amazon ECS cluster. The ALB injects an x-amzn-oidc-data header containing the person’s claims in JWT format, with the sub area uniquely figuring out the person.
- Agentic workload: The Agentic Workload exposes a FastAPI server with an /invocations endpoint that accepts a sessionId and message. The FastAPI server passes these to an agent constructed with Strands Brokers. You may also use LangChain or different agent SDKs for the reason that server handles requests independently of the agent framework. The agent calls a big language mannequin (LLM) on Amazon Bedrock, however different mannequin suppliers work, too. The agent shops session state in an Amazon S3 bucket and it makes use of the person’s sub declare as a key prefix to isolate periods between customers. The agent additionally has instruments to carry out actions on the person’s behalf in GitHub, which requires the person’s OAuth entry token.
- Outbound authentication with AgentCore Identification: When the agent must act on the person’s behalf in a third-party service like GitHub, it requests an OAuth entry token by means of AgentCore Identification. If no legitimate token exists, AgentCore Identification initiates an Authorization Code Grant move, prompting the person to authorize entry.
- OAuth callback processing: After the person authorizes entry, the Session Binding Service completes the OAuth move by binding the authorization to the right person session through AgentCore Identification.
- Person interface: The FastAPI server that hosts the agentic workload exposes a /docs endpoint, which renders the OpenAPI specification as an interactive HTML web page. The top person interacts with the agent by means of this web page, which offers a minimal UI for demonstration
Amazon CloudWatch captures logs, and a devoted S3 bucket shops entry logs for each the load balancer and the information bucket. ECS pulls container photographs from Amazon ECR. A set of primary AWS WAF guidelines is hooked up to the load balancer to offer baseline safety towards frequent internet exploits. An Amazon KMS buyer managed key (CMK) encrypts knowledge, apart from the entry logs bucket, which requires Amazon S3 managed encryption (SSE-S3).
Amazon Bedrock AgentCore Identification: Authorization Code Grant
This walkthrough adapts the final AgentCore Identification session binding move for a self-hosted structure utilizing ALB for authentication, a devoted Session Binding Service, and direct API calls as a substitute of the AgentCore SDK and Runtime.
The sequence diagram reveals how AgentCore Identification’s workload identification, workload entry tokens, and OAuth 2.0 credential supplier work collectively to securely present OAuth tokens to the agent on behalf of a person. This move assumes the authenticated person has not but licensed the agent to entry their sources, that means no legitimate token exists within the AgentCore Identification Token Vault.
- An authenticated person sends a request to the agentic workload. The agentic workload extracts the person ID from the sub declare within the ALB-signed JWT (x-amzn-oidc-data header) to determine the person.
- The agentic workload calls the GetWorkloadAccessTokenForUserId API, passing the userId and workloadName, to acquire a workload entry token that represents the agent’s identification scoped to this person.
- AgentCore Identification returns the workload entry token to the agentic workload.
- The agentic workload calls the GetResourceOauth2Token API, passing the workload entry token, the supplier identify of the configured OAuth 2.0 credential supplier, a session binding URL (see callbackUrl parameter), and the required scopes, for example the learn:person scope of GitHub. This requests an OAuth token for the third-party service (on this case, GitHub).
- As a result of no legitimate token exists for this person, AgentCore Identification creates a sessionURI that tracks the authorization move state throughout the next requests and responses through the OAuth 2.0 authentication course of.
- AgentCore Identification returns an authorization URL and session URI to the workload
- The agentic workload returns the authorization URL to the person, prompting them to authorize entry.
- The person clicks the authorization URL and grants the agent permission within the third-party supplier’s consent display screen.
- The Authorization Server sends the authorization code to AgentCore Identification.
- AgentCore Identification redirects the person to the Session Binding URL with the session URI appended, routing them to the Session Binding Service.
- The person’s browser follows the redirect to the Session Binding Service through the Session Binding URL. The ALB injects the JWT within the x-amzn-oidc-data header.
- The Session Binding Service calls the CompleteResourceTokenAuth API with the session URI and person ID (extracted from the JWT), binding the finished authorization to the right person session. On success, it returns a static software owned HTML web page confirming the authorization was profitable.
- AgentCore Identification exchanges the authorization code with the Authorization Server for an OAuth2 entry token.
- The Authorization Server returns the OAuth2 entry token.
- AgentCore Identification shops the token within the Token Vault.
- AgentCore Identification returns success to the Session Binding Service.
- The Session Binding Service shows “Authorization full” to the person.
On subsequent requests, whether or not the person must re-authorize relies on the credentials the authorization server issued. AgentCore Identification shops each entry tokens and refresh tokens (when out there) within the Token Vault. When a refresh token is current — as with GitHub when Person-to-server token expiration is enabled — AgentCore Identification mechanically makes use of it to acquire a brand new entry token as soon as the unique expires, with out prompting the person once more. If no refresh token was issued and the entry token expires, the person can be prompted to re-authorize. Observe that tokens can be revoked on the supplier aspect; in such instances, setting forceAuthentication: true forces a contemporary authentication move.
Session binding:
Session binding protects towards two safety threats:
Cross-Web site Request Forgery (CSRF): An attacker makes an attempt to bind their very own OAuth token to the sufferer’s identification, inflicting the sufferer’s agent to unknowingly entry the attacker’s sources, enabling knowledge exfiltration and injection.
Browser Swapping Assault: An attacker methods the sufferer into consenting on their behalf, binding the sufferer’s OAuth token to the attacker’s identification, granting the attacker direct entry to the sufferer’s sources.
Session binding prevents each assaults by making certain that the person ID on the agent workload matches the person ID on the Session Binding Service, with each identities cryptographically verified by means of the authentication chain.
AgentCore Identification additionally helps an elective customState parameter within the GetResourceOauth2Token API that can be utilized to move a cryptographically random nonce to guard your callback endpoint towards CSRF assaults, as really helpful by the OAuth 2.0 specification.
Why we use GetWorkloadAccessTokenForUserId with AWS ALB and Microsoft Entra ID
The really helpful API for acquiring a workload entry token is GetWorkloadAccessTokenForJWT. This resolution makes use of GetWorkloadAccessTokenForUserId as a substitute.
GetWorkloadAccessTokenForJWT requires a dynamically validatable JWT whose signature may be verified at runtime towards the issuer’s revealed signing keys, and whose aud declare matches your software. To acquire such a token from Microsoft Entra ID, you could embrace your Software ID within the scope of the OIDC authorization request, see the AgentCore Microsoft Inbound documentation for particulars.
Nonetheless, that is incompatible with the AWS ALB OIDC move.
As a part of its OIDC handshake (see ALB OIDC documentation), the ALB sends the entry token to Entra’s UserInfo endpoint to retrieve the authenticated person’s claims which is a compulsory step within the ALB’s authentication move. This UserInfo endpoint is hosted on Microsoft Graph (https://graph.microsoft.com/oidc/userinfo), and it solely accepts tokens scoped to Microsoft Graph. If you embrace your Software ID within the scope, the ensuing entry token has your software because the viewers, the UserInfo endpoint rejects it with a 401 and the ALB returns a 561.
In the event you take away your Software ID from the scope, Entra defaults the entry token viewers to Microsoft Graph (00000003-0000-0000-c000-000000000000). The ALB handshake succeeds however the ensuing JWT can’t be dynamically validated by AgentCore. It’s unusable with GetWorkloadAccessTokenForJWT.
This resolution: The ALB completes its handshake utilizing the Graph-scoped token. The ALB forwards an ALB-signed JWT within the x-amzn-oidc-data header containing the person’s claims from the UserInfo endpoint, together with a sub declare that uniquely identifies the authenticated person. We validate this ALB-signed JWT utilizing AWS’s revealed signing keys, extract the sub, and move it to GetWorkloadAccessTokenForUserId.
Implementation
View the entire code GitHub repository.
Acquiring the Workload Entry Token
The server extracts the person ID from the JWT’s sub declare and requests a workload entry token from AgentCore Identification. The server then makes use of this token, the session ID, and the message to invoke the agent on behalf of the person. Observe that session ID right here refers back to the agent’s dialog session, not the OAuth session URI from the authorization move.
@router.publish(“/invocations”)
async def invoke_agent(
request: InvocationRequest,
user_id: str = Relies upon(get_current_user),
settings: Settings = Relies upon(get_settings),
agent_service: AgentService = Relies upon(get_agent_service),
) -> StreamingResponse:
“””Invoke agent with streaming response.”””
attempt:
agentcore = boto3.consumer(“bedrock-agentcore”, region_name=settings.identity_aws_region)
response = agentcore.get_workload_access_token_for_user_id(
workloadName=settings.workload_identity_name, userId=user_id
)
workload_access_token = response[“workloadAccessToken”]
return StreamingResponse(
content material=agent_service.stream_response(
user_message=request.user_message,
session_id=request.session_id,
user_id=user_id,
workload_access_token=workload_access_token,
),
media_type=”textual content/event-stream”,
)
Requesting the entry token
The server makes use of the require_access_token decorator from AgentCore SDK to retrieve OAuth 2.0 entry token, see Acquire OAuth 2.0 entry token. We modify the decorator to just accept the workload entry token as an express parameter fairly than resolving it internally, giving direct management over token lifecycle administration whereas preserving the SDK’s token retrieval and error-handling logic
def requires_access_token(
*,
provider_name: str,
scopes: record[str],
auth_flow: Literal[“M2M”, “USER_FEDERATION”],
workload_access_token: str | None = None,
session_binding_url: str | None = None,
on_auth_url: Callable[[str], Any] | None = None,
force_authentication: bool = False,
token_poller: TokenPoller | None = None,
custom_state: str | None = None,
custom_parameters: dict[str, str] | None = None,
into: str = “access_token”,
area: str | None = None,
) -> Callable[[Callable[…, Any]], Callable[…, Any]]:
“””Fetch OAuth2 entry token with express workload token.
Args:
provider_name: The credential supplier identify
scopes: OAuth2 scopes to request
auth_flow: Authentication move kind (“M2M” or “USER_FEDERATION”)
workload_access_token: The workload entry token (express, not from context)
session_binding_url: Session Binding URL pointing to the customer-managed service that completes the session binding
on_auth_url: Handler invoked with the authorization URL when person authorization is required
force_authentication: Pressure re-authentication
token_poller: Customized token poller implementation
custom_state: State for callback verification
custom_parameters: Extra OAuth parameters
into: Parameter identify to inject the token into
area: AWS area
Returns:
Decorator operate
“””
def decorator(func: Callable[…, Any]) -> Callable[…, Any]:
consumer = IdentityClient(area)
@wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> Any:
attempt:
if not workload_access_token:
elevate ValueError(“workload_access_token is required”)
token = await consumer.get_token(
provider_name=provider_name,
agent_identity_token=workload_access_token,
scopes=scopes,
auth_flow=auth_flow,
callback_url=session_binding_url,
on_auth_url=on_auth_url,
force_authentication=force_authentication,
token_poller=token_poller,
custom_state=custom_state,
custom_parameters=custom_parameters,
)
kwargs[into] = token
return await func(*args, **kwargs)
besides Exception:
logger.exception(“Error in requires_access_token decorator”)
elevate
return wrapper
return decorator
Our instrument class makes use of this decorator to provide the entry token when calling the GitHub API.
class GitHubTools:
“””Instruments for interacting with GitHub utilizing OAuth authentication.”””
def _on_auth_url(self, url: str) -> None:
“””Deal with authorization URL by elevating AuthorizationRequiredError.
This URL have to be offered to the person to grant entry.
“””
elevate AuthorizationRequiredError(supplier=”GitHub”, auth_url=url)
async def _call_github_api(
self, endpoint: str, scopes: record[str], params: dict | None = None
) -> Any:
“””Make authenticated GitHub API name.
Raises:
ApiError: When API name fails
“””
@requires_access_token(
provider_name=self.config.provider_name,
scopes=scopes,
auth_flow=”USER_FEDERATION”,
workload_access_token=self.config.workload_access_token,
session_binding_url=self.config.session_binding_url,
on_auth_url=self._on_auth_url,
area=self.config.aws_region,
)
async def make_request(*, access_token: str) -> Any:
async with httpx.AsyncClient() as consumer:
response = await consumer.get(
f”{self.config.github_api_base}{endpoint}”,
headers={
“Authorization”: f”Bearer {access_token}”,
“Settle for”: “software/vnd.github+json”,
“X-GitHub-Api-Model”: “2022-11-28”,
},
params=params or {},
timeout=10.0,
)
response.raise_for_status()
return response.json()
attempt:
return await make_request()
Every instrument within the class makes use of this technique, as proven beneath:
from strands import instrument
class GitHubTools:
@instrument
async def get_github_user(self) -> GitHubUser:
“””Get the authenticated GitHub person’s profile info.
Use this instrument when the person desires to:
– See their GitHub profile
– Examine who they’re authenticated as
– View their GitHub account particulars
Returns:
GitHub person profile
Raises:
ApiError: When API name fails
“””
end result: dict[str, Any] = await self._call_github_api(
“/person”, scopes=[“read:user”]
)
return GitHubUser.model_validate(end result)
Three key design selections:
- Pydantic BaseModel as return sorts: GitHubUser and GitHubProject are BaseModel sub-classes. Strands mechanically derives instrument descriptions from their schema and docstrings, giving the LLM structured context about every instrument’s return kind.
- Sort-consistent error dealing with: When no token exists and AgentCore Identification returns an authorization URL, the on_auth_url callback raises an AuthorizationRequiredError fairly than returning a string — a instrument declaring GitHubUser as its return kind can’t return a URL. The agent’s streaming layer catches the exception and surfaces the URL to the person.
- Scopes per instrument: Every instrument declares solely the OAuth scopes it wants, conserving consent aligned with the precept of least privilege.
Finishing the OAuth session binding move
Subsequent, we have a look at the session binding service. When a person authorizes entry in GitHub, GitHub redirects them to {session_binding_url}?session_id={session_id}, the place session_id corresponds to the session URI that AgentCore Identification included within the unique authorization URL. This ties the session binding request to the precise OAuth move the agent initiated.
@router.get(“/session-binding”, response_class=HTMLResponse)
async def oauth_session_binding(
session_id: str = Question(…, description=”Session URI from AgentCore Identification”),
user_id: str = Relies upon(get_current_user),
settings: Settings = Relies upon(get_settings),
) -> HTMLResponse:
“””Deal with OAuth2 session binding from exterior suppliers.”””
consumer = boto3.consumer(“bedrock-agentcore”, region_name=settings.identity_region)
attempt:
consumer.complete_resource_token_auth(
sessionUri=session_id,
userIdentifier={“userId”: user_id},
)
The service extracts the person ID from the sub declare within the x-amzn-oidc-data header, making certain constant identification throughout the move. It then calls complete_resource_token_auth with the session URI and person ID, which binds the ensuing entry token to the right person session.
Cleanup
To keep away from incurring future prices, delete the sources created by this resolution when they’re not wanted. Observe the instruction for cleansing up the sources.
Conclusion
On this publish, you discovered how one can safe AI brokers on Amazon ECS utilizing Amazon Bedrock AgentCore Identification. You noticed how inbound authentication verifies person identification through OIDC, how outbound authentication implements OAuth 2.0 with session binding, and the way separating session binding out of your agent workload allows unbiased scaling whereas defending towards assaults. This sample works throughout completely different compute platforms, whether or not you run brokers on ECS, EKS, Lambda, or exterior AWS fully. It additionally extends past GitHub to different OAuth 2.0-enabled companies like Jira, Salesforce, or Google Calendar. Subsequent steps:
- Assessment the entire code in GitHub to see the implementation
- Adapt the sample to your OAuth supplier, change GitHub together with your service
- Discover extra patterns within the AgentCore Identification Samples repository
- Learn the publish on AgentCore Runtime for managed agent internet hosting
- Dive into the AgentCore Identification documentation
Concerning the authors
Julian Grüber is a Information Science Marketing consultant at Amazon Net Companies. He companions with strategic clients to scale GenAI options that unlock enterprise worth, working at each the use case and enterprise structure stage. Drawing on his background in utilized arithmetic, machine studying, enterprise, and cloud infrastructure, Julian bridges technical depth with enterprise outcomes to handle advanced AI/ML challenges
Tobias works as Safety Marketing consultant at Amazon Net Companies as a Safety Engineer. Tobias combines hands-on resolution constructing with strategic advisory to assist enterprise customersaccelerate their cloud transformation and obtain their enterprise aims. He makes a speciality of partnering with strategic clients to design and scale GenAI options, working at each the use-case and enterprise-architecture stage.
Satveer Khurpa is a Sr. WW Specialist Options Architect, Amazon Bedrock AgentCore at Amazon Net Companies, specializing in agentic AI safety with a concentrate on AgentCore Identification and Safety. On this function, he makes use of his experience in cloud-based architectures to assist purchasers design and deploy safe agentic AI methods throughout numerous industries. Satveer applies his deep understanding of agentic AI patterns, identification and entry administration, and defense-in-depth safety ideas to architect scalable, safe, and accountable agent-based purposes, enabling organizations to unlock new enterprise alternatives whereas sustaining sturdy safety postures for autonomous AI workloads.

