简体   繁体   中英

Cannot get a Blazor Server App to call a secure downstream Web API using Azure AD B2C

I created a new blazor server project using do.net new

dotnet new blazorserver --output "PlayingWithBlazor" --framework net6.0 --auth IndividualB2C --aad-b2c-instance "https://{mytenant}.b2clogin.com/" --domain "{mydomain}.onmicrosoft.com" --client-id "{myClientID}" --susi-policy-id "B2C_1_SignUp" --called-api-url "https://localhost:7042/api" --called-api-scopes "https://{mytennant}.onmicrosoft.com/a82e00f8-939d-47ab-b2f3-4e557020f729/access_as_user"

When I run the app using Visual Studio, I can log in using my AAD B2C user flow. However, when I click on "Call Web API" it throws an exception at await downstreamAPI.CallWebApiForUserAsync in CallWebAPI.razor

The @Code block for CallWebApi.razor that was autogenerated with the project looks like this:

@code { private HttpResponseMessage? response; private string? apiResult;

protected override async Task OnInitializedAsync()
{
    
    try
    {
     
        response = await downstreamAPI.CallWebApiForUserAsync(
                "DownstreamApi",
                options => options.RelativePath = "/Subscriber");

        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            apiResult = await response.Content.ReadAsStringAsync();
        }
        else
        {
            apiResult = "Failed to call the web API";
        }
    }
    catch (Exception ex)
    {
        ConsentHandler.HandleException(ex);
    }
}

Here is the Exception:

Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException
  HResult=0x80131500
  Message=IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent. 
  Source=Microsoft.Identity.Web
  StackTrace:
   at Microsoft.Identity.Web.TokenAcquisition.<GetAuthenticationResultForUserAsync>d__16.MoveNext()
   at Microsoft.Identity.Web.DownstreamWebApi.<CallWebApiForUserAsync>d__5.MoveNext()
   at PlayingWithBlazor.Pages.CallWebApi.<OnInitializedAsync>d__3.MoveNext() in C:\Users\AndySchneider\source\repos\PlayingWithBlazor\Pages\CallWebApi.razor:line 33

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call. 

My Web API is pretty plain vanilla right now.

// GET: api/<SubscriberController>
        [Authorize]
        [HttpGet]
        public IEnumerable<Subscriber> Get()
        {
            return subscriberRepository.GetAllAsync().Result;
        }

From what I have gathered, it looks like Microsoft.Identity.Web is calling MSAL. Everything I have found so far in troubleshooting an error like this leads me to catching MSAL exceptions and working with AcquireTokenSilent. Because I am not using MSAL directly, I am not sure where I would need to go to add this error handling, or what to add, for that matter.

For what its worth, here is my Program.cs as well

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using PlayingWithBlazor.Data;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Identity.Client;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"))
        .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
            .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
            .AddInMemoryTokenCaches();

builder.Services.AddControllersWithViews()
    .AddMicrosoftIdentityUI();


builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
    .AddMicrosoftIdentityConsentHandler();
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}


app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

I tried the scenario in my environment.

I have called something like below controller method:

MyController.cs

[Authorize]
        [HttpGet]
        public IEnumerable<Subscriber> Get()
        {
            var user = await _api.Me.Request().GetAsync();

          ViewData["ApiResult"] = user.DisplayName;
            return await _api.Me.Request().GetAsync().Result;
        }

Received same error:

MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.
Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken)
MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent

在此处输入图像描述

**On behalf flow is used while using the call with `CallWebApiForAppAsync`, which is not available for Azure AD B2C.**

Please check this b2c limitations · AzureAD/microsoft-identity-web Wiki · GitHub

As an alternative, you can request access tokens for downstream APIs using GetAccessTokenForUserAsync

And the use exception handler to handle if any issue persists And allow MicrosoftIdentityWebChallengeUserException to handle the exception in try-catch block while calling the user.

try
{
// other code
    ...
    ITokenAcquisition.GetAccessTokenForUserAsync(...)
//other code
}
catch (MicrosoftIdentityWebChallengeUserException)
{
    
    // catch  after capture happens with [AuthorizeForScopes] attribute 
    
    throw;
}
catch (Exception ex)
{
    exceptionMessage = ex.Message;
}

Make sure to give api permissions required and it has to be granted admin consent through portal or during authentication.

Then call to my API can be successful:

在此处输入图像描述

Reference: blazor-server-authorized-downstream-api-call-with-azure-ad-b2c

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM