An AI agent that updates a ticket or reads a buyer document acts for an individual, inside functions that individual can attain. Your identification crew then has to reply a query that per-app OAuth consent and shared service accounts go away open: which agent would possibly act for which consumer by which software, and the way you cease it. Okta Cross App Entry (XAA) solutions it in your identification supplier (IdP). You register the agent in Okta, an administrator approves the functions it would attain, and Okta evaluates coverage every time the agent asks to behave for a consumer. In our exams, after an administrator eliminated a consumer’s task to the agent, Okta refused the agent’s new requests for that consumer inside about 1 second, and tokens already issued expired inside 5 minutes.
Amazon Bedrock AgentCore is a platform to construct, join, and optimize brokers at scale, with any framework or mannequin. This resolution makes use of two of its capabilities to hold out Okta’s determination on AWS. Amazon Bedrock AgentCore runtime, a functionality of Amazon Bedrock AgentCore, validates Okta’s grant. Amazon Bedrock AgentCore Id, a functionality of Amazon Bedrock AgentCore, redeems it for a short-lived entry token with credentials your agent code doesn’t see, and retains that token in its token vault. In our exams, AgentCore Id added about 0.6 seconds to a request that took about 30 seconds finish to finish.
On this publish, the primary of two, you construct a process agent on AgentCore runtime that acts for signed-in customers by means of XAA and AgentCore Id. You configure the AgentCore Id elements it wants, implement every consumer’s authority within the instruments, and see what we measured: what every test refuses, the practices we settled on, and how briskly Okta’s controls take impact. Half 2 then places the identical governance behind Amazon Bedrock AgentCore Gateway, a functionality of Amazon Bedrock AgentCore, with Cedar insurance policies, a second agent on Amazon Elastic Kubernetes Service (Amazon EKS), and an audit path in Amazon CloudWatch.
Answer overview
Okta is the governance aircraft: it decides which agent would possibly act for which consumer, in opposition to which useful resource. AgentCore Id is the execution aircraft: it holds the agent’s credentials, turns Okta’s determination right into a token the useful resource accepts, and retains each identities in that token. In Determine 1, the agent and the 2 Mannequin Context Protocol (MCP) servers it calls all run on AgentCore runtime.
Every request carries three tokens, and every information a unique determination: the ID token says who the consumer is, the Id Assertion JWT Authorization Grant (ID-JAG) is Okta’s determination, and the entry token is what the useful resource accepts. One request flows like this:
- The consumer indicators in to the online entrance finish, a backend for frontend (BFF), with OpenID Join by means of the agent’s linked Okta app.
- For every request, the BFF exchanges the consumer’s ID token for an ID-JAG at Okta’s org authorization server, authenticated because the agent (RFC 8693 token alternate). Okta checks that the agent is lively, that the consumer is assigned to it, and that an administrator linked it to the goal authorization server for the requested scopes. The ID-JAG is single-use and expires in 5 minutes.
- The BFF invokes the agent on AgentCore runtime with the ID-JAG as its bearer token. The runtime’s JWT authorizer accepts solely ID-JAGs that Okta issued to this agent.
- The runtime will get a workload entry token from AgentCore Id that binds the agent’s workload identification to the consumer, and passes it to the agent code. This workload entry token is an AWS-internal binding token: neither Okta nor the useful resource sees it, so it isn’t one of many three tokens.
- The agent asks AgentCore Id for an entry token. AgentCore Id redeems the ID-JAG on the useful resource’s Okta customized authorization server with the JWT authorization grant (RFC 7523), authenticated because the agent. Okta returns a 5-minute entry token that names each the consumer and the agent and carries the consumer’s teams.
- The agent calls the MCP servers with that token. The duty supervisor checks {that a} registered agent obtained it, and that the consumer’s group and scope permit the device.
Determine 2 reveals the identical request as a sequence, with the test that every occasion makes.
Determine 2: The three-token chain and the test every occasion makes, with the step 4 workload entry token staying inside AWS
Stipulations
To comply with alongside, you want the next:
| Prerequisite | What we used |
| An AWS account with entry to Amazon Bedrock AgentCore and an Anthropic Claude mannequin in Amazon Bedrock | US West (Oregon) |
| An Okta org with Cross App Entry for AI brokers and API Entry Administration (customized authorization servers) | An Okta Integrator Free Plan org |
| Python, the AWS Command Line Interface (AWS CLI), boto3, the AgentCore CLI, and the AgentCore Python SDK, a functionality of Amazon Bedrock AgentCore | Python 3.11, AWS CLI model 2, boto3 1.43, AgentCore CLI 0.31.1, and AgentCore Python SDK 1.24.1 |
| AWS Id and Entry Administration (IAM) permissions | Permission to create AgentCore assets and IAM roles |
The walkthrough takes about 1 hour. AgentCore expenses for what you utilize. See Amazon Bedrock AgentCore pricing.
The bottom for this resolution is the AWS pattern 08-okta-xaa-delegation-chain within the AgentCore samples repository on GitHub, and you may construct the answer on this publish on high of it. Past that pattern, the snippets and configurations on this publish present the sample conceptually. They aren’t an entire, deployable resolution. The code is pattern code for non-production use, so work along with your safety and authorized groups to satisfy your group’s safety, regulatory, and compliance necessities earlier than you deploy it.
Register the agent in Okta
Okta wants an authorization server for the useful resource, the agent, a connection between them, and a coverage that admits the agent’s grant. Do all of those steps within the Okta Admin Console: registering the agent creates its linked app, so that you don’t want an app integration wizard.
- Create a customized authorization server for the useful resource software with the viewers
api://task-managerand the customized scopesxaa:learnandxaa:write. Request solely the useful resource’s customized scopes: Okta rejects an ID-JAG request that features OpenID Join scopes comparable toopenidorprofile(invalid_scope), so a request wants at the very least one customized scope and no OpenID Join scopes. - Add a
teamsdeclare to its entry tokens, filtered to teams that begin withxaa-. This declare carries every consumer’s authority to the instruments. - Within the Admin Console, go to Listing > AI Brokers, select Register AI agent after which Register manually, optionally assign an proprietor, and create a linked OpenID Join app. On Consumer registration, select Consumer secret, generate a secret, after which activate the agent. The agent and the app share one shopper ID, and that app should signal your customers in: an ID token from one other app fails the alternate with “the shopper software will not be registered for delegation to this agent.”
- On the agent’s Useful resource connections tab, join it to the authorization server, restricted to the scopes it wants, as Determine 3 reveals.
- Add an entry coverage rule to the customized authorization server that enables the agent’s JWT Bearer grant, with a 5-minute entry token lifetime. With out the grant kind, the second alternate fails with
no_matching_policy, and the lifetime bounds how lengthy a revoked delegation retains working. - Assign the customers or teams who would possibly act by means of the agent. They register by means of the linked app, so give it an app sign-in coverage that admits them with the authenticators they’ve enrolled. In our sandbox org, the linked app had its personal coverage with a rule for the take a look at group. For actual customers, apply your group’s coverage, together with multifactor authentication.
Determine 3: The duty agent’s useful resource connection in Okta, restricted to the xaa:learn and xaa:write scopes
Okta’s Cross App Entry information covers the console steps, and the AWS pattern 08-okta-xaa-delegation-chain scripts the authorization server and its JWT bearer coverage. The BFF then requests the ID-JAG, authenticated because the agent:
The AWS pattern 06-okta-xaa sends the identical request in Python (agent/xaa_client.py). The BFF authenticates because the agent, so retailer the agent’s shopper secret in AWS Secrets and techniques Supervisor somewhat than in code or configuration recordsdata.
Arrange AgentCore Id
AgentCore Id holds three issues for this resolution, and your agent code doesn’t see the shopper secret or name Okta’s token endpoint:
| A part of AgentCore Id | What it holds | Who creates it |
| Workload identification | The agent’s identification on AWS, sure to the signed-in consumer for every request | AgentCore runtime, for every agent runtime |
| OAuth2 credential supplier | The agent’s Okta shopper ID and secret, and the grant to make use of | You, as soon as per Okta agent |
| Token vault | The entry tokens the supplier obtains | AgentCore Id |
Create the credential supplier
The credential supplier performs the second alternate. Level it on the customized authorization server, authenticate it because the AI agent, and set the on-behalf-of grant to JWT_AUTHORIZATION_GRANT, which AgentCore Id sends as jwt-bearer with the ID-JAG because the assertion:
Okta binds each exchanges to the shopper named within the ID-JAG, so every Okta agent wants its personal supplier: after we redeemed one agent’s ID-JAG as one other, Okta refused with “The ‘client_id’ within the JWT Bearer Grant should match the ‘client_id’ used to authenticate the shopper.” AgentCore Id retains the key in a service-managed AWS Secrets and techniques Supervisor secret, which you rotate with update-oauth2-credential-provider. In case your crew already manages the shopper secret in Secrets and techniques Supervisor, you may reference that secret’s ARN as a substitute. AgentCore Id reads the shopper secret from it, so you may rotate the key with out updating the supplier (see Reference your personal AWS Secrets and techniques Supervisor secrets and techniques in Amazon Bedrock AgentCore Id). To keep away from a shared secret, authenticate with a personal key as a substitute. See Authenticate with Personal Key JWT utilizing Amazon Bedrock AgentCore Id. Determine 4 reveals the supplier within the AgentCore console.
Determine 4: The credential supplier authenticating because the agent with the JWT authorization grant
Use the workload identification that AgentCore runtime manages
AgentCore runtime creates and manages a workload identification for every agent runtime. When a request passes the runtime’s JWT authorizer, the runtime will get a workload entry token for that workload identification and the inbound token’s issuer and topic, and passes it to your code. AgentCore Id then makes use of the ID-JAG behind it because the assertion for the alternate.
The AgentCore Python SDK retains the workload entry token in BedrockAgentCoreContext, not within the request headers that your entrypoint receives. Our first model seemed for it within the headers, discovered nothing, and fell again to its personal GetWorkloadAccessTokenForJWT name with a second workload identification, on all 21 requests we checked. Learn it from the context as a substitute:
AgentCore Id retains the entry token in its token vault for the remainder of the request. Throughout 10 requests, the agent’s first name redeemed the ID-JAG in about half a second (see the latency desk in Validate the outcomes), and the second, for the opposite MCP server, returned the identical token in a median of 58 milliseconds with out redeeming the single-use ID-JAG once more. A brand new ID-JAG for a similar consumer received a brand new token, so every request displays Okta’s newest determination. The pattern 08-okta-xaa-delegation-chain reads the workload entry token the identical method (agent/agent.py).
Scope the agent’s position to its workload identification and supplier
AgentCore Id doesn’t bind workload identities to credential suppliers in the identical account. The agent’s IAM position does, so scope it to the agent’s personal workload identification and supplier, and to that supplier’s secret:
Take the workload identification’s identify from workloadIdentityDetails in GetAgentRuntime, and the supplier’s ARN from credentialProviderArn: the phase is oauth2credentialprovider, and with the hyphenated type each name failed with AccessDeniedException. The position wants no GetWorkloadAccessToken permission, as a result of the runtime will get that token, not the agent. Our exams handed with solely these two statements.
Deploy the agent on AgentCore runtime
Configure the agent runtime’s inbound authorizer to simply accept solely ID-JAGs that Okta issued to this agent. Its issuer is Okta’s org authorization server, and its viewers is the useful resource’s customized authorization server. In agentcore.json:
allowedClients matches the client_id declare that an ID-JAG carries. With out it, the runtime admitted one other agent’s ID-JAG with HTTP 200 and the request failed later, on the alternate. With it, the runtime refused that token with HTTP 401.
Moreover the coverage from the earlier part, the execution position wants bedrock:InvokeModel and bedrock:InvokeModelWithResponseStream. It wants no bedrock-agentcore:InvokeAgentRuntime permission for the MCP servers: every runs on AgentCore runtime behind its personal JWT authorizer, with the customized authorization server as issuer and api://task-manager as viewers, so the agent calls it with the Okta entry token as a bearer token, not with AWS Signature Model 4 (SigV4).
Implement every consumer’s authority within the MCP server
The useful resource connection units the scopes for each consumer of an agent, so all three take a look at customers acquired xaa:learn xaa:write, and per-user authority travels within the teams declare as a substitute: Alice’s token carried xaa-readers, Bob’s xaa-writers, and Carol’s had none.
The duty supervisor checks two issues: it accepts solely tokens a registered agent obtained, and it permits a write solely when the group and the scope are each current:
Each checks shut gaps we discovered: a write test that allowed tokens with no teams declare let Carol, who belongs to neither group, create duties, and earlier than _require_agent a plain token-exchange token, issued to a service app with no act declare, created a process for Bob. The server can learn the claims with out verifying the token once more solely as a result of the runtime’s JWT authorizer already verified it. A server with out that authorizer in entrance of it should test the signature, issuer, viewers, and expiry itself, because the Half 2 agent on Amazon EKS does.
Validate the outcomes
We ran each test in opposition to the deployed resolution. Alice, Bob, and Carol every requested the agent to create a process after which record the challenge’s duties:
| Caller | create_task |
list_tasks |
| Alice | Denied: requires membership in xaa-writers |
Allowed |
| Bob | Created | Allowed |
| Carol | Denied: no teams declare |
Allowed |
| Bob, then Alice proper after him | Created for Bob, then denied for Alice: no authority carried over | Allowed |
These requests stopped earlier than instruments ran:
| Request | Stopped by | Response |
| Agent B’s ID-JAG despatched to the duty agent | The runtime’s JWT authorizer | 401: “Declare ‘client_id’ worth mismatch with configuration.” |
| An ID-JAG held for 245 seconds | The runtime’s JWT authorizer | 401: “Ineffectual token, will expire inside the subsequent minute” |
| A malformed bearer token | The runtime’s JWT authorizer | 403: “Didn’t parse token” |
| One ID-JAG redeemed twice | Okta’s customized authorization server | 400: “The id-jag was already used.” |
| A narrower scope on the second alternate | Okta’s customized authorization server | 400: “The scope parameter should precisely match the scopes within the id-jag.” |
| A scope exterior the connection | Okta’s org authorization server | 400 invalid_scope |
| A plain token alternate token on the MCP server | The MCP server | Denied: solely tokens {that a} registered AI agent obtained |
The token that AgentCore Id returned matched one we redeemed straight at Okta, declare for declare: sub was the consumer, cid and act named the agent, scp held the connection’s scopes, teams held the consumer’s teams, and it lived 300 seconds.
AgentCore Id provides about 0.6 seconds to a request that takes about 30 seconds finish to finish, most of it mannequin and gear calls. Medians of 5 runs from our take a look at shopper:
| Name | Median |
| Signal-in, scripted (authorization code move) | 1,205 ms |
| ID token to ID-JAG at Okta’s org authorization server | 216 ms |
GetWorkloadAccessTokenForJWT |
135 ms |
GetResourceOauth2Token, together with Okta’s JWT bearer grant |
508 ms |
Govern brokers in operation
Okta evaluates the agent, the consumer, and the connection when it mints an ID-JAG. We measured what every administrator motion does to the subsequent request and to issued tokens:
| Admin motion in Okta | New requests | Tokens already issued |
| Delete the agent’s useful resource connection | Refused instantly (invalid_target) |
Work till they expire, as much as 5 minutes |
| Take away the consumer’s task | Refused inside about 1 second, even with an ID token the consumer already held | As much as 5 minutes |
| Deactivate the agent | Refused after Okta runs the operation, which our take a look at org queued for 86 minutes | As much as 5 minutes |
Actions on one agent left the opposite working. Okta doesn’t re-check the consumer when an ID-JAG is redeemed: its customized authorization server issued an entry token 3 seconds after we eliminated a consumer, for an ID-JAG minted earlier than the removing. Okta’s System Log information every grant with the agent because the actor, and Half 2 streams these occasions into CloudWatch.
Greatest practices for the delegation chain
Six practices got here out of those measurements, each a property of the chain somewhat than a choice:
| Follow | What we measured |
| Mint a contemporary ID-JAG for every request | An ID-JAG is single-use and lives 300 seconds. The runtime refuses one with below 60 seconds left, so 225 seconds labored, 245 didn’t |
Carry per-user authority within the teams declare, and request one scope set per request |
Okta units scopes from the useful resource connection, so all three customers received the identical scopes, and the second alternate should request precisely the ID-JAG’s |
Pin the agent on the runtime, and test act and cid within the device |
allowedClients matches the client_id declare an ID-JAG carries. Okta entry tokens carry cid as a substitute |
| Reproduce a failed alternate straight in opposition to Okta | By AgentCore Id, a scope mismatch and a flawed shopper each floor as “Token alternate failed with HTTP standing 400” |
| Grant the agent’s position learn entry to the supplier’s secret | GetResourceOauth2Token failed till the position may learn it |
| Preserve lifetimes brief, and use the useful resource connection as your kill change | Deleting the connection took impact instantly. Deactivating the agent sat in Okta’s beta AI agent API queue for 86 minutes, with no cancel |
Cross App Entry in contrast with plain token alternate
Plain RFC 8693 token alternate additionally carries the consumer’s identification downstream. We in contrast it with XAA in opposition to the identical Okta authorization server and consumer:
| Conduct we noticed | Plain token alternate | Cross App Entry |
| Agent identification within the token | No act declare |
act names the agent |
| Scopes | Widened: Alice’s personal token had xaa:learn, and the alternate returned xaa:learn xaa:write |
The scopes the administrator authorized |
Change rule restricted to xaa-writers |
Alice nonetheless acquired a token | Alice denied |
| Person unassigned, earlier token reused | Nonetheless exchanged | Refused |
| What directors see | A service app with a grant kind | The agent, its connections, and the agent in every log occasion |
These outcomes replicate our Okta configuration. Okta’s information reveals an act declare for exchanged tokens, which we didn’t obtain. Plain token alternate carried the consumer’s identification, however not the consumer’s authority.
The place this sample applies
We work with media and leisure firms, and that is the place we met the issue first. A studio or streaming or video games group runs brokers in opposition to manufacturing monitoring, title and asset metadata, rights and localization, and viewer or participant assist. Every system has its personal authorization mannequin and its personal auditors, and a consumer’s authority differs by title, territory, or launch window, so “which agent would possibly act for which consumer by which system” must be answered earlier than an agent ships.
Nothing within the chain is restricted to that trade: the agent is an identification in your IdP, the goal is an OAuth 2.0 authorization server, and the consumer’s authority rides in a declare. Monetary companies, healthcare, and public sector groups put completely different assets behind the identical controls.
Clear up
To keep away from ongoing expenses, delete the assets you created:
- Delete the AWS CloudFormation stacks that the AgentCore CLI created for the agent and the 2 MCP servers.
- Delete the credential supplier with
aws bedrock-agentcore-control delete-oauth2-credential-provider --name okta-xaa-obo, and any workload identification you created your self withdelete-workload-identity. - Delete the IAM insurance policies you added to the execution roles.
- In Okta, deactivate and delete the AI agent and its linked app, and take away the useful resource connection, the authorization server’s coverage rule, scopes, and declare, and the take a look at customers and teams.
Conclusion
On this publish, you used Okta Cross App Entry to determine which agent would possibly act for which consumer, and AgentCore Id to hold out every determination. The runtime admitted solely the agent’s personal ID-JAGs, the credential supplier redeemed each with a secret the agent didn’t maintain, the token vault reused the end result inside a request, and the MCP server enforced every consumer’s authority from the teams declare.
This design retains the alternate within the agent’s position and repeats the authorization test in each MCP server. See Half 2 of this collection to be taught why its design strikes each behind one AgentCore Gateway with Cedar insurance policies, provides a second agent on Amazon EKS, and joins the Okta System Log with AgentCore traces in CloudWatch.
To get began, discover the AgentCore samples and Okta’s Cross App Entry documentation. For the API particulars, see On-behalf-of token alternate within the AgentCore Developer Information.
Concerning the authors


