blazorazure-ad-b2cblazor-webassemblyblazor-bootstrap

Azure AD B2C login redirect not working in Blazor WASM Standalone


I just started a new Blazor WASM Standalone project (targeting .NET 9) using the Blazor Bootstrap starter template here.

I then went through the setup process to use Azure AD B2C following this document here.

It detects that I'm not yet logged in and displays the correct message but when I click on Login link, it does nothing. I do see that the URL correctly changes to /authentication/login in the browser address bar but the actual redirect doesn't happen.

enter image description here

The interesting thing is that I also started a sepearate vanilla Blazor WASM Standalone project and using the same exact files/settings, I got that to work fine. Clicking login opens up the popup and I'm able to login just fine. Everything is identical except for Blazor Bootstrap template that I'm using in my original project.

I found this potential solution but it doesn't fix the problem for me i.e. adding the following to the project file didn't work.

<ItemGroup>
    <TrimmerRootAssembly Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
</ItemGroup>

Here's my App.razor file:

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    @if (context.User.Identity?.IsAuthenticated != true)
                    {
                        <RedirectToLogin />
                    }
                    else
                    {
                        <p role="alert">You are not authorized to access this resource.</p>
                    }
                </NotAuthorized>
            </AuthorizeRouteView>
            <FocusOnNavigate RouteData="@routeData" Selector="h1" />
        </Found>
        <NotFound>
            <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(MainLayout)">
                <p role="alert">Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

And here's my Program.cs:

using MyApp;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

bool _isProduction = true;
string _baseApiUrl = _isProduction ? "https://{MY_APP_PRODUCTION_URL}" : "https://localhost:7141";

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

// Add Azure AD B2C authentication
builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("https://{MY_B2C_TENANT_DOMAIN}.onmicrosoft.com/api/{MY_CUSTOM_SCOPES}");
});

// Add HttpClient for MyApp API calls
builder.Services.AddHttpClient("MyAppApiClient",
        client => client.BaseAddress = new Uri(_baseApiUrl))
    .AddHttpMessageHandler(sp =>
    {
        var handler = sp.GetService<AuthorizationMessageHandler>()
        .ConfigureHandler(
             authorizedUrls: new[] { _baseApiUrl },
             scopes: new[] { "openid", "profile", "offline_access", "https://{MY_B2C_TENANT_DOMAIN}.onmicrosoft.com/api/{MY_CUSTOM_SCOPES}" }
         );
        return handler;
    });

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("MyAppApiClient"));

builder.Services.AddBlazorBootstrap();

await builder.Build().RunAsync();

Any idea what maybe the issue here? I'd appreciate some suggestions. Thank you!

UPDATE:

I created a public repo that provides all the code and setup of the app as I have it. Here's the link: https://github.com/imsam67/blazor-wasm-aad-b2c


Solution

  • Anyway, if you directly access the url authentican/login with nothing happens, I doubt you missed add following script to index.html . I have tested the bootstrap template and there's no conflict.

        <script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>
    

    --------update-------
    In MainLayout.razor. It seems you want all the pages not accessible when not authorized by no @body included. But Authentication.razor is the component to manage login etc. So you have to let it being accessible. To do this you could add following to _Imports.rzor to authrozied all pages.

    @attribute [Authorize]
    

    But in Authentication.razor you need to AllowAnonymous to exclude this page

    @page "/authentication/{action}"
    @attribute [AllowAnonymous]
    

    In the MainLayout, you have to add @Body also to <NotAuthorized>make Authentication.razor being accesible.

                <ContentSection>
                    <div class="alert alert-danger" role="alert">
                        You are not logged in.
                        @Body
                    </div>
                </ContentSection>