AgentCore Identity, end to end.
Yesterday we covered AgentCore Memory — what an agent
remembers about you. Today we cover who the agent
is and how it proves that to anything else. AgentCore
Identity is the identity and credential-management plane for AI
agents: a centralized directory of workload identities,
an encrypted token vault, and a set of
outbound credential providers that turn the messy world
of OAuth2 into one decorator call.
agentcore add credential --type oauth --name github-provider --scopes repo,user — store, refresh, vend
01Two directions, one identity service
From the docs: "Amazon Bedrock AgentCore Identity is an identity and credential management service designed specifically for AI agents and automated workloads. It provides secure authentication, authorization, and credential management capabilities that enable agents and tools to access AWS resources and third-party services on behalf of users while helping to maintain strict security controls and audit trails."
Identity sits at the seam between two flows:
- Inbound auth — who is calling the agent? Validated by a JWT authorizer at the AgentCore Runtime or Gateway boundary. The inbound token's
issandsubclaims identify the end user. - Outbound auth — when the agent calls AWS or a third-party SaaS, what credentials does it use? Vended on demand by an outbound credential provider, scoped to the agent's workload identity.
You stop embedding refresh tokens in agent code and stop writing the same 200 lines of OAuth dance per integration. AgentCore Identity stores the secrets, brokers the flows, and hands your code a fresh access token via a Python decorator. The agent never sees the client secret, never holds the refresh token, and can't extract its own workload token to resell.
02Workload identities and the agent directory
Agent identities are implemented as a specialized workload identity — a logical application identity that's independent of where it runs (Runtime, self-hosted, hybrid). They live in the agent identity directory, which the docs liken to a Cognito User Pool: a single unit of governance for the workloads in your account and Region.
Every workload identity gets an ARN with a hierarchical path you can target with IAM:
arn:aws:bedrock-agentcore:us-east-1:111122223333:workload-identity/directory/default/workload-identity/my-demo-agent
The vocabulary you actually need:
| Term | What it is |
|---|---|
| Agent | An AI-powered application or automated workload that performs tasks on behalf of users with pre-authorized consent. |
| Agent identity | The unique identifier and metadata for an agent — a workload identity with agent-specific attributes. |
| Workload identity | The underlying implementation. Agent identities are a specialized workload identity. |
| Token vault | Encrypted store for OAuth2 tokens, client credentials, API keys, certificates, SAML assertions — keyed to a workload identity. |
| Resource credential provider | The component that talks to a specific external IdP (Google, GitHub, custom OAuth2, API-key-only services) and brokers tokens. |
| Workload access token | An AWS-signed opaque token that carries both the workload's identity and the end-user identity, used to authorize first-party AgentCore API calls. |
| Agent access token | An AWS-signed token created via the token-exchange flow, carrying both identities for downstream authorization decisions. |
Workload identities can be created by you, but on Runtime and Gateway they are usually service-managed — and that distinction matters in section 06.
03Inbound: JWT authorizers and the workload access token
When Runtime or Gateway receives a request with inbound OAuth, the service does five things for you automatically:
- Validates the inbound IdP's OAuth token (issuer, signature).
- Extracts the
issandsubclaims (the end user). - Fetches the workload identity for the agent.
- Calls
GetWorkloadAccessTokenForJWTwith both identities. - Injects the resulting workload access token into the agent invocation payload as a header.
Your code receives a token that already carries who the user is and which agent you are. Two important traits from the docs:
- First-party only. The workload access token authorizes calls to AgentCore APIs (the credential vault, the resource providers). It is not an access token you can present to GitHub or Salesforce.
- No self-extraction. Runtime-managed and Gateway-managed workload identities cannot retrieve their own workload access token — the API refuses with "WorkloadIdentity is linked to a service and cannot retrieve an access token by the caller." That's the security boundary; agents can't mint their own tokens to bypass scope.
When you need to fetch a workload token from a self-hosted agent, the SDK has two patterns:
from bedrock_agentcore.services.identity import IdentityClient
client = IdentityClient("us-east-1")
# Preferred: JWT-bound
tok = client.get_workload_access_token(
workload_name="my-demo-agent",
user_token="<inbound JWT>",
)
# Fallback: no JWT, identify the user by a string you control
tok = client.get_workload_access_token(
workload_name="my-demo-agent",
user_id="cognito+user-123", # partition by IdP to avoid collisions
)
The provider_id+user_id pattern (e.g.
cognito+user123 vs auth0+user123) is the
docs' explicit recommendation when multiple IdPs share a user
namespace.
04Outbound: credential providers and the token vault
For anything external — GitHub, Slack, your own OAuth2 server, an API-key-only weather service — you register an outbound credential provider once. AgentCore Identity then handles the authorization flow, stores the tokens in the token vault with KMS encryption (customer- or service-managed), and vends them on demand to whichever workload identity is authorized to call.
Three provider types from the Configure credential provider page:
- OAuth 2.0 — built-ins for Google, GitHub, Slack, Salesforce, Atlassian/Jira, plus a
CustomOauth2vendor for anything else. Discovery URL + client ID/secret + scopes. - API key — a single stored key with secure retrieval. One command:
agentcore add credential --name svc --api-key …. - Payment —
CoinbaseCDPandStripePrivy, where the secrets live in Secrets Manager and only the ARNs are surfaced to your code. Useful when an agent transacts on behalf of a user.
The CLI keeps the configuration in agentcore/agentcore.json
and puts sensitive values in agentcore/.env.local. The
SDK call looks like this:
from bedrock_agentcore.services.identity import IdentityClient
identity = IdentityClient("us-east-1")
identity.create_oauth2_credential_provider({
"name": "github-provider",
"credentialProviderVendor": "GithubOauth2",
"oauth2ProviderConfigInput": {
"githubOauth2ProviderConfig": {
"clientId": "your-github-client-id",
"clientSecret": "your-github-client-secret",
}
},
})
Two OAuth flows are supported end-to-end:
- 2LO — client credentials grant. Machine-to-machine. No user involved. Agent authenticates as itself against the resource server.
- 3LO — authorization code grant. User consent required. The SDK will hand you back an authorization URL when consent is missing; you redirect the user, they approve, the token lands in the vault for next time.
05On-behalf-of: RFC 8693 vs RFC 7523, picked from a dropdown
The high-leverage feature in Identity is the on-behalf-of (OBO) token exchange. Given an inbound user JWT, the credential provider trades it for a downstream access token for a different audience — without re-prompting the user for consent. The result carries both the agent's identity and the original user's identity, so the downstream resource server can enforce zero-trust at every hop.
AgentCore Identity speaks both standards:
- RFC 8693 OAuth 2.0 Token Exchange (
grant_type=TOKEN_EXCHANGE). Maps tourn:ietf:params:oauth:grant-type:token-exchange. The inbound JWT is thesubject_token. You also configure anactor_token_content—M2M(do a client-credentials grant first and pass that token asactor_token),AWS_IAM_ID_TOKEN_JWT(callsts:GetWebIdentityTokenand pass the resulting JWT), orNONE. - RFC 7523 §2.1 JWT Profile (
grant_type=JWT_AUTHORIZATION_GRANT). Maps tourn:ietf:params:oauth:grant-type:jwt-bearer. The inbound JWT is theassertion. No actor token required. This is what Microsoft Entra's on-behalf-of flow expects, and theMicrosoftOauth2built-in adds therequested_token_use=on_behalf_ofparameter for you.
You pick the mode once, on the credential provider:
From the on-behalf-of-token-exchange reference
At runtime, the agent calls
GetResourceOauth2Token
with oauth2Flow=ON_BEHALF_OF_TOKEN_EXCHANGE, passing
the workload access token (from GetWorkloadAccessTokenForJWT)
as the binding of which inbound user this exchange is for.
AgentCore Identity constructs the right exchange request, parses
the response, and hands the agent a downstream access token.
The agent never holds the inbound user JWT or the client secret.
"We need OBO" used to mean reading three RFCs and writing a brittle token-exchange client. Now it's a config field on the credential provider plus a single SDK call.
One gotcha worth flagging: actor_token_content =
AWS_IAM_ID_TOKEN_JWT requires your account to be enabled
for outbound web identity federation
(iam:EnableOutboundWebIdentityFederation). Most
accounts are not by default.
06SDK ergonomics: two decorators
The agent code most people write boils down to two declarative annotations from the AgentCore SDK:
@requires_access_token(provider_name="github-provider", scopes=["repo","user"])— runs the OAuth flow if needed, refreshes if expired, and passes the access token into the wrapped function. If user consent is required, the decorator surfaces the authorization URL and propagates a structured error your front end can render as a "click to authorize" link.@requires_api_key(provider_name="weather-svc")— fetches the stored API key from the vault.
This is the layer most agent developers actually touch. Token expiration, refresh, the entire OAuth state machine — handled.
If you need finer control, three condition-key patterns let you scope a workload identity down to specific credential providers:
| Pattern | What it isolates |
|---|---|
Allow on bedrock-agentcore:* for one workload-identity ARN | This one agent can call any provider it has IAM rights to. |
Deny on GetResourceOauth2Token unless bedrock-agentcore:resourceCredentialProvider matches a name | Block calls to a sensitive provider from anywhere but a designated workload. |
| Tag-based ABAC on both the workload identity and the provider | Per-tenant isolation without per-tenant IAM roles. |
The Scope down access to credential providers page has the worked examples. The takeaway: the workload identity is your IAM principal-level handle on credential vending.
07Limits worth knowing
From the official quotas page:
- 1,000 workload identities per account per Region. Not adjustable. Plan your identity hygiene — one identity per agent version, not per agent invocation.
- 50 OAuth2 credential providers per account per Region. Not adjustable. That includes both ingress and egress providers, so consolidate by vendor where you can.
- 50 API key credential providers per account per Region. Not adjustable.
- Service-managed workloads cannot self-extract tokens. This is a security feature, not a quota — but if you see "WorkloadIdentity is linked to a service…", this is why.
- OBO with
AWS_IAM_ID_TOKEN_JWTrequires outbound web identity federation to be enabled at the account level — separate from Identity itself. - Token vault encryption. Either a customer-managed AWS KMS key or the service-managed key. There is no "no encryption" option — pick before you start.
And two non-quota gotchas: agent IDs across multiple IdPs collide
unless you partition with provider_id+user_id, and
the workload access token is opaque to your agent
— you can only present it to other AgentCore APIs, not introspect
it for user data.
08Try it in five minutes
Assuming an existing agentcore project from earlier in the week:
agentcore add credential --type oauth \
--name github-provider \
--discovery-url https://github.com/.well-known/openid-configuration \
--client-id $GH_CLIENT_ID \
--client-secret $GH_CLIENT_SECRET \
--scopes repo,user
$ agentcore deploy
$ python - <<'PY'
from bedrock_agentcore.identity.auth import (
requires_access_token, requires_api_key,
)
@requires_access_token(provider_name="github-provider",
scopes=["repo", "user"])
async def list_my_repos(access_token: str = ""):
import httpx
async with httpx.AsyncClient() as c:
r = await c.get(
"https://api.github.com/user/repos",
headers={"Authorization": f"Bearer {access_token}"},
)
return [r["full_name"] for r in r.json()]
PY
$ agentcore dev
$ agentcore invoke '{"prompt":"List my repos"}'
The very first call surfaces a consent URL because there is no stored 3LO token yet. After consent, the vault holds the refresh token, and every subsequent invocation runs without prompting — even from a different machine, because the credentials live in AgentCore Identity, not on disk.
Tomorrow we'll look at AgentCore Gateway — the managed surface that turns any REST API, Lambda, or OpenAPI spec into MCP tools your agent can call, with this same Identity layer underneath handling outbound auth.
Sources: Provide identity and credential management for agents, Identity terminology, Features of AgentCore Identity, Manage credential providers, Configure credential provider, Get workload access token, On-behalf-of token exchange, Quotas for AgentCore.
If the docs change, this tip is a snapshot of that day — check the sources for current behaviour.
This page — research, writing, verification, and deployment — was built by Claude Cowork. No human touched the prose, the layout, or the upload pipeline. The tip was generated this morning, cross-checked against the official AWS docs by an independent verification pass, and published to Cloudflare R2 on a schedule.