Service to service calls on behalf of the user - AzureAD/azure-activedirectory-library-for-dotnet GitHub Wiki
Web APIs can acquire tokens in the name of a user, leveraging User assertions
Web API cannot have any user interaction, and therefore when a web API (labeled "first Web API") needs to call another Web API (named "second Web API") in the name of a user, it needs to use the "On Behalf Of" OAuth 2.0 flow, which is one of the flows described in details in Daemon or Server Application to Web API (along with the client credential flows that you have already seen).
This flow is a confidential client flow, and therefore the first web API provides client credentials (client secret or certificate), as seen in the previous paragraph. However, it will also provide another parameter named the userAssertion
: this time, the first web API will receive a bearer token and send it to Azure AD by embedding it into a user assertion to request another token to the downstream API.
How to do?
The flow is explained in details in the following sample: https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof and https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof-ca, this last sample demonstrates how the middle Web API needs to process claims exceptions in the case of conditional access.
The code extracting the bearer token and creating the user assertion is available from TodoListController.cs#L138-L142
ClientCredential clientCred = new ClientCredential(clientId, appKey);
ClaimsPrincipal current = ClaimsPrincipal.Current;
var bootstrapContext = current.Identities.First().BootstrapContext
as System.IdentityModel.Tokens.BootstrapContext;
string userName = current.FindFirst(ClaimTypes.Upn) != null
? current.FindFirst(ClaimTypes.Upn).Value
: current.FindFirst(ClaimTypes.Email).Value;
string userAccessToken = bootstrapContext.Token;
UserAssertion userAssertion = new UserAssertion(userAccessToken,
"urn:ietf:params:oauth:grant-type:jwt-bearer",
userName);
The web API then acquires an access token for the downstream web API using the overrides of AcquireTokenAsync having a user assertion parameter as shown in TodoListController.cs#L158
try
{
result = await authContext.AcquireTokenSilentAsync(graphResourceId, clientId);
}
catch (AdalException adalException)
{
if (adalException.ErrorCode == AdalError.FailedToAcquireTokenSilently || adalException.ErrorCode == AdalError.UserInteractionRequired)
{
result = await authContext.AcquireTokenAsync(graphResourceId,
clientCred,
userAssertion);
}
}
Handling claim challenge exceptions.
See Handling ADAL.NET claim challenge exceptions See also Developer Guidance for Azure Active Directory Conditional Access
Samples illustrating the on-behalf-of flow with ADAL.NET
Sample | Platform | Description |
---|---|---|
active-directory-dotnet-webapi-onbehalfof | Desktop (WPF), SPA (JavaScript), Web API (ASP.NET MVC) | A .NET 4.5 MVC Web API protected by Azure AD that receives tokens from a client and uses ADAL to get tokens for calling the Microsoft Graph ![]() |
active-directory-dotnet-webapi-onbehalfof-ca | Desktop (WPF), SPA (JavaScript), Web API (ASP.NET MVC) | A .NET 4.5 MVC Web API protected by Azure AD that receives tokens from a client and uses ADAL to get tokens for calling a downstream web API with a Conditional Access policy applied to it. This is a variation of the previous sample to illustrate processing of Conditional access ![]() |