API to API Authentication in Azure AD B2C and Entra

When you think of Azure Active Directory B2C or Microsoft Entra ID, the immediate picture that comes to mind is often a user signing in. There is usually a branded login screen, perhaps a federation to an external identity provider, and a token returned to a frontend application. That is the most visible part of the story, but it is only the beginning. In modern distributed systems, authentication does not stop at the first API. Real architectures involve multiple services, often chained together, where one API must call another on behalf of the user. This introduces a subtle but critical challenge, how do you propagate identity securely across API boundaries?
The OAuth 2.0 On Behalf Of (OBO) flow exists to solve exactly that problem. It allows an API to redeem a user’s token for another token targeted at a downstream API, thereby maintaining the link between the user and every service in the call chain. OBO is not the only option though. In many cases, the simpler Client Credentials flow, where an application authenticates as itself, may be the better fit. Choosing between them requires a solid understanding of what information needs to travel downstream, and what level of authorisation each API enforces.
Why Identity Propagation is important
Imagine you have a business portal built for insurance brokers. A broker signs in through B2C, their access token is validated by a backend-for-frontend (BFF) service, and that service must in turn call other APIs, one for programmes, one for pricing, and another for document storage. If the BFF simply forwards the original token, those APIs are likely to reject it because the audience claim does not match. Each API expects a token minted specifically for it.
The OBO flow provides a controlled way to handle this situation. The BFF takes the broker’s access token, redeems it with the identity provider, and receives a new token whose audience matches the downstream API. That new token still represents the broker, but is scoped appropriately for the specific service. The downstream API can therefore enforce user level permissions, while the overall call chain remains secure.
Entra vs B2C
The implementation of OBO looks similar in Entra and B2C, but there are important nuances. In Entra, app registrations typically live in the same tenant, API permissions can be granted and consented in a straightforward manner, and downstream APIs can be protected with minimal ceremony. In B2C, by contrast, you often manage multiple app registrations, one for the frontend, one for the API that receives the first token, and one or more for downstream APIs. Scopes must be defined explicitly, and B2C user flows or custom policies determine which claims appear in the tokens. One crucial limitation in B2C is that social logins complicate OBO. A user authenticating with Google or Facebook does not automatically receive a token that can be redeemed downstream. Without custom policies that transform and issue proper access tokens, OBO fails. Many teams discover this only when their integration tests begin failing with random “invalid audience” or “invalid scope” errors.
Setting Up the Registrations
To see this in action, imagine that our insurance platform defines three services, ProgramService, PricingService, and DocumentsService. Each is registered in Entra or B2C with its own application identity and exposed scopes. The BFF is registered as a confidential client with a client secret, and is granted permission to request tokens on behalf of the user for each of those downstream services. The frontend application, meanwhile, is registered as a public client that can sign users in and obtain an initial access token for the BFF.
The critical part is exposing scopes correctly. If the ProgramService app registration does not expose something like Program.ReadWrite, then no downstream token can be minted for it, no matter how well configured everything else might be. It is here that many projects stumble: scopes are an afterthought until they become the cause of persistent 401 responses.
Implementing OBO in .NET
The Microsoft.Identity.Web library has made OBO in .NET considerably easier. In the BFF’s Program.cs you might see configuration like this:
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddDownstreamWebApi("ProgramService", builder.Configuration.GetSection("ProgramService"));
builder.Services.AddDownstreamWebApi("PricingService", builder.Configuration.GetSection("PricingService"));
The configuration in appsettings.json ties each logical downstream service to its scopes:
"ProgramService": {
"BaseUrl": "https://programservice.ci.xxx.net",
"Scopes": "api://program-service/Program.ReadWrite"
},
"PricingService": {
"BaseUrl": "https://pricingservice.ci.xxx.net",
"Scopes": "api://pricing-service/Pricing.Read"
}
When a controller action calls a downstream API, the library handles token redemption behind the scenes:
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class BrokerController : ControllerBase
{
private readonly IDownstreamWebApi _programApi;
private readonly IDownstreamWebApi _pricingApi;
public BrokerController(IDownstreamWebApi programApi, IDownstreamWebApi pricingApi)
{
_programApi = programApi;
_pricingApi = pricingApi;
}
[HttpGet("program/{id}")]
public async Task<IActionResult> GetProgram(long id)
{
var program = await _programApi.CallWebApiForUserAsync("ProgramService", options =>
{
options.RelativePath = $"/v1/programs/{id}";
});
return Ok(program);
}
}
From the developer’s perspective, there is no manual token exchange. The middleware intercepts the incoming token, redeems it for a downstream one, caches it, and injects it into the outgoing request.
OBO vs Client Credentials
Now we come to the crucial comparison. Why not simply use Client Credentials everywhere and be done with it?
The OBO flow carries the user’s identity forward. The downstream API knows who the user is, can check their claims, and can apply user level authorisation rules. This is essential when actions are tied to individual users: uploading documents, modifying programmes, approving quotes. In such cases, the API must enforce that only specific users can perform specific actions. Without OBO, all requests would appear to come from a single service identity, and the downstream API would have no way to distinguish users.
The Client Credentials flow, on the other hand, carries only the application’s identity. No user information is present in the token. This makes it much simpler to configure and more efficient at runtime, because there is no need to redeem user tokens or maintain caches. It is ideal when the downstream API does not care about the end user and only needs to know that the call originates from a trusted service. A pricing API that serves reference data is a good example: the content is the same for everyone, so enforcing user level rules would add needless complexity. In practice, mature systems adopt a hybrid model. The BFF might use OBO to call APIs that contain sensitive, user specific information, while relying on Client Credentials for APIs that are purely system to system. The architecture diagram becomes a blend, some arrows labelled “on behalf of” carrying user identity, others labelled “client credentials” carrying only the service identity.
Common issues
Even with the right flow chosen, the details matter. Audience mismatches are the most common cause of errors; if the token’s aud claim does not match the downstream API’s expected identifier, the call will fail with 401 Unauthorized. Consent is another stumbling block: in Entra, delegated permissions must be granted and consented, while in B2C the custom policy must be issuing the right scopes. Token caching is often overlooked, leading to performance problems as every call triggers a token redemption. And in B2C scenarios with social logins, OBO simply does not work unless a custom policy is configured to the identity provider’s token into a downstream redeemable one.
Considerations
Beyond the basics, some advanced patterns emerge. In long call chains, latency can mount as each API redeems tokens. Architectures may need to collapse hops or switch to client credentials for efficiency. In regulated industries, step up authentication can be introduced, where a downstream API demands a token with higher assurance, for example, after multi factor authentication. While Azure does not currently support generic token exchange as defined by RFC 8693, the OBO flow is effectively a constrained form of it, and it is not uncommon to integrate it with other clouds’ identity providers when building hybrid systems.
The OBO flow and the Client Credentials flow are both essential tools for API to API authentication in Azure. OBO shines whenever the downstream API must know who the user is, and enforce user specific authorisation. Client Credentials excels when the downstream API only needs to know that a trusted application is calling, not which individual initiated the request. Mixing them intelligently results in a secure, efficient architecture that does not leak user identity unnecessarily but still enforces correct access where it matters. Getting OBO right in Entra or B2C is not a trivial checkbox exercise. It demands careful app registration, correct scopes, caching, and sometimes custom policies. But the payoff is worth it, a distributed system where security boundaries remain intact, where every API call can be tied back to the initiating user, and where sensitive actions are controlled at the right level. When combined with the simplicity of client credentials in the right places, the result is an architecture that is both secure and practical, the sign of a well designed system in Azure.





