Agent infrastructure
Platform integration
Just-in-time tokens for AI agents: a seven-step guide to issuing short-lived, scoped credentials with token exchange, DPoP, and per-call authorisation.

By the end of this guide you'll have a working pattern for issuing just-in-time tokens for AI agents — short-lived, narrowly scoped credentials minted at the moment of a tool call and thrown away after. You'll know how to structure the token exchange, what claims to bind, how to keep the blast radius small, and how to instrument the flow so a security review actually passes.
Prerequisites: an OAuth 2.1 or OIDC-capable identity provider (Auth0, Okta, Entra ID, Keycloak, or your own), an agent runtime that can hold a user session, and a downstream API you control the auth on. Time: about an hour to prototype, a day to production-harden.
This guide assumes you've already decided not to give your agent a long-lived service account. If you're still weighing that trade-off, read our piece on agent identity vs user identity first — this article picks up where that one ends.
Start with the claims, not the crypto. A JIT token for an agent tool call needs to answer four questions at the API boundary: who is the user, what tool is being called, what scope does this specific call need, and when does the token expire.
Write the claim set down before you write any code. A minimal set looks like this:
{
"sub": "user_9f2a...",
"act": { "sub": "agent_billing_v3" },
"scope": "invoices:read invoices:refund",
"aud": "https://api.internal/billing",
"exp": 1735689600,
"jti": "01JABC...",
"tool_call_id": "tc_01JABC..."
}
The act claim (from RFC 8693) is the one most teams miss. It records that the agent is acting on behalf of the user — not impersonating them. Your audit log needs both identities. Your authorisation logic needs the user's identity. The act claim gives you both without collapsing them.
Short means short. The point of a JIT token is that if it leaks — from a log line, a prompt injection, a compromised model provider — the window of abuse is measured in seconds.
Sensible defaults:
Don't reuse tokens across tool calls. The reflex to "cache the token for this session" defeats the point. If your token exchange is slow enough that per-call issuance hurts, fix the exchange — don't lengthen the token.
Use RFC 8693 token exchange. The agent runtime holds a longer-lived session or access token, and swaps it for a JIT access token at each tool call.
The request:
POST /oauth/token HTTP/1.1
Host: auth.yourdomain.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<user_access_token>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=<agent_credential>
&actor_token_type=urn:ietf:params:oauth:token-type:jwt
&audience=https://api.internal/billing
&scope=invoices:read invoices:refund
&resource=https://api.internal/billing/invoices/inv_123
A note on portability: RFC 8693 registers several subject token types, but IdPs vary in what they'll actually accept. Keycloak's standard token exchange, for example, only accepts access_token as the subject. Check your IdP's docs before you commit to a subject token type — access_token is the safest default.
The response is a short-lived access token bound to that specific audience, scope, and — if your IdP supports it — that specific resource.
Make this call from the tool runtime, not the model. The model should never see the credential. It sees a tool call; the runtime handles the exchange and attaches the token to the outbound API request.
An unbound bearer token is a liability. Anything holding it can use it. Two techniques close that gap.
DPoP (Demonstrating Proof of Possession, RFC 9449) binds the token to a key held by the runtime. The API verifies both the token and a signed DPoP header on each request. Steal the token, you still can't use it.
Resource indicators (RFC 8707) narrow the audience. A token issued for https://api.internal/billing/invoices/inv_123 won't work against https://api.internal/billing/invoices/inv_456. If the agent's next tool call needs a different resource, it gets a different token.
If you can only implement one, pick DPoP. If you can do both, do both. This is the layer where the difference between "we use OAuth" and "we use OAuth properly" actually shows up.
A common failure: teams issue a token with scope=invoices:refund and assume the presence of that scope means the call is authorised. It doesn't. The token proves the user has permission to refund invoices. It doesn't prove this specific refund is allowed.
At the API, run three checks in order:
Step three is where most agent security incidents actually happen. The token was fine. The scope was fine. The user just didn't own the invoice they refunded. See API security best practices for AI agents for the fuller boundary model.
Every tool call must produce an audit record that captures the user (sub), the agent (act.sub), the tool, the resource, and the outcome. Two rules:
jti and tool_call_id, never the token itself. Tokens in logs are the leading cause of "how did they get in?"denied: user does not own resource inv_456 is worth a hundred 200 OKs in an incident.This is also the record that lets you answer the question every enterprise buyer will eventually ask: when your agent did the thing, was it acting as a real user with real permissions, or as a shared service account with god-mode? JIT tokens are how you get to answer "the former" honestly.
Short-lived tokens don't get refreshed — they get re-issued. But the session that authorises re-issuance does need refresh handling.
For the mechanics of rotating the signing keys underneath all of this, see webhook signing key rotation — the JWKS overlap pattern is the same.
act claim. Without it, your audit log shows the user did something the user didn't do. This is the claim security reviewers ask about specifically.Get these seven steps right and the token boundary holds. Get any of them wrong and you've built the same shared-service-account pattern with more moving parts.
Stay up to date on the ever changing agentic landscape.