Posted on

Blazor WebAssembly with Auth0 Authentication

Focus on Blazor WebAssembly (WASM)

Blazor WebAssembly is the most suitable option for creating client-side applications that consume APIs.

Key Points

  • C# in the Browser: The entire .NET runtime and your application are downloaded by the browser as WebAssembly files. C# code runs directly in the browser within a secure sandbox.

  • SPA Architecture: Blazor WASM creates Single Page Applications (SPA), meaning the app loads once and subsequent user interactions dynamically update only the necessary parts of the page, providing a smooth user experience.

  • Razor Components: User interfaces are built using Razor components (.razor), which combine HTML markup with C# logic (using the @code directive).

  • Open Standards: Building a Blazor app means using Microsoft technology (C#) built upon a totally open and universal foundation (WebAssembly). This ensures your app works on any modern device without depending on proprietary plugins.

Focus on Auth0

  • Auth0 is an Identity Provider that utilizes the OIDC (OpenID Connect) protocol.

  • Blazor WASM is a native OIDC client, and Microsoft provides the Microsoft.AspNetCore.Components.WebAssembly.Authentication libraries to handle it.

  • The Blazor application redirects the user to the Auth0 login page (Universal Login), receives the Access Token, and stores it securely.

Blazor WebAssembly Project

Creating the Project in Visual Studio

  1. Open Visual Studio and create a New Project.

  2. Search for and select the “Blazor WebAssembly App” template (ensure it is not “Blazor Server App”).

  3. In the configuration window, under Authentication, choose “None”.

✅ Summary of Recommended Settings

Option Status Notes
Progressive Web Application (PWA) Unchecked Not necessary for now; reduces complexity.
Do not use top-level statements Unchecked Uses modern, concise .NET code.
Configure for HTTPS Checked Essential. Your app must use HTTPS for Auth0 and API calls.
Include sample pages Checked Useful for base architecture (layout, menu) and quick testing.

Configuration on Auth0.com

1. Application

In the Auth0 portal, create a new application of type “Single Page Application” . Under the Settings tab:

  • Note down: Domain and Client ID.

  • Enable “Cross-Origin Authentication”.

2. API

In the API section, click + Create API. Complete these fields:

  • Name: A descriptive name (e.g., “My Data Service API”).

  • Identifier: This is the Audience!

The Identifier (Audience) Field

The Identifier (Audience) must be a URI (Uniform Resource Identifier) and usually follows this format:

  • Example Audience: https://api.yourdomain.com

  • Important: It doesn’t have to be a working URL, but it must be a unique URI.

Auth0 Configuration and Dependencies

Packages

To authenticate a Blazor WASM app, use Microsoft’s built-in OIDC support. Install these via NuGet:

  1. Microsoft.Authentication.WebAssembly.Msal

  2. Microsoft.AspNetCore.Components.WebAssembly.Authentication (Version 8.0.1)

  3. Microsoft.Extensions.Http

Application Configuration

Create appsettings.json in the wwwroot directory to keep configurations separate from code (Best Practice).

{
  "Auth0": {
    "Domain": "[YOUR-TENANT].auth0.com",
    "Authority": "https://[YOUR-TENANT].auth0.com",
    "ClientId": "[YOUR-AUTH0-CLIENT-ID]",
    "Audience": "https://api.yourdomain.com"
  },
  "TestWebservice": {
    "Address": "localhost:7003"
  }
}

Core Application Files

Program.cs

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

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

// =========================================================
// >>> BASE REGISTRATIONS (Must come before AddOidcAuthentication) <<<
// =========================================================
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<SignOutSessionStateManager>();
// =========================================================

// =========================================================
// >>> Crucial part for Auth0/OIDC Authentication <<<
// =========================================================
builder.Services.AddOidcAuthentication(options =>
{
    // Audience setting (CRUCIAL for calling APIs)
    // The Audience must be passed as an additional OIDC parameter
    options.ProviderOptions.AdditionalProviderParameters.Add(
        "audience",
        builder.Configuration["Auth0:Audience"]!
    );

    // Explicitly assign the Authority
    options.ProviderOptions.Authority = builder.Configuration["Auth0:Authority"]!;

    // Assign the ClientId
    options.ProviderOptions.ClientId = builder.Configuration["Auth0:ClientId"]!;

    // Scope Configuration
    options.ProviderOptions.DefaultScopes.Clear(); // Remove default scopes

    // Add all required scopes, including custom ones:
    options.ProviderOptions.DefaultScopes.Add("openid");
    options.ProviderOptions.DefaultScopes.Add("profile");
    options.ProviderOptions.DefaultScopes.Add("email");
    options.ProviderOptions.DefaultScopes.Add("address");
    options.ProviderOptions.DefaultScopes.Add("phone");
    options.ProviderOptions.DefaultScopes.Add("read:appointments"); // <--- API Scope
});
// =========================================================

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();

MainLayout.razor

To display the login status, use the built-in CascadingAuthenticationState component.

In Layout/MainLayout.razor, wrap the content as follows:

@using Microsoft.AspNetCore.Components.Authorization
@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <CascadingAuthenticationState>
        <main>
            <div class="top-row px-4">
                <LoginDisplay /> 
                <a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
            </div>

            <article class="content px-4">
                @Body
            </article>
        </main>
    </CascadingAuthenticationState>
</div>

LoginDisplay.razor

Create a Shared folder and add the LoginDisplay.razor component.

@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager

<AuthorizeView>
    <Authorized>
        Hello, @context.User.Identity?.Name!
        <button class="nav-link btn btn-link" @onclick="BeginSignOut">Log out</button>
    </Authorized>
    <NotAuthorized>
        <a href="authentication/login">Log in</a>
    </NotAuthorized>
</AuthorizeView>

@code {
    [CascadingParameter]
    private Task<AuthenticationState>? AuthenticationState { get; set; }

    private async Task BeginSignOut()
    {
        // Redirect user to the Auth0 Logout flow (via Blazor handler)
        await SignOutManager.SetSignOutState();
        Navigation.NavigateTo("authentication/logout");
    }
}

Note: Add the @using YourProjectName.Shared to _Imports.razor.

Authentication Script (index.html)

Add the missing <script> tag in your index.html inside the <body> section, before the Blazor framework script:

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

Authentication.razor

Create Pages/Authentication.razor:

@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="@Action" />

@code {
    [Parameter]
    public string? Action { get; set; }
}

Auth0.com Configuration (Part 2)

Upon launching the app, the “Login” link will appear after a slight delay. Clicking it might trigger “Checking login state…” followed by a “Callback URL mismatch” error.

To fix this, go to your Auth0 Application Settings and add:

  • Allowed Callback URLs: https://localhost:7004/authentication/login-callback

  • Allowed Logout URLs: https://localhost:7004/authentication/logout-callback

(Ensure the port matches your local project).

Now, Login and Logout should work perfectly!