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 the agent in the act (actor) claim, so any resource that receives it 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.

Extend the agent to act for a user

By default an agent acts only on its own. Extending it lets the same agent also act for a signed-in user, without giving up acting on its own. In the Console, extend the agent 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 sign a user in and act for them (authorization_code with PKCE).
  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).
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 there is no single switch; each method below adds the grant it needs.

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. Through the API, configure these in the agent's OAuth token settings; see the Agents API Reference.

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.

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.

{
"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.

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.
issThe ThunderID instance URL.
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.
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 grant, and built from the actor_token on a token exchange.
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.