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"));
}
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;
}
How do get the authenticated user into the HTTP context for the Xamarin application?

