Posted on

Blazor WebAssembly with Auth0 Authentication

Focus su Blazor WebAssembly (WASM)

Blazor WebAssembly è l’opzione più adatta per creare applicazioni client per il consumo di API.

Punti Chiave

  1. C# nel Browser: L’intero runtime .NET e la tua applicazione sono scaricati dal browser come file WebAssembly. Il codice C# viene eseguito direttamente nel browser in un sandbox.

  2. Architettura SPA: Blazor WASM crea applicazioni Single Page Application (SPA), il che significa che l’applicazione viene caricata una volta e le successive interazioni utente aggiornano dinamicamente solo le parti necessarie della pagina, offrendo un’esperienza utente fluida.

  3. Componenti Razor: Le interfacce utente sono costruite utilizzando i componenti Razor (.razor), che combinano markup HTML con logica C# (usando la direttiva @code).

Scrivere un’app in Blazor significa usare una tecnologia Microsoft (C#) che però poggia su una base totalmente aperta e universale (WebAssembly). Questo garantisce che la tua app funzionerà su qualsiasi dispositivo moderno, senza dipendere da plugin proprietari

Focus su Auth0

  • Auth0 è un Identity Provider che usa il protocollo OIDC (OpenID Connect).

  • Blazor WASM è un client OIDC nativo e Microsoft fornisce le librerie Microsoft.AspNetCore.Components.WebAssembly.Authentication per gestirlo.

  • L’applicazione Blazor reindirizza l’utente alla pagina di login Auth0 (Universal Login), riceve l’Access Token e lo memorizza.

Progetto Blazor WebAssembly

Creazione del Progetto in Visual Studio

  • Apri Visual Studio e crea un Nuovo Progetto.

  • Cerca e seleziona il template “Blazor WebAssembly App” (assicurati che non sia “Blazor Server App”).

  • Nella finestra di configurazione, per quanto riguarda  l’Autenticazione, scegli “Nessuna”

✅ Riepilogo delle Tue Scelte (Consigliate)

Opzione Stato (Check) Note
Applicazione Web Progressiva (PWA) Deselezionato Non necessario ora, aumenta la complessità.
Non usare istruzioni di primo livello Deselezionato Usa il codice moderno e conciso di .NET.
Configura per HTTPS Selezionato Essenziale. La tua app deve usare HTTPS per l’autenticazione con Auth0 (e per chiamare le API in produzione).
Include sample pages Selezionato Utile per avere un’architettura di base già pronta (layout, menu) e per testare velocemente l’autenticazione.

Configurazione su Auth0.com

Applicazione

Nel portale Auth0 crea una nuova applicazione di tipo “Single Page Application“.

Nella applicazione , nella tab settings :

  • prendi nota di : Domain e ClientID.
  • Seleziona “Cross-Origin Authentication”

API

Nella sezione API , Clicca su + Create API. Devi compilare questi campi:

  • Name (Nome): Un nome descrittivo (es. “Mio Servizio Dati API”).

  • Identifier (Identificativo): Questo è l’Audience!

Il Campo Identifier (Audience)

L’Identificativo (Audience) deve essere un URI (Uniform Resource Identifier) e di solito ha questo formato:

Esempio Audience: https://api.iltuoservizio.com

Importante: Non deve per forza essere un URL funzionante, ma deve essere un URI univoco.

Configurazione di Auth0 e delle Dipendenze

Pacchetti

Per autenticare un’applicazione Blazor WebAssembly con Auth0, devi utilizzare il supporto integrato di Microsoft per l’autenticazione OIDC (OpenID Connect), che è il protocollo utilizzato da Auth0. Con Nuget

  • installa il pacchetto Microsoft.Authentication.WebAssembly.Msal
  • installa il pacchetto Microsoft.AspNetCore.Components.WebAssembly.Authentication. Attenzione: installare la versione 8.0.1
  • installa il pacchetto Microsoft.Extensions.Http

Configurazione Applicazione

Nelle applicazioni .NET (inclusi i client Blazor WASM), il file appsettings.json è lo standard per archiviare le configurazioni, come: le Credenziali di Autenticazione: (Il Domain e il Client ID di Auth0). Blazor WebAssembly è progettato per leggere le impostazioni da questo file. Crea il file appsettings.json nella directory wwwroot per mantenere le configurazioni separate dal codice (il che è una best practice).

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

File Applicazione

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");

// =========================================================
// >>> REGISTRAZIONI DI BASE (Devono venire prima di AddOidcAuthentication) <<<
// =========================================================
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<SignOutSessionStateManager>();
// =========================================================

// =========================================================
// >>> Parte cruciale per l'autenticazione Auth0/OIDC <<<
// =========================================================
builder.Services.AddOidcAuthentication(options =>
{
// Impostazione dell'Audience (CRUCIALE per chiamare le API)
// L'Audience deve essere passato come parametro aggiuntivo OIDC
options.ProviderOptions.AdditionalProviderParameters.Add(
"audience",
builder.Configuration["Auth0:Audience"]!
);

// Modifica o aggiungi questo per assegnare esplicitamente l'Authority
options.ProviderOptions.Authority = builder.Configuration["Auth0:Authority"]!;

// Assegna il ClientId
options.ProviderOptions.ClientId = builder.Configuration["Auth0:ClientId"]!;

// 3. Configurazione degli Scope
// Sostituiamo gli Scope predefiniti con la tua lista completa.
options.ProviderOptions.DefaultScopes.Clear(); // Rimuovi gli scope predefiniti

// Aggiungi tutti gli scope richiesti, inclusi quelli personalizzati:
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"); // <--- Scope API
});

// =========================================================

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

await builder.Build().RunAsync();
MainLayout.razor

Per mostrare lo stato di login, devi usare il componente Blazor integrato CascadingAuthenticationState nel file di layout principale.

Nel file Layout/MainLayout.razor, assicurati che il contenuto sia racchiuso in questo

@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

Crea la cartella Shared ed in essa il componente LoginDisplay.razor.

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

@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager

<AuthorizeView>
<Authorized>
Ciao, @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()
{
// Reindirizza l'utente al flusso di Logout di Auth0 (tramite il gestore Blazor)
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}

Importante. Per usare questo componente è necessario aggiungere a _Imports.razor il collegamento alla cartella Shared.

Script di Autenticazione (index.html)

Devi semplicemente aggiungere il tag <script> mancante nel tuo index.html nella sezione <body>, prima del tag che carica Blazor (_framework/blazor.webassembly.js).

Aggiungi questa riga:

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

Crea il componente Pages/Authentication.razor

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

<RemoteAuthenticatorView Action="@Action" />

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

// NOTA BENE: Non è necessario dichiarare RouteData
// in questo componente specifico se usi solo l'Action
}

Configurazione Auth0.com (seconda parte)

Lanciando l’app vedrete che la scritta Login in alto compare ma compare con u lieve ritardo. Se spingo login , scrive “Checking login state…” , poi, dopo alcuni secondi, vieni reindirizzato su una pagina si Auth0.com con errore “Callback URL mismatch.The provided redirect_uri is not in the list of allowed callback URLs.”

Trovate la vostra applicazione :

  • alla voce “Allowed Callback Urls” aggiungete   : https://localhost:7004/authentication/login-callback
  • alla voce “Allowed Logout Urls” aggiungete : https://localhost:7004/authentication/logout-callback

Riprovate. Dovreste avere implementato Login e Logout