OAuth 2.0 On-Behalf-Of Flow: Delegating User Access Across APIs
Modern applications rarely stop at a single backend. A client might call one API, which then needs to call Microsoft Graph, another internal service, or an agent tool while preserving the signed-in user's identity.
The OAuth 2.0 On-Behalf-Of flow, usually shortened to OBO, solves this middle-tier delegation problem. Instead of forwarding the client's access token to every downstream service, the middle-tier API exchanges it for a new token issued specifically for the next API.

The problem OBO solves
Consider this request chain:
Signed-in user → Client application → API A → API B
The client already has an access token for API A. However, API A now needs to call API B using the user's delegated permissions.
API A must not simply forward the original token:
- Token A was issued with API A as its audience.
- API B should reject a token intended for another service.
- Relaying the same bearer token through multiple services expands the impact of accidental disclosure.
- API B needs a token containing the scopes and claims relevant to API B.
OBO lets API A exchange Token A for Token B without asking the user to sign in again.
The simple mental model
Think of OBO as re-minting, not forwarding:
| Token | Intended audience | Where it is used |
|---|---|---|
| Token A | API A | Client → API A |
| Token B | API B | API A → API B |
Both tokens represent the same signed-in user, but each token is audience-bound and suitable for a different API.
How the flow works
1. The client obtains Token A
The user signs in through an interactive OAuth flow, normally Authorization Code with PKCE. The identity provider issues the client an access token whose aud claim identifies API A.
Token A might conceptually contain:
{
"aud": "api://api-a",
"sub": "signed-in-user",
"scp": "access_as_user"
}
The exact token contents are controlled by the identity platform. Applications should validate tokens rather than depend on an assumed JWT layout.
2. The client calls API A
The client sends Token A in the request:
GET /work
Host: api-a.example.com
Authorization: Bearer <token-a>
API A validates the token's signature, issuer, audience, lifetime, tenant, and required scopes.
3. API A requests an OBO exchange
API A sends Token A to the Microsoft Entra ID token endpoint as an assertion. It also authenticates itself as a confidential client and asks for the downstream API's configured delegated permissions.
A simplified request looks like this:
POST https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id=<api-a-client-id>
&client_secret=<api-a-secret>
&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=<token-a>
&requested_token_use=on_behalf_of
&scope=api://api-b/.default
Production systems should avoid manually constructing this exchange when a supported authentication library such as MSAL is available. For API A's own authentication, a certificate or managed identity is generally preferable to a long-lived shared secret when the deployment platform supports it.
4. Entra ID issues Token B
The identity platform validates:
- Token A and its user identity
- API A's confidential-client authentication
- Existing user or administrator consent
- The delegated permissions configured for API B
- Applicable tenant and Conditional Access policies
If the request is allowed, Entra ID issues Token B with API B as its audience.
{
"aud": "api://api-b",
"sub": "signed-in-user",
"scp": "data.read"
}
API A does not gain unrestricted access to API B. The resulting authority remains bounded by the user's delegated permissions, granted consent, application configuration, and API B's own authorization policy.
5. API A calls API B
API A uses Token B only for API B:
GET /data
Host: api-b.example.com
Authorization: Bearer <token-b>
API B validates Token B and returns the requested data to API A. API A then returns an appropriate result to the client. Token B stays inside the trusted server tier and is not returned to the browser, mobile application, or model runtime.
What OBO proves
An OBO exchange combines two pieces of evidence:
- User delegation: Token A represents the signed-in user and the access already granted to API A.
- Middle-tier identity: API A authenticates itself when requesting a downstream token.
This allows the identity provider to issue a resource-specific token while retaining user context. It does not turn delegated access into application-level privilege.
Security rules worth keeping
Validate the incoming token before exchanging it
API A should verify that Token A is valid and was actually issued for API A. An unvalidated bearer token must not be treated as proof of identity.
Never send a token to the wrong audience
Token A belongs to API A. Token B belongs to API B. A token should only be sent to the resource named by its audience.
Keep the exchange server-side
OBO requires a confidential middle tier. Client credentials, certificate keys, downstream tokens, and token caches should remain outside browsers, mobile clients, model prompts, logs, and shared context stores.
Request least privilege
Configure only the delegated permissions API A genuinely needs when calling API B. API B must still authorize each operation based on its own policy and the claims in Token B.
Treat tokens as secrets
Do not place access tokens in URLs, telemetry, exception messages, agent conversations, or persisted context. Redact authorization headers before logging requests.
Plan for claims challenges
Conditional Access can require MFA, device compliance, sign-in frequency, or another step-up interaction. Because the middle tier cannot show a consent or sign-in screen directly, it may need to return a claims challenge to the client so the client can complete the interactive requirement.
OBO in an MCP or multi-agent architecture
For a network of MCP servers and local models, a useful trust boundary looks like this:
User client
│ Token A
▼
Trusted gateway or MCP server
│ OBO exchange
▼
Target-specific Token B
│
▼
Downstream API
The trusted gateway should own:
- Incoming token validation
- OBO exchanges
- Confidential-client credentials
- Token caching and expiry handling
- Scope and audience enforcement
- Audit records that do not contain raw tokens
The model or worker machine should receive only the minimum task data and authorization result it needs. It should not receive a reusable bearer token merely because it is participating in the request.
If another trusted service must make the next delegated call, it should receive a token intended for that service and perform its own controlled exchange for the next resource. Avoid building long, unconstrained bearer-token chains across model hosts.
When not to use OBO
OBO is designed for calls made on behalf of a signed-in user. It is not the right flow when:
- A daemon or scheduled job acts only as itself.
- There is no signed-in user context.
- The downstream action should use application permissions.
- A browser or public client is trying to hold middle-tier credentials.
For application-only service access, use the OAuth 2.0 client credentials flow instead.
Summary
The On-Behalf-Of flow preserves user delegation across an API boundary without reusing one token everywhere:
Token A: user → API A
Token B: same user, API A → API B
The middle-tier API validates the incoming token, authenticates itself, exchanges that token for a downstream audience, and keeps the resulting token server-side. The result is a cleaner security boundary, narrower token exposure, and authorization that remains tied to both the user and the intended resource.
References
- Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow
- Scopes and permissions in the Microsoft identity platform
react & discuss
EOF · cd ~