Agent identities downstream
Give each agent its own account in ServiceNow, Jira, M365 or SAP, publish agents into your Entra or Okta directory, and use Cross-App Access (ID-JAG) in both directions.
- Give an agent its own account on a connected system
- Provision that account through the system's API
- Require agents to call as themselves
- Publish an agent to Entra Agent ID or Okta
- Trust an IdP's ID-JAGs for Liyagent's MCP endpoint
- Reach another app through Cross-App Access
By default, an agent running as itself calls a connected system with the server's shared login. The far end then sees one integration user for every agent. On this page you give each agent an identity of its own, both in the systems it calls and in your identity provider.
Nothing here changes an existing flow until you set it up. An agent with no account of its own keeps using the shared login. An agent running for a person still sends that person's token.
An agent's own account on a system
An account belongs to one agent on one MCP server (PUT /api/agent-accounts/{agent}/{server}, needs access.write):
{"system": "servicenow", "external_user": "liyagent.triage",
"auth": {"mode": "static_bearer", "secret_value": "..."}}systemis one ofservicenow,jira,m365,saporother.authtakes the same modes as a server's own credential:static_bearer,static_headerorclient_credentials. A pastedsecret_valueis filed in the Secrets Store, and the account keeps only itssecret://reference.- The server must be a remote (HTTP) server with a shared credential. A server bound to each person's own connection never sends an agent account.
When the agent runs as itself, its calls to that server carry its own credential. Its tool listing is cached separately from the shared login's. Each save, rotation and delete is audited as agent.account.create, agent.account.rotate and agent.account.delete.
Provision the account through the system's API
POST /api/agent-accounts/{agent}/{server}/provision creates the agent's user in the system, using an administrator credential you name by secret:// reference:
kind | What is created | Credential |
|---|---|---|
servicenow | A web-service-only sys_user (Table API) | A generated password, filed in the Secrets Store and used at once |
jira | A Jira Cloud user (/rest/api/3/user) | None: the account waits until you save an API token |
m365 | A Microsoft Graph user (/v1.0/users), with a licence when you give license_sku | None: use Entra Agent ID below, or save one |
scim | A SCIM 2.0 user (/Users): SAP Cloud Identity and any SCIM directory | None: the account waits until you save one |
An account waiting for its credential is never sent, so the agent keeps the shared login until you save one. Each provisioning is audited as agent.account.provision, and a refused one as agent.account.provision_failed.
Require agents to call as themselves
PUT /api/agent-accounts/settings with {"require_own_account": true} refuses an agent's call to any server where it holds no enabled account of its own, instead of sending the shared login. Each refusal is audited as agent.account.refused. It is off by default, because turning it on stops every agent that has no account yet.
Publish agents to your identity provider
Configure the directory once (PUT /api/agent-idp/settings), then publish an agent (POST /api/agent-idp/{agent}/publish) or withdraw it (DELETE /api/agent-idp/{agent}).
- Microsoft Entra Agent ID. Set
provider: "entra", yourtenant_id, and your agent identity blueprint'sblueprint_client_idandblueprint_secret_ref. Optionally addsponsor_ids, the Entra object ids of the people who answer for the agents. Publishing creates an agent identity under the blueprint (POST /beta/servicePrincipals/Microsoft.Graph.AgentIdentity). - Okta. Set
provider: "okta", yourorg_urland anapi_token_ref. Publishing creates an OIDC service application for the agent: client credentials only, authenticated withprivate_key_jwtagainst Liyagent's published keys, so no client secret is ever minted.
Publishing is off (provider: "none") until you configure it, and only an operator's call publishes. Each publish, withdrawal and failure is audited as agent.idp.publish, agent.idp.withdraw and agent.idp.publish_failed.
Cross-App Access (ID-JAG)
Cross-App Access lets the enterprise IdP, not each app's consent screen, decide whether one app may reach another for a person. It is the Identity Assertion JWT Authorization Grant (ID-JAG), and Liyagent supports both sides of it.
Liyagent reaching another app
Add a connection provider in mode obo with profile: "id_jag":
{"subject_source": "sso:okta", "profile": "id_jag",
"audience": "https://wiki-as.example.com",
"resource": "https://wiki.example.com/mcp",
"resource_as_url": "https://wiki-as.example.com/oauth/token",
"resource_client_id": "liyagent", "resource_client_secret": "secret://wiki-client"}On each call, Liyagent first trades the person's sign-in ID token at their IdP for an ID-JAG addressed to the app's authorization server (RFC 8693, requested_token_type urn:ietf:params:oauth:token-type:id-jag). It then redeems the ID-JAG there for an access token (RFC 7523 JWT bearer). When every provider that uses a sign-in source is an id_jag one, single sign-on keeps the ID token for it rather than the access token. A person signed in before that is asked to sign in again.
Other apps reaching Liyagent
Trust an IdP's ID-JAGs with PUT /api/xaa/issuers/{name}:
{"issuer": "https://acme.okta.com", "jwks_uri": "https://acme.okta.com/oauth2/v1/keys",
"clients": ["claude-enterprise"], "user_claim": "email"}A confidential client named in clients can then post grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer with the ID-JAG as assertion to /oauth/token. Liyagent checks the following before it issues a /mcp token for the person:
typisoauth-id-jag+jwt.- The signature verifies against the issuer's keys.
audis Liyagent's authorization server.client_idis the authenticated client.- The assertion is unexpired, lives at most an hour, and has not been used before.
user_claimnames an enabled account.
With no trusted issuer, there is no grant. Each token issued and each refusal is audited as oauth.server.token or oauth.server.token.refused.