Skip to main content

Agent Access on Behalf of Users

An agent can hold an access token that represents a user instead of itself. The token names the user in the sub claim, and on the sign-in, exchange, and ID-JAG redemption paths it also names the agent in the act (actor) claim, so a resource sees both the user who authorized the action and the agent that carried it out. ThunderID issues these tokens once the agent is extended to act on behalf of a user.

Select the path based on whether the user can sign in now, must approve on another device, or already has a token. These paths run after agent onboarding.

Extend the agent to act for a user​

An agent created through an OBO onboarding flow already has delegated mode and the administrator-selected redirect URIs, login flow, and user type. Onboarding is an Administration flow; the user sign-in flow runs later when a user accesses the agent.

To extend an existing own-identity agent, configure it in the Console as follows:

  1. On the Advanced tab, under Operating mode, turn on the Delegated mode toggle. The agent keeps acting on its own (client_credentials) and gains the ability to act for a user. Turning the toggle on adds the authorization code (authorization_code with PKCE) and refresh token grants by default.
  2. Still on the Advanced tab, add the Authorized redirect URIs for the sign-in callback, and set Allowed user types to the user types allowed to register through the agent.
  3. On the Flows tab, pick the Authentication and Registration flows that decide how users authenticate (enterprise SSO, social login, passkeys, OTP, and where step-up applies). The tab applies only when the agent signs users in with the authorization code or CIBA grant.
note

Flows are designed in the flow builder, not here. See Login and Registration Flows to build them; this page only selects which flow the agent uses.

Through the API, send delegated: true on the agent and add the grants for the method you use. The flag is not stored, so send it on every update. With delegated: true and any grant beyond client_credentials, the agent must hold at least one grant that lets it act for a user: authorization_code, urn:openid:params:grant-type:ciba, urn:ietf:params:oauth:grant-type:token-exchange, or urn:ietf:params:oauth:grant-type:jwt-bearer. With delegated: true and no grant beyond client_credentials, ThunderID adds authorization_code and refresh_token. The methods below cover the authorization code, CIBA, and token exchange grants. To let the agent redeem an ID-JAG from an external identity provider with the jwt-bearer grant, see Accept Identity Assertions from an External IdP.

Choose what the user token carries​

Once the agent is extended to act for users, the User token becomes editable on the Tokens tab (under Issued to, alongside the Agent token). Open it to choose which of the user's attributes the access token carries and to set the validity periods for the access, ID, and refresh tokens. The agent's actor (act) claim is shown here too. The access token validity you set here also applies to the access tokens the agent receives by redeeming an ID-JAG with the JWT bearer grant. Through the API, configure these in the agent's OAuth token settings; see the Agents API Reference.

All of these settings appear when the agent signs users in with the authorization code or CIBA grant. Token exchange and JWT bearer issue only an access token, so an agent whose delegated grants are only token exchange, JWT bearer, or both sees the access token validity and the scope mapping, plus, with token exchange, the attributes to keep from the incoming token. Tokens from a redeemed ID-JAG carry no user attributes.

Get a token for the user​

An agent gets a token that represents the user in one of three ways: by signing a present user in, by asking an absent user to approve out of band, or by exchanging a user token that already exists. Choose the method that fits. An agent can also receive one by redeeming an ID-JAG from an external identity provider; see Use identity assertions.

The agent has no token for the user yet, so it signs the user in through the flow configured for the agent and receives a token that names the user as sub and the agent as act. Extending the agent already gave it the ability to sign a user in (authorization_code with PKCE), so there is no extra grant to enable.

The user signs in through the agent's Authentication flow, and ThunderID returns the token to the redirect URI. Nothing more to configure here.

The issued token names the user as sub and the agent as act. The act claim appears on the access token only, never the ID token.

json
{
"sub": "<user-id>",
"act": { "sub": "<agent-id>" }
}

Refresh the token​

An agent that acts for a user can also renew that access without a new sign-in, by keeping a refresh token. Enable the refresh grant like the other delegated grants:

  • Console: on the Advanced tab, with the agent extended to act for a user, add the refresh grant (refresh_token) in Grant types.
  • API: add refresh_token to the agent's grantTypes.

The exchange call, and the rules for narrowing scope and preserving the resource binding, are the same as for any client. See Refresh Token for the request and its rules. The reissued token keeps the agent as the actor.

Use identity assertions​

An agent that acts for a user can also take part in an Identity Assertion JWT Authorization Grant (ID-JAG) exchange. To let the agent obtain an ID-JAG for the signed-in user that an external service accepts, see Issue Identity Assertions (ID-JAG). To let the agent redeem an ID-JAG issued by an external identity provider with the JWT bearer grant, see Accept Identity Assertions from an External IdP.

What the token carries​

The token an agent holds for a user is a signed JWT (typ: at+jwt). Three claims identify who is involved, and it helps to keep them apart:

  • sub is the principal the token represents. In a delegated token this is the user.
  • act is the actor that exercises the token: the agent acting for the user, identified by the agent's resource ID (not its client ID). It nests further actors when an agent delegates to another agent.
  • client_id is the OAuth client that authenticated to obtain the token, which is always the agent.

A resource reads sub to apply the user's permissions and act to see which agent carried the request. The full claim set:

ClaimValue
subThe user's subject identifier. For an access token from redeeming an ID-JAG with the JWT bearer grant, the subject of the external identity provider's assertion, not a local user ID.
issThe ThunderID instance URL.
idpPresent only on an access token from redeeming an ID-JAG with the JWT bearer grant: the issuer of the assertion, which identifies the external identity provider that sub belongs to.
audThe resolved resource server, or the agent's default audience (its client ID when none is set) when the request carries no permission scopes or resource.
scopeA subset of the user's scopes, never more than the user's own token held. For a JWT bearer token, bounded by the assertion's scope claim and narrowed by the request's scope.
client_idThe agent's client ID.
actThe agent acting for the user, on the access token only and never the ID token. It is appended automatically on the authorization_code, CIBA, refresh_token, and JWT bearer grants. On a token exchange it names the actor_token's subject, or the agent itself when the request has no actor_token. Earlier actors in a subject_token that ThunderID issued stay nested beneath it. See The nested act claim.
exp, iat, nbf, jtiStandard JWT lifetime and identity claims.

Troubleshooting​

The common token-endpoint errors, invalid_client, unauthorized_client, invalid_target, and a 403 from the resource, mean the same here as for the agent token; see Troubleshooting. The errors specific to acting for a user are:

ErrorLikely cause
invalid_requestOn a token exchange, the subject_token is missing or its subject_token_type is unsupported. Pass the user's token as subject_token with subject_token_type set to urn:ietf:params:oauth:token-type:jwt.
invalid_scopeOn a refresh, a requested scope is broader than the original grant. On a token exchange, scopes were requested but the subject token carries none; scopes the subject token does not hold are otherwise dropped, not rejected.
authorization_pending, slow_down, expired_token, access_deniedBackchannel (CIBA) polling outcomes while waiting for the user to approve. See Backchannel Authentication (CIBA).

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.