Azure AD Authentication for Xamarin Application Fails Authentication on Downstream Web API

Viewed 174

I've built an ASP.NET core Web App calling an ASP.NET Core Web API that is secured using Azure AD as described here.

I've also built a Xamarin mobile application that authenticates to the Web App's Azure App Service and utilizes the downstream Web API as described here.

The Web App can access the API without issue; however, the Xamarin application fails with an HTTP Response Message: Unauthorized.

Utilizing the guidance from the Microsoft Identity team here, I was able to track down the cause of the failure.

Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.

A snippet from the source code for the requirement shows where it fails. Specifically, it is on the IsAuthenticated check.

public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DenyAnonymousAuthorizationRequirement requirement)
    {
        var user = context.User;
        var userIsAnonymous =
            user?.Identity == null ||
            !user.Identities.Any(i => i.IsAuthenticated);
        if (!userIsAnonymous)
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }

Tracing the code from the working Web App, the context is generated as shown below. The critical point is the GetAccessTokenForUserAsync method gets an access token for a downstream API on behalf of the user account for which the claims are provided in the User member of the controller's HttpContext parameter.

...
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration);

await PrepareAuthenticatedClient();

var response = await httpClient.GetAsync($"https://example.com/api/mycontroller");
...

private async Task PrepareAuthenticatedClient()
{
    var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "api://xxxx/.default"});
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}

enter image description here

Tracing the code from the failing Xamarin application, the context is generated as shown below.

...
PCA = PublicClientApplicationBuilder
   .Create(ClientID)
   .WithTenantId(TenantID)
   .WithRedirectUri($"msal{ClientID}://auth")
   .Build();

authResult = await App.PCA.AcquireTokenInteractive("api://xxxx/.default").ExecuteAsync();

var result = Network.GetHttpContentWithTokenAsync(authToken);
...

public async Task<string> GetHttpContentWithTokenAsync(string token)
{
    HttpClient client = new HttpClient();
    HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://example.com/api/mycontroller");
    message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
    HttpResponseMessage response = await client.SendAsync(message);
    string responseString = await response.Content.ReadAsStringAsync();
    return responseString;
}

enter image description here

How do get the authenticated user into the HTTP context for the Xamarin application?

0 Answers
Related