Agent Identity with Azure Entra ID
How an AI agent's identity reaches the Prisma AIRS AI Gateway, what the gateway does with it, and where it stops
What This Page Covers
An AI agent calls the AI Gateway. Somebody then asks who made that call, and whether the answer in the logs is a person, an application, or nothing useful at all. This page follows one agent request from the directory sync that ran last night through to the provider the gateway finally reaches, and names every component on the path.
The scope is deliberately narrow. It covers identity: how a credential is issued, how the gateway validates it, what the gateway concludes about who is calling, and what it does with that conclusion. It does not cover model routing, guardrails, or cost controls beyond the point where identity feeds into them.
User-ID is a PAN-OS mechanism. A firewall learns an IP-to-user mapping out of band, from a domain controller or a User-ID agent, and security policy then matches on the user it found. The AI Gateway has none of that. It has no IP-to-user mapping, no User-ID agent, and no directory group travelling alongside the request. Identity arrives inside the request, as the credential itself. The useful question is therefore not how to turn User-ID on. It is which credential the agent presents, and what identity that credential carries.
Who this is for
- Architects deciding how agents should authenticate to the gateway before any code is written
- Engineers debugging a gateway log where every call is attributed to the same generic key
- Anyone who has been asked whether Entra ID groups can drive AI Gateway policy, and wants a precise answer
The Three Entra Roles
Entra ID appears three times in this flow and means something different each time. Conflating the three is the single most common source of confusion, because all three can point at the same tenant while being configured in three different places. Breaking one does not break the others, and fixing the wrong one wastes an afternoon.
| Role | Where it is configured | When it runs | What it decides |
|---|---|---|---|
| Source directory | Strata Cloud Manager, under Cloud Identity Engine | On a background sync schedule | Who is a member of which AI Gateway workspace |
| Token issuer | An app registration in Entra, plus a JWKS URL in the gateway | On every request | Whether the call is authenticated, and whose identity it carries |
| Egress credential | The gateway's Azure provider configuration | On every request that reaches a provider | How the gateway authenticates itself to Azure AI Foundry |
A customer who says "we use Entra ID" has told you almost nothing about their gateway configuration. They may have the directory synced and no JWT path at all, or a JWT path and no directory sync, in which case every token authenticates successfully and attributes to nobody. Establish which of the three roles is actually in place before designing anything on top of it.
The Complete Flow
The diagram below carries every element in scope, in three bands. Band A is the control plane, which runs on a sync schedule and never touches a request. Band B is the one-off configuration, in three separate places. Band C is a single agent call from end to end, with the one comparison that decides attribution marked in gold. The reference panels on the right cover the claim shapes, where validation runs, the failure modes, and the point at which identity stops being forwarded.
Open the image in a new tab to read it at full size:
Each band becomes a phase below. The phases run in order, because workspace membership has to exist before a token can be matched against it.
Prerequisites
Confirm each of the following before starting. The directory sync path and the gateway-local validation path each carry their own floor, and discovering a version gap in the middle of phase 3 is avoidable.
| Requirement | Applies to | Note |
|---|---|---|
| A Strata Cloud Manager tenant | Directory Sync (phase 1) | Cloud Identity Engine is reached through SCM. Non-SCM tenants provision users through SCIM instead. |
| Administrator access to the Entra tenant | Phases 1 and 2 | Needed to authorise the CIE directory and to create the agent's app registration. |
| Organisation owner or admin in the AI Gateway | Phase 2 | The JWKS configuration lives under Admin Settings, which a workspace-scoped role cannot reach. |
| Gateway 2.5.0 or higher | Phase 3, hybrid only | Required for JWT_ENABLED=ON. Air-gapped deployments additionally need backend v1.13.0 or higher. |
| Gateway 2.1.0 or higher | Phase 2, Entra specifically | Entra's JWKS omits the alg field on its keys. Builds from 2.1.0 onward fall back to RS256; earlier builds fail validation. |
| Gateway 2.21.0 or higher | Phase 5 | Carries email-based attribution of JWT callers to workspace users, and support for multiple JWKS URLs. |
Verification: read the gateway version from the Admin Settings page or from the deployed image tag, and confirm it clears the highest floor that applies to the phases you intend to run. If you are on AI Gateway Cloud, phase 3 does not apply and the 2.5.0 floor is irrelevant.
This decision changes which phases you run, so make it first rather than discovering it in phase 5.
- Attribute to a person — a human is signed in and the agent acts on their behalf. Run every phase. The token must carry that person's email, and that person must already be a workspace member.
- Attribute to an application — the agent runs unattended on a schedule or a queue. Skip the JWT path entirely and issue the agent its own workspace service key, naming the agent in request metadata. Phases 1 and 2 are then unnecessary.
- Attribute to a named service account — you want the agent to appear in the workspace member list. Run phase 1, then issue a User API key against the provisioned user rather than using JWTs.
A client-credentials token identifies a service principal. Building a JWT flow for an agent that no human is behind produces a token the gateway validates happily and then cannot match to anyone, which is more configuration than a service key for a worse result. Do not pretend there is a person when there is not one.
Verification: write down which of the three you chose and which phases it implies. If the answer is the second one, stop here and follow the service key path in the LLM API Key Management guide instead.
1. Sync the Directory into Workspaces
This phase builds the list that phase 5 compares the token against. It runs on the control plane, on a background schedule, and is never consulted during a request. Skip it and every JWT in phase 4 will validate correctly and attribute to nobody.
- In Strata Cloud Manager, open Settings → Identity Services → Cloud Identity Engine.
- Add a directory and choose Azure AD as the directory type. Okta and on-premises Active Directory are the other two options and behave identically from the gateway's point of view.
- Authorise the directory against your Entra tenant. This grants Cloud Identity Engine read access to users and groups.
- Set the User Identity Attribute. The choice is between UPN and Mail.
Many tenants carry a UPN of the form user@tenant.onmicrosoft.com while the address people actually use is different. The attribute you select here becomes the key the gateway matches tokens against in phase 5, so selecting UPN in that situation guarantees that every match fails. Check a sample user in Entra before choosing.
Verification: open the directory's user list in Cloud Identity Engine and confirm that the identity column shows the address you expect for several users, including at least one who was created recently. A user whose chosen attribute is empty is skipped during sync without raising an error, so a shorter list than you expected is a signal rather than a coincidence.
- In the AI Gateway, open Admin Settings → Organisation → Directory Sync.
- Select the Cloud Identity Engine groups you want to provision from.
- Map each group to exactly one workspace. The relationship is one group to one workspace, not many to many.
- Assign the workspace role that members of that group should inherit.
Background sync then provisions new members as they appear in the directory and deprovisions them when they leave. Nothing in this step runs per request.
Verification: open the target workspace's member list and confirm that users from the mapped group appear, each with the role you assigned. Then remove a test user from the group in Entra, wait for the next sync, and confirm they disappear from the workspace. Deprovisioning is the half of this that nobody tests and the half that matters at audit time.
2. Issue and Trust the Token
Two separate configurations meet here. Entra needs something for the agent to authenticate as, and the gateway needs to be told which signing keys to trust. Neither one knows about the other until a request arrives.
- Create an app registration in the Entra tenant for the agent.
- Choose the grant that matches the attribution decision from P.2. Client credentials for an unattended agent. On-behalf-of when the agent acts for a signed-in person and the token must carry that person's identity.
- Define an audience the gateway's tokens will carry, for example
api://ai-gateway. - Note the tenant ID. It forms part of the JWKS URL in the next step.
Verification: acquire a token with the grant you configured and decode it. Confirm the header carries alg: RS256, typ: JWT and a kid, and that the payload carries iss, aud, sub and exp. If you chose on-behalf-of, confirm an email claim is present. If you chose client credentials, it will not be, and that is expected.
- In the AI Gateway, open Admin Settings → Organisation → Authentication.
- Paste either a JWKS URL or the static JWKS JSON. For Entra the URL is the tenant discovery endpoint.
- If more than one issuer needs to be trusted, supply a comma-separated list of URLs. The keys from all of them are merged into one set.
https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys
A JWKS holds public keys. The private key is used for signing and must never be pasted into this field. If you are supplying static JSON rather than a URL, confirm it validates as JSON and that it wraps the keys in a keys array.
Verification: fetch the JWKS URL from a host on the same network path the gateway uses, not from your laptop. The gateway has to be able to reach it, and an egress policy that blocks it produces a signing-key error that looks nothing like a connectivity problem. JWKS fetches honour HTTPS_PROXY and TLS_CA, so a proxied environment can be made to work rather than needing an exception.
3. Choose Where to Validate
Token validation happens on the management plane by default. Hybrid and air-gapped deployments can move it to the data plane, which removes a round trip from every call and keeps authentication working through a control-plane outage.
On AI Gateway Cloud the management plane validates every token. There is no local validation option and no environment variable to set. Phase 2 is the whole of the configuration, and you can move straight to phase 4.
Verification: confirm your deployment is Cloud rather than hybrid. If you administer the gateway containers yourself, it is not Cloud, and step 3.2 applies.
Set the following on the gateway data plane. The feature requires gateway 2.5.0 or higher, and air-gapped deployments additionally require backend v1.13.0 or higher.
JWT_ENABLED=ON
ORGANISATIONS_TO_SYNC=<your-org-id>
PORTKEY_CLIENT_AUTH=<client-auth-token>
JWT_LOCAL_AUTH_DEFAULT_SCOPES=completions.write,mcp.invoke
| Variable | Required | What it does |
|---|---|---|
JWT_ENABLED | Yes | Turns on local verification. Left off, JWT-shaped tokens are sent to the management plane as before. |
ORGANISATIONS_TO_SYNC | Yes | Supplies the organisation when the token carries no organisation claim, which is the normal case with a stock Entra token. |
PORTKEY_CLIENT_AUTH | Yes | Authenticates the data plane to the control plane so it can pull the configuration it needs. |
JWT_LOCAL_AUTH_DEFAULT_SCOPES | Usually | Supplies scopes the IdP cannot issue. Entra generally cannot issue completions.write, so without this a valid token is still refused. |
Verification: restart the gateway and send one authenticated call. Then make the management plane unreachable, from the gateway's network rather than by deleting anything, and send the same call again. If local validation is active the second call succeeds. If it fails with an authentication error, JWT_ENABLED did not take effect.
4. Send the Agent Call
The token goes in a header, and the gateway then runs four resolutions in order before it looks at identity at all.
Two headers are accepted. Use either one, not both.
# Option 1: the raw token, with no prefix
curl https://api.portkey.ai/v1/chat/completions \
-H "x-portkey-api-key: $JWT" \
-H "Content-Type: application/json" \
-d '{"model":"@azure-foundry/gpt-4o","messages":[{"role":"user","content":"ping"}]}'
# Option 2: the standard Authorization header, with the Bearer prefix
curl https://api.portkey.ai/v1/chat/completions \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"model":"@azure-foundry/gpt-4o","messages":[{"role":"user","content":"ping"}]}'
The x-portkey-api-key header takes the raw token with nothing in front of it. Prefixing it with the word Bearer is the most common setup failure on this path, and it returns an error that reads like a bad API key rather than a malformed header.
Verification: the call returns a completion. If it returns an authentication error, decode the token and check its expiry before changing anything else, because an expired token and a malformed header produce the same message.
A successful call ran four resolutions you did not see. Knowing their order turns a vague authentication failure into a specific one.
| Order | Resolution | From the token | Fallback |
|---|---|---|---|
| 1 | Signature | kid resolved against the JWKS, alg must be RS256, RSA key 2048-bit or larger, exp in the future | None. This one either passes or the call is refused. |
| 2 | Organisation | portkey_oid or organisation_id | ORGANISATIONS_TO_SYNC |
| 3 | Workspace | portkey_workspace or workspace_slug | First entry in the workspace allowlist, then the organisation default workspace |
| 4 | Scopes | scope as a space-separated string, or scopes as an array | JWT_LOCAL_AUTH_DEFAULT_SCOPES |
Small clock skew is tolerated on exp. The iat and nbf claims are recommended rather than required. The portkey. prefix on a scope is optional, so completions.write and portkey.completions.write are both accepted.
Verification: deliberately request a token without the scope your endpoint needs and send the same call. It should be refused even though the signature is valid, which confirms that scope resolution is running rather than being skipped. Restore the scope afterwards.
5. Confirm Attribution
Everything so far decided whether the call is allowed. This phase decides whose name appears next to it, and it turns on a single string comparison.
The gateway takes the first of these claims that is present, in this order, and uses it as the user identity for logging and metrics.
| Precedence | Claim | Typical content |
|---|---|---|
| 1 | email_id | The person's email address |
| 2 | sub | A subject identifier, which for a client-credentials token is a service principal object ID |
| 3 | uid | A user identifier, where the issuer supplies one |
Verification: decode the token your agent actually sends and find the first of those three claims it carries. That value, and nothing else, is what the next step compares.
If the token's email matches an AI Gateway user in the target workspace, the request is attributed to that person and inherits their workspace role. Logs, metrics and per-user budget policies all then resolve to a human being.
If there is no match, the request falls back to a generic workspace service key. The call still succeeds and no error is returned. The only symptom is attribution that quietly stops naming anyone, which is why this breaks in production months after it was configured, when somebody changes a UPN or a group membership.
Verification: send a call with a token carrying a known workspace member's email, then open the gateway logs and confirm the entry names that person. Then send a call with a token carrying an address that is deliberately not a member, and confirm the entry attributes to the workspace service key rather than failing. Seeing both outcomes is how you know the comparison is live rather than coincidental.
A client-credentials token carries a service principal rather than a person, so precedence falls through to sub. There is no email to compare, so the comparison in 5.2 cannot match a workspace member.
The product documentation states the claim precedence and it states that an email match attributes to the matched user. It does not spell out the client-credentials case. The conclusion above follows from those two statements rather than being written down anywhere, so verify it in your tenant before building a design on it.
Where attribution of an unattended agent matters, send metadata alongside the call. The _user key inside x-portkey-metadata is reserved, and from version 1.17.0 onward metadata._user can be used as a match key for budget and rate-limit policies.
-H 'x-portkey-metadata: {"_user":"invoice-reconciliation-agent","team":"finance"}'
Verification: send the call with the metadata header and confirm the log entry carries the value you set. Then build a budget policy keyed on metadata._user and confirm it matches that agent and not others.
6. Configure Egress
The gateway now has to reach the model. It does so on a credential of its own, which has nothing to do with the credential the caller presented.
In the gateway's provider configuration for Azure AI Foundry, supply one of:
- API key — the simplest option, and the one that requires no Entra configuration at all
- Entra service principal — a client ID and secret or certificate, scoped to the Foundry resource
- Managed identity — available when the gateway runs on Azure compute, and the option that avoids holding a secret
The agent's token never leaves the gateway. Azure AI Foundry sees the gateway's own identity on every call, not the agent's and not the person's. Any per-user accounting you need has to come from the gateway's logs, because the provider cannot supply it.
Verification: make a call through the gateway, then check the Azure activity or diagnostic log on the Foundry resource. The caller recorded there should be the gateway's service principal or managed identity. If you expected to see the end user there, this is the step that explains why you do not.
Choosing the Credential
Four credential types reach the gateway, and each one attributes differently. The choice is worth making deliberately, because changing it later means changing the agent's code.
| Credential | Attributes to | Requires directory sync | Best for |
|---|---|---|---|
| Workspace service key | The application | No | Unattended agents, batch jobs, anything with no human behind it |
| User API key | The individual, automatically | Yes, the user must be provisioned | A named service account you want visible in the member list |
| IdP JWT with a matching email | The matched workspace user | Yes | An agent acting on behalf of a signed-in person |
Any of the above plus x-portkey-metadata | Whatever the application sends in _user | No | Splitting one shared key across several logical callers |
The user picker that issues a User API key only lists people the directory has already synced into that workspace, which is why that row depends on phase 1 even though it does not use a token.
Claim Reference
How much the gateway can read out of a token depends on whether you control the issuer. The product documentation splits this into two integration modes and names Entra as a case of the second, alongside Okta, Auth0 and Cognito.
When the token is minted by something you own, gateway-specific claims can be added to it and everything travels in the token.
{
"sub": "agent-7",
"email_id": "ana@corp.example",
"portkey_oid": "org_abc123",
"portkey_workspace": "ws_ai_platform",
"scope": "completions.write mcp.invoke",
"usage_limits": { "credit_limit": 1000 },
"rate_limits": { "requests_per_minute": 60 },
"defaults": { "metadata": { "team": "finance" } },
"exp": 1767225600
}
Nothing has to be configured on the gateway side beyond the JWKS, because organisation, workspace, scopes and limits all arrive with the request.
A token from a commercial IdP carries a schema you cannot extend. It can be as thin as this and still authenticate.
{
"iss": "https://login.microsoftonline.com/<tenant-id>/v2.0",
"aud": "api://ai-gateway",
"sub": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"exp": 1767225600
}
Everything the token cannot carry has to be supplied somewhere else.
| What the token lacks | Where it comes from instead |
|---|---|
| Organisation | ORGANISATIONS_TO_SYNC on the gateway |
| Workspace | Deployment settings, or the organisation default workspace |
| Scopes | The IdP registration if it can issue them, otherwise JWT_LOCAL_AUTH_DEFAULT_SCOPES |
| Usage and rate limits | Configured on the workspace, or applied through workspace policies keyed off request metadata |
What This Does Not Do
Four things are commonly assumed to work on this path and do not. Each one is worth knowing before a design depends on it.
A Cloud Identity Engine group maps to a workspace, and membership of that workspace carries a workspace role. The group itself does not travel with the request and cannot be matched in a policy the way a firewall rule matches a User-ID group. If you need per-group differentiation, it has to be expressed as separate workspaces, or as metadata the application sends.
Phase 6 covers this. The provider sees the gateway. Per-user accounting against the provider's own bill is not available, and the gateway's logs are the only place that attribution exists.
The MCP Gateway can forward caller identity to an MCP server three ways: a claims header, passing the original bearer token straight through, or a gateway-signed JWT in a header you name. The Agent Gateway has no equivalent. A client authenticates to it with a gateway API key carrying the Agent Gateway toggle and the agents.invoke scope, and access control works per agent and per skill. Access control for specific users, skill control within a workspace, and skill control for an individual user are all listed as coming soon.
An A2A call can be attributed at the gateway, but the agent it invokes receives no identity from the caller. A design that assumes the downstream agent can make an authorisation decision about the original human will not work today. See the Agent Gateway phase of the streaming and A2A guide for the surrounding mechanics.
Step 5.3 covers this. A client-credentials token has no email, so no amount of directory configuration will make it attribute to a human. Use a service key and metadata instead.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Invalid API key, with a valid-looking token | The token is signed with HS256 | Only RS256 is accepted. Reconfigure the issuer. The error message does not mention the algorithm. |
Error Verifying JWT token: Signing Key Not Found | The kid is missing, or it does not resolve to a key in the JWKS | Confirm the token header carries a kid and that the configured JWKS contains that key. |
| Authentication fails only on older gateway builds | Entra omits alg on its JWKS keys | Upgrade to 2.1.0 or higher, which falls back to RS256 when alg is absent. |
| Invalid API key, intermittently | The token expired, or clock skew exceeded the tolerance | Shorten the agent's token cache lifetime and confirm time sync on the client. |
| Every token fails after a working period | The JWKS URL became unreachable from the gateway | Fetch it from the gateway's own network path. Set HTTPS_PROXY or TLS_CA if the environment requires them. |
| Token is valid but the call is refused | The required scope is absent | Chat completions need completions.write. Add it at the IdP, or set JWT_LOCAL_AUTH_DEFAULT_SCOPES. |
Every invalid-JWT error is written to the audit log, with the token redacted to its first and last few characters. Start there rather than from the client-side error, because the audit entry names the actual reason.
| Symptom | Cause | Fix |
|---|---|---|
| Calls succeed but attribute to a service key | The token's email does not match a workspace member | Compare the token's email_id against the workspace member list character for character. Check for a UPN rather than a mail address. |
| Some users attribute, others do not | The non-matching users were skipped during directory sync | A user whose selected identity attribute is empty is skipped silently. Populate the attribute in Entra and wait for the next sync. |
| Attribution worked and then stopped for one person | Their UPN or mail attribute changed in the directory | Confirm the current value in Entra, and confirm the sync has picked it up in the workspace member list. |
| Attribution is empty rather than wrong | The token carries none of email_id, sub or uid | Add a claim the gateway can read, or move the agent to a service key with metadata. |
| A departed employee still appears | Deprovisioning has not run, or the group mapping was removed rather than the user | Removing the group mapping stops future syncs without removing existing members. Remove the user from the group instead. |
Reference URLs
The canonical pages behind this guide, grouped by what they cover. Palo Alto Networks pages are authoritative for Cloud Identity Engine. Portkey pages are authoritative for the gateway itself, which is the upstream product behind Prisma AIRS AI Gateway.
| Page | Covers |
|---|---|
| JWT authentication | The primary source. Header and claim requirements, the JWKS configuration path, integration modes A and B, gateway-local validation, workspace resolution, scopes, and the troubleshooting list. |
| API keys, authentication and authorisation | Workspace service keys, User API keys, and the scopes each key type can carry. |
| Inference API authentication | Which headers the inference endpoints accept, and in what form. |
| Inference API headers | The x-portkey-metadata header and the reserved _user, _prompt, _organisation and _environment keys. |
| Metadata | How metadata reaches logs, and which keys are special. |
| Budget policies | Using metadata._user as a policy match key, available from version 1.17.0. |
| Page | Covers |
|---|---|
| Cloud Identity Engine Directory Sync | The gateway side of phase 1. Group to workspace mapping, the identity attribute, and the provisioning lifecycle. |
| SCIM provisioning | The alternative for tenants that are not on Strata Cloud Manager. |
| Cloud Identity Engine overview | What Cloud Identity Engine is, and how it sits relative to the rest of the platform. |
| Configure a cloud-based directory | The Azure AD directory type, and the authorisation flow against the tenant. |
| Cloud Identity Engine attributes | The attribute set available for the User Identity Attribute choice in step 1.1. |
| Configure Azure as an IdP | The Entra side of the directory authorisation. |
| Cloud Identity Engine troubleshooting checklist | First stop when users do not appear after a sync. |
| Page | Covers |
|---|---|
| Hybrid deployment architecture | The control plane and data plane split that phase 3 depends on. |
| Azure integration | Fronting Azure AI Foundry, and the credential options in phase 6. |
| MCP Gateway identity forwarding | The three forwarding methods the MCP Gateway has and the Agent Gateway does not. |
| Agent Registry | Agent Gateway access control, and the coming-soon list quoted in the limits section. |
| Enterprise changelog | Every version floor cited in the prerequisites, including 2.1.0, 2.5.0 and 2.21.0. |
Sources
Everything in this guide is drawn from the product documentation listed above, as mirrored locally on 21 September 2026, together with the Palo Alto Networks Cloud Identity Engine documentation. One statement is an inference rather than a quotation and is marked as such where it appears.
| Section | Source | What it supplies |
|---|---|---|
| Phase 1 | Cloud Identity Engine Directory Sync, plus the PAN Cloud Identity Engine pages | Directory types, the identity attribute choice, one-to-one group mapping, the provisioning lifecycle, and the silent skip of users with an empty attribute. |
| Phase 2 | JWT authentication | The Admin Settings path, the comma-separated JWKS list, RS256, typ, kid, the 2048-bit floor, and the public-key-only warning. |
| Phase 3 | JWT authentication, gateway-local section, plus the enterprise changelog | The JWT_ENABLED behaviour, the four environment variables, and the 2.5.0 and v1.13.0 floors. |
| Phase 4 | JWT authentication, plus the inference API header reference | Both accepted headers, the Bearer prefix warning, workspace resolution order, and scope handling. |
| Phase 5 | JWT authentication, the enterprise changelog at v2.21.0, and budget policies | The email_id to sub to uid precedence, email-based attribution to a workspace user, and metadata._user as a policy key. |
| Phase 6 | Azure integration | The provider credential options for Azure AI Foundry. |
| Limits | MCP Gateway identity forwarding, and the Agent Registry page | The three MCP forwarding methods, the agents.invoke scope, and the coming-soon access control list. |
The coming-soon list on the Agent Registry is designed to change, so confirm it is still accurate before quoting it. The client-credentials attribution behaviour in step 5.3 is derived from two documented statements rather than being stated directly, so verify it against the customer's own tenant before it becomes a design assumption.