Proteggere un'app ASP.NET Core Blazor WebAssembly ospitata con Identity server

Questo articolo illustra come creare una soluzione ospitata Blazor WebAssembly che usa Duende Identity Server per autenticare gli utenti e le chiamate API.

Importante

Duende Software potrebbe richiedere il pagamento di una tariffa di licenza per l'uso in produzione di Duende Identity Server. Per altre informazioni, vedere Eseguire la migrazione da ASP.NET Core 5.0 a 6.0.

Nota

Per configurare un'app autonoma o ospitata Blazor WebAssembly per l'uso di un'istanza del server esterna Identity esistente, seguire le indicazioni riportate in Proteggere un'app autonoma ASP.NET Core Blazor WebAssembly con la libreria di autenticazione.

Per altre informazioni sulla copertura dello scenario di sicurezza dopo la lettura di questo articolo, vedere ASP.NET Scenari di sicurezza aggiuntivi di baseBlazor WebAssembly.

scenario

Le sottosezioni della procedura dettagliata illustrano come:

  • Creare l'app Blazor
  • Eseguire l'app

Creare un'app Blazor

Per creare un nuovo Blazor WebAssembly progetto con un meccanismo di autenticazione:

  1. Creare un nuovo progetto.

  2. Scegliere il Blazor WebAssembly modello App . Selezionare Avanti.

  3. Specificare un nome di progetto senza usare trattini. Verificare che la posizione sia corretta. Selezionare Avanti.

    Evitare di usare trattini (-) nel nome del progetto che interrompono la formazione dell'identificatore dell'app OIDC. La logica nel Blazor WebAssembly modello di progetto usa il nome del progetto per un identificatore di app OIDC nella configurazione della soluzione e i trattini non sono consentiti in un identificatore di app OIDC. Le maiuscole/minuscole Pascal (BlazorSample) o caratteri di sottolineatura (Blazor_Sample) sono alternative accettabili.

  4. Nella finestra di dialogo Informazioni aggiuntive selezionare Account singoli come tipo di autenticazione per archiviare gli utenti all'interno dell'app usando il sistema di Identity ASP.NET Core.

  5. Selezionare la casella di controllo ASP.NET Core Hosted .Select the ASP.NET Core Hosted checkbox.

  6. Selezionare il pulsante Crea per creare l'app.

Eseguire l'app

Eseguire l'app dal Server progetto. Quando si usa Visual Studio, è possibile:

  • Selezionare la freccia a discesa accanto al pulsante Esegui . Aprire Configura progetti di avvio dall'elenco a discesa. Selezionare l'opzione Progetto di avvio singolo. Confermare o modificare il progetto per il progetto di avvio nel Server progetto.

  • Verificare che il Server progetto sia evidenziato in Esplora soluzioni prima di avviare l'app con uno degli approcci seguenti:

    • Selezionare il pulsante Run (Esegui).
    • Usare Debug>Avvia debug dal menu.
    • Premere F5.
  • In una shell dei comandi passare alla Server cartella del progetto della soluzione. Eseguire il comando dotnet run.

Parti della soluzione

Questa sezione descrive le parti di una soluzione generata dal Blazor WebAssembly modello di progetto e descrive come vengono configurati i progetti e Server la soluzione Client per riferimento. Non sono disponibili indicazioni specifiche da seguire in questa sezione per un'applicazione funzionante di base se è stata creata l'app usando le indicazioni nella sezione Procedura dettagliata . Le indicazioni contenute in questa sezione sono utili per aggiornare un'app per autenticare e autorizzare gli utenti. Tuttavia, un approccio alternativo all'aggiornamento di un'app consiste nel creare una nuova app dalle indicazioni nella sezione Procedura dettagliata e spostare i componenti, le classi e le risorse dell'app nella nuova app.

Server servizi app

Questa sezione riguarda l'app della Server soluzione.

Vengono registrati i servizi seguenti.

  • Nel file Program:

    • Entity Framework Core e ASP.NET Core Identity:

      builder.Services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite( ... ));
      builder.Services.AddDatabaseDeveloperPageExceptionFilter();
      
      builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Server con un metodo helper aggiuntivo AddApiAuthorization che configura le convenzioni predefinite ASP.NET Core su Identity Server:

      builder.Services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Autenticazione con un metodo helper aggiuntivo AddIdentityServerJwt che configura l'app per convalidare i token JWT prodotti dal Identity server:

      builder.Services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • In Startup.ConfigureServices di Startup.cs:

    • Entity Framework Core e ASP.NET Core Identity:

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(
              Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Server con un metodo helper aggiuntivo AddApiAuthorization che configura le convenzioni predefinite ASP.NET Core su Identity Server:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Autenticazione con un metodo helper aggiuntivo AddIdentityServerJwt che configura l'app per convalidare i token JWT prodotti dal Identity server:

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Nota

Quando viene registrato un singolo schema di autenticazione, lo schema di autenticazione viene usato automaticamente come schema predefinito dell'app e non è necessario dichiarare lo schema a AddAuthentication o tramite AuthenticationOptions. Per altre informazioni, vedere Panoramica dell'autenticazione principale di ASP.NET e dell'annuncio di ASP.NET Core (aspnet/Announcements #490).

  • Nel file Program:
  • In Startup.Configure di Startup.cs:
  • Il Identity middleware del server espone gli endpoint OIDC (OpenID Connessione):

    app.UseIdentityServer();
    
  • Il middleware di autenticazione è responsabile della convalida delle credenziali della richiesta e dell'impostazione dell'utente nel contesto della richiesta:

    app.UseAuthentication();
    
  • Il middleware di autorizzazione abilita le funzionalità di autorizzazione:

    app.UseAuthorization();
    

Autorizzazione API

Questa sezione riguarda l'app della Server soluzione.

Il AddApiAuthorization metodo helper configura Identity Server per scenari ASP.NET Core. Identity Server è un framework potente ed estendibile per la gestione dei problemi di sicurezza delle app. Identity Il server espone la complessità non necessaria per gli scenari più comuni. Di conseguenza, viene fornito un set di convenzioni e opzioni di configurazione che si considera un buon punto di partenza. Una volta modificata l'autenticazione, la potenza completa del Identity server è disponibile per personalizzare l'autenticazione in base ai requisiti di un'app.

Aggiungere un gestore di autenticazione per un'API coesistenti con Identity Server

Questa sezione riguarda l'app della Server soluzione.

Il AddIdentityServerJwt metodo helper configura uno schema di criteri per l'app come gestore di autenticazione predefinito. Il criterio è configurato per consentire Identity di gestire tutte le richieste indirizzate a qualsiasi sottopercorso nello Identity spazio URL in /Identity. JwtBearerHandler Gestisce tutte le altre richieste. Inoltre, questo metodo:

  • Registra una risorsa API con Identity Server con un ambito predefinito di {PROJECT NAME}API, dove il {PROJECT NAME} segnaposto è il nome del progetto al momento della creazione dell'app.
  • Configura il middleware del token di connessione JWT per convalidare i token rilasciati dal Identity server per l'app.

Controllo previsioni meteo

Questa sezione riguarda l'app della Server soluzione.

In (Controllers/WeatherForecastController.cs), l'attributoWeatherForecastController[Authorize] viene applicato alla classe . L'attributo indica che l'utente deve essere autorizzato in base ai criteri predefiniti per accedere alla risorsa. I criteri di autorizzazione predefiniti sono configurati per l'uso dello schema di autenticazione predefinito, configurato da AddIdentityServerJwt. Il metodo helper configura JwtBearerHandler come gestore predefinito per le richieste all'app.

Contesto del database dell'applicazione

Questa sezione riguarda l'app della Server soluzione.

ApplicationDbContext In (Data/ApplicationDbContext.cs) DbContext estende ApiAuthorizationDbContext<TUser> per includere lo schema per Identity Server. L'oggetto ApiAuthorizationDbContext<TUser> è derivato da IdentityDbContext.

Per ottenere il controllo completo dello schema del database, ereditare da una delle classi disponibili IdentityDbContext e configurare il contesto per includere lo Identity schema chiamando builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) nel OnModelCreating metodo .

Controller di configurazione OIDC

Questa sezione riguarda l'app della Server soluzione.

OidcConfigurationController In (Controllers/OidcConfigurationController.cs), viene effettuato il provisioning dell'endpoint client per gestire i parametri OIDC.

Impostazioni app

Questa sezione riguarda l'app della Server soluzione.

Nel file di impostazioni dell'app (appsettings.json) nella radice del progetto la IdentityServer sezione descrive l'elenco dei client configurati. Nell'esempio seguente è presente un singolo client. Il nome del client corrisponde al Client nome dell'assembly dell'app ed è mappato per convenzione al parametro OAuth ClientId . Il profilo indica il tipo di app configurato. Il profilo viene usato internamente per guidare convenzioni che semplificano il processo di configurazione per il server.

"IdentityServer": {
  "Clients": {
    "{ASSEMBLY NAME}": {
      "Profile": "IdentityServerSPA"
    }
  }
}

Il segnaposto {ASSEMBLY NAME} è il Client nome dell'assembly dell'app , ad esempio BlazorSample.Client.

Pacchetto di autenticazione

Questa sezione riguarda l'app della Client soluzione.

Quando un'app viene creata per usare singoli account utente (Individual), l'app riceve automaticamente un riferimento al pacchetto per il Microsoft.AspNetCore.Components.WebAssembly.Authentication pacchetto. Il pacchetto fornisce un set di primitive che consentono all'app di autenticare gli utenti e ottenere i token per chiamare le API protette.

Se si aggiunge l'autenticazione a un'app, aggiungere manualmente il Microsoft.AspNetCore.Components.WebAssembly.Authentication pacchetto all'app.

Nota

Per indicazioni sull'aggiunta di pacchetti alle app .NET, vedere gli articoli sotto Installare e gestire pacchetti in Flusso di lavoro dell'utilizzo di pacchetti (documentazione di NuGet). Confermare le versioni corrette del pacchetto all'indirizzo NuGet.org.

Configurazione HttpClient

Questa sezione riguarda l'app della Client soluzione.

Program Nel file , un oggetto denominato HttpClient è configurato per fornire HttpClient istanze che includono token di accesso durante l'esecuzione di richieste all'API server. Per impostazione predefinita, la soluzione denominata HttpClient è {PROJECT NAME}.ServerAPI, dove il {PROJECT NAME} segnaposto è il nome del progetto.

builder.Services.AddHttpClient("{PROJECT NAME}.ServerAPI", 
        client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("{PROJECT NAME}.ServerAPI"));

Il segnaposto {PROJECT NAME} è il nome del progetto durante la creazione della soluzione. Ad esempio, specificando un nome di progetto di BlazorSample produce un nome denominato HttpClient .BlazorSample.ServerAPI

Nota

Se si configura un'app per l'uso di un'istanza Blazor WebAssembly del server esistente Identity che non fa parte di una soluzione ospitata Blazor , modificare la HttpClient registrazione dell'indirizzo di base da IWebAssemblyHostEnvironment.BaseAddress (builder.HostEnvironment.BaseAddress) all'URL dell'endpoint di autorizzazione API dell'app server.

Supporto dell'autorizzazione API

Questa sezione riguarda l'app della Client soluzione.

Il supporto per l'autenticazione degli utenti viene collegato al contenitore del servizio dal metodo di estensione fornito all'interno del Microsoft.AspNetCore.Components.WebAssembly.Authentication pacchetto. Questo metodo configura i servizi richiesti dall'app per interagire con il sistema di autorizzazione esistente.

builder.Services.AddApiAuthorization();

Per impostazione predefinita, la configurazione per l'app viene caricata per convenzione da _configuration/{client-id}. Per convenzione, l'ID client viene impostato sul nome dell'assembly dell'app. Questo URL può essere modificato in modo che punti a un endpoint separato chiamando l'overload con le opzioni.

File Imports

Questa sezione riguarda l'app della Client soluzione.

Lo Microsoft.AspNetCore.Components.Authorization spazio dei nomi viene reso disponibile in tutta l'app tramite il _Imports.razor file :

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using {APPLICATION ASSEMBLY}
@using {APPLICATION ASSEMBLY}.Shared

Index Pagina

Questa sezione riguarda l'app della Client soluzione.

La pagina Indice (wwwroot/index.html) include uno script che definisce in AuthenticationService JavaScript. AuthenticationService gestisce i dettagli di basso livello del protocollo OIDC. L'app chiama internamente i metodi definiti nello script per eseguire le operazioni di autenticazione.

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

Componente App

Questa sezione riguarda l'app della Client soluzione.

Il App componente (App.razor) è simile al App componente presente nelle Blazor Server app:

  • Il CascadingAuthenticationState componente gestisce l'esposizione dell'oggetto AuthenticationState al resto dell'app.
  • Il AuthorizeRouteView componente garantisce che l'utente corrente sia autorizzato ad accedere a una determinata pagina o che esegua il rendering del RedirectToLogin componente.
  • Il componente gestisce il RedirectToLogin reindirizzamento di utenti non autorizzati alla pagina di accesso.

A causa delle modifiche apportate al framework tra le versioni di ASP.NET Core, Razor il markup per il App componente (App.razor) non viene visualizzato in questa sezione. Per esaminare il markup del componente per una determinata versione, usare uno degli approcci seguenti:

  • Creare un'app di cui è stato effettuato il provisioning per l'autenticazione dal modello di progetto predefinito Blazor WebAssembly per la versione di ASP.NET Core che si intende usare. Esaminare il App componente (App.razor) nell'app generata.

  • Esaminare il componente (App.razor) nell'origineAppdi riferimento. Selezionare la versione dal selettore di ramo e cercare il componente nella ProjectTemplates cartella del repository perché è stato spostato nel corso degli anni.

    Nota

    I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Componente RedirectToLogin

Questa sezione riguarda l'app della Client soluzione.

Componente RedirectToLogin (RedirectToLogin.razor):

  • Gestisce il reindirizzamento di utenti non autorizzati alla pagina di accesso.
  • L'URL corrente a cui l'utente sta tentando di accedere viene mantenuto in modo che possa essere restituito a tale pagina se l'autenticazione ha esito positivo:
    • Stato della cronologia di spostamento in ASP.NET Core in .NET 7 o versione successiva.
    • Stringa di query in ASP.NET Core in .NET 6 o versioni precedenti.

Esaminare il componente nell'origine RedirectToLogindi riferimento. La posizione del componente è cambiata nel tempo, quindi usare gli strumenti di ricerca gitHub per individuare il componente.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Componente LoginDisplay

Questa sezione riguarda l'app della Client soluzione.

Il LoginDisplay rendering del MainLayout componente (LoginDisplay.razor) viene eseguito nel componente (MainLayout.razor) e gestisce i comportamenti seguenti:

  • Per gli utenti autenticati:
    • Visualizza il nome utente corrente.
    • Offre un collegamento alla pagina del profilo utente in ASP.NET Core Identity.
    • Offre un pulsante per disconnettersi dall'app.
  • Per gli utenti anonimi:
    • Offre la possibilità di registrarsi.
    • Offre l'opzione di accesso.

A causa delle modifiche apportate al framework tra le versioni di ASP.NET Core, Razor il markup per il LoginDisplay componente non viene visualizzato in questa sezione. Per esaminare il markup del componente per una determinata versione, usare uno degli approcci seguenti:

  • Creare un'app di cui è stato effettuato il provisioning per l'autenticazione dal modello di progetto predefinito Blazor WebAssembly per la versione di ASP.NET Core che si intende usare. Esaminare il LoginDisplay componente nell'app generata.

  • Esaminare il componente nell'origine LoginDisplaydi riferimento. La posizione del componente è cambiata nel tempo, quindi usare gli strumenti di ricerca gitHub per individuare il componente. Viene usato il contenuto basato su modelli per Hosted uguale a true .

    Nota

    I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Componente Authentication

Questa sezione riguarda l'app della Client soluzione.

La pagina prodotta dal Authentication componente (Pages/Authentication.razor) definisce le route necessarie per la gestione di diverse fasi di autenticazione.

Componente RemoteAuthenticatorView :

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

<RemoteAuthenticatorView Action="Action" />

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

Nota

I tipi di riferimento nullable (NRT) e l'analisi statica dello stato null del compilatore .NET sono supportati in ASP.NET Core in .NET 6 o versione successiva. Prima del rilascio di ASP.NET Core in .NET 6, il string tipo viene visualizzato senza la designazione di tipo Null (?).

Componente FetchData

Questa sezione riguarda l'app della Client soluzione.

Il FetchData componente mostra come:

  • Effettuare il provisioning di un token di accesso.
  • Usare il token di accesso per chiamare un'API risorsa protetta nell'app Server .

La @attribute [Authorize] direttiva indica al Blazor WebAssembly sistema di autorizzazione che l'utente deve essere autorizzato per visitare questo componente. La presenza dell'attributo nell'app Client non impedisce che l'API nel server venga chiamata senza credenziali appropriate. L'app Server deve essere usata [Authorize] anche negli endpoint appropriati per proteggerli correttamente.

IAccessTokenProvider.RequestAccessToken richiede un token di accesso che può essere aggiunto alla richiesta per chiamare l'API. Se il token viene memorizzato nella cache o il servizio è in grado di effettuare il provisioning di un nuovo token di accesso senza interazione dell'utente, la richiesta di token ha esito positivo. In caso contrario, la richiesta di token ha esito negativo con un AccessTokenNotAvailableExceptionoggetto , che viene intercettato in un'istruzione try-catch .

Per ottenere il token effettivo da includere nella richiesta, l'app deve verificare che la richiesta sia riuscita chiamando tokenResult.TryGetToken(out var token).

Se la richiesta ha esito positivo, la variabile del token viene popolata con il token di accesso. La AccessToken.Value proprietà del token espone la stringa letterale da includere nell'intestazione della Authorization richiesta.

Se la richiesta non è riuscita perché non è stato possibile effettuare il provisioning del token senza l'interazione dell'utente:

  • ASP.NET Core in .NET 7 o versione successiva: l'app passa all'uso AccessTokenResult.InteractiveRequestUrl dell'oggetto specificato AccessTokenResult.InteractionOptions per consentire l'aggiornamento del token di accesso.
  • ASP.NET Core in .NET 6 o versioni precedenti: il risultato del token contiene un URL di reindirizzamento. Passando a questo URL, l'utente passa alla pagina di accesso e torna alla pagina corrente dopo l'autenticazione completata.
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using {APP NAMESPACE}.Shared
@attribute [Authorize]
@inject HttpClient Http

...

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Servizio app di Azure in Linux

Specificare l'autorità emittente in modo esplicito durante la distribuzione in app Azure Servizio in Linux. Per altre informazioni, vedere Usare Identity per proteggere un back-end dell'API Web per le applicazioni a pagina singola.

Attestazione di nome e ruolo con autorizzazione API

Factory utente personalizzata

Nell'app Client creare una factory utente personalizzata. Identity Il server invia più ruoli come JSmatrice ON in una singola role attestazione. Un singolo ruolo viene inviato come valore stringa nell'attestazione. La factory crea una singola role attestazione per ognuno dei ruoli dell'utente.

CustomUserFactory.cs:

using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomUserFactory
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

            if (roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = 
                    account.AdditionalProperties[identity.RoleClaimType];

                if (options.RoleClaim is not null && rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            var roleValue = role.GetString();

                            if (!string.IsNullOrEmpty(roleValue))
                            {
                                identity.AddClaim(
                                  new Claim(options.RoleClaim, roleValue));
                            }

                        }
                    }
                    else
                    {
                        var roleValue = roles.GetString();

                        if (!string.IsNullOrEmpty(roleValue))
                        {
                            identity.AddClaim(
                              new Claim(options.RoleClaim, roleValue));
                        }
                    }
                }
            }
        }

        return user;
    }
}
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomUserFactory
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

            if (roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = account.AdditionalProperties[identity.RoleClaimType];

                if (rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
                        }
                    }
                    else
                    {
                        identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
                    }
                }
            }
        }

        return user;
    }
}

Nell'app Client registrare la factory nel Program file:

builder.Services.AddApiAuthorization()
    .AddAccountClaimsPrincipalFactory<CustomUserFactory>();

Nell'app Server chiamare AddRoles il Identity generatore, che aggiunge servizi correlati al ruolo.

Nel file Program:

using Microsoft.AspNetCore.Identity;

...

builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

In Startup.cs:

using Microsoft.AspNetCore.Identity;

...

services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Configurare il Identity server

Usare uno degli approcci seguenti:

Opzioni di autorizzazione API

Nell'app Server :

  • Configurare Identity il server per inserire le name attestazioni e role nel token ID e nel token di accesso.
  • Impedire il mapping predefinito per i ruoli nel gestore di token JWT.

Nel file Program:

using System.IdentityModel.Tokens.Jwt;

...

builder.Services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

In Startup.cs:

using System.IdentityModel.Tokens.Jwt;
using System.Linq;

...

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Servizio profili

Nell'app Server creare un'implementazione ProfileService .

ProfileService.cs:

using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;

public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}
using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using System.Threading.Tasks;

public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}

Nell'app Server registrare il servizio profili nel Program file:

using Duende.IdentityServer.Services;

...

builder.Services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Nell'app Server registrare il servizio profili in Startup.ConfigureServices :Startup.cs

using IdentityServer4.Services;

...

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Usare i meccanismi di autorizzazione

Nell'app Client , gli approcci di autorizzazione dei componenti sono funzionali a questo punto. Uno dei meccanismi di autorizzazione nei componenti può usare un ruolo per autorizzare l'utente:

User.Identity.Name viene popolato nell'app Client con il nome utente dell'utente, che in genere è il relativo indirizzo di posta elettronica di accesso.

UserManager e SignInManager

Impostare il tipo di attestazione dell'identificatore utente quando un'app Server richiede:

In Program.cs per ASP.NET Core in .NET 6 o versione successiva:

using System.Security.Claims;

...

builder.Services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

In Startup.ConfigureServices per le versioni di ASP.NET Core precedenti alla 6.0:

using System.Security.Claims;

...

services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

Di seguito WeatherForecastController viene registrato quando UserName viene chiamato il Get metodo .

Nota

L'esempio seguente usa uno spazio dei nomi con ambito file, ovvero una funzionalità C# 10 o successiva (.NET 6 o versione successiva).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using BlazorSample.Server.Models;
using BlazorSample.Shared;

namespace BlazorSample.Server.Controllers;

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly UserManager<ApplicationUser> userManager;

    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", 
        "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger, 
        UserManager<ApplicationUser> userManager)
    {
        this.logger = logger;
        this.userManager = userManager;
    }

    [HttpGet]
    public async Task<IEnumerable<WeatherForecast>> Get()
    {
        var rng = new Random();

        var user = await userManager.GetUserAsync(User);

        if (user != null)
        {
            logger.LogInformation("User.Identity.Name: {UserIdentityName}", user.UserName);
        }

        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

Nell'esempio precedente:

  • Lo Server spazio dei nomi del progetto è BlazorSample.Server.
  • Lo Shared spazio dei nomi del progetto è BlazorSample.Shared.

Host nel servizio app Azure con un dominio e un certificato personalizzati

Le indicazioni seguenti illustrano:

  • Come distribuire un'app ospitata Blazor WebAssembly con Identity Server in app Azure Servizio con un dominio personalizzato.
  • Come creare e usare un certificato TLS per la comunicazione con il protocollo HTTPS con i browser. Anche se le linee guida sono incentrate sull'uso del certificato con un dominio personalizzato, le linee guida sono ugualmente applicabili all'uso di un dominio di app Azure s predefinito, ad esempio contoso.azurewebsites.net.

Per questo scenario di hosting, non usare lo stesso certificato per Identity la chiave di firma del token del server e la comunicazione sicura HTTPS del sito con i browser:

  • L'uso di certificati diversi per questi due requisiti è una buona procedura di sicurezza perché isola le chiavi private per ogni scopo.
  • I certificati TLS per la comunicazione con i browser sono gestiti in modo indipendente senza influire sulla Identity firma del token del server.
  • Quando Azure Key Vault fornisce un certificato a un'app servizio app per l'associazione di dominio personalizzata, Identity il server non può ottenere lo stesso certificato da Azure Key Vault per la firma del token. Anche se la configurazione del Identity server per l'uso dello stesso certificato TLS da un percorso fisico è possibile, l'inserimento dei certificati di sicurezza nel controllo del codice sorgente è una procedura non corretta e deve essere evitata nella maggior parte degli scenari.

Nel materiale sussidiario seguente viene creato un certificato autofirmato in Azure Key Vault esclusivamente per la Identity firma del token del server. La Identity configurazione del server usa il certificato dell'insieme di credenziali delle chiavi tramite l'archivio certificati dell'app>CurrentUserMy. Altri certificati usati per il traffico HTTPS con domini personalizzati vengono creati e configurati separatamente dal certificato di firma del Identity server.

Per configurare un'app, app Azure Service e Azure Key Vault per ospitare con un dominio personalizzato e HTTPS:

  1. Creare un piano servizio app con un livello di piano superiore Basic B1 o superiore. servizio app richiede un Basic B1 livello di servizio o superiore per l'uso di domini personalizzati.

  2. Creare un certificato PFX per la comunicazione sicura del browser (protocollo HTTPS) del sito con un nome comune del nome di dominio completo (FQDN) del sito che l'organizzazione controlla ,ad esempio www.contoso.com. Creare il certificato con:

    • Uso delle chiavi
      • Convalida della firma digitale (digitalSignature)
      • Crittografia delle chiavi (keyEncipherment)
    • Uso di chiavi avanzate/estese
      • Autenticazione client (1.3.6.1.5.5.7.3.2)
      • Autenticazione server (1.3.6.1.5.5.7.3.1)

    Per creare il certificato, usare uno degli approcci seguenti o qualsiasi altro strumento o servizio online appropriato:

    Prendere nota della password, usata in un secondo momento per importare il certificato in Azure Key Vault.

    Per altre informazioni sui certificati di Azure Key Vault, vedere Azure Key Vault: Certificates( Azure Key Vault: Certificates).

  3. Creare un nuovo insieme di credenziali delle chiavi di Azure o usare un insieme di credenziali delle chiavi esistente nella sottoscrizione di Azure.

  4. Nell'area Certificati dell'insieme di credenziali delle chiavi importare il certificato del sito PFX. Registrare l'identificazione personale del certificato, che viene usata nella configurazione dell'app in un secondo momento.

  5. In Azure Key Vault generare un nuovo certificato autofirmato per Identity la firma del token del server. Assegnare al certificato un nome certificato e un oggetto. L'oggetto viene specificato come CN={COMMON NAME}, dove il {COMMON NAME} segnaposto è il nome comune del certificato. Il nome comune può essere qualsiasi stringa alfanumerica. Ad esempio, CN=IdentityServerSigning è un soggetto di certificato valido. In Configurazione dei criteri avanzati dei criteri di rilascio>usare le impostazioni predefinite. Registrare l'identificazione personale del certificato, che viene usata nella configurazione dell'app in un secondo momento.

  6. Passare a app Azure Servizio nel portale di Azure e creare una nuova servizio app con la configurazione seguente:

    • Pubblicare impostato su Code.
    • Stack di runtime impostato sul runtime dell'app.
    • Per SKU e dimensioni, verificare che il livello di servizio app sia Basic B1 o superiore. servizio app richiede un Basic B1 livello di servizio o superiore per l'uso di domini personalizzati.
  7. Dopo aver creato il servizio app, aprire la configurazione dell'app e aggiungere una nuova impostazione dell'applicazione specificando le identificazioni personali del certificato registrate in precedenza. La chiave dell'impostazione dell'app è WEBSITE_LOAD_CERTIFICATES. Separare le identificazioni personali del certificato nel valore dell'impostazione dell'app con una virgola, come illustrato nell'esempio seguente:

    • Chiave: WEBSITE_LOAD_CERTIFICATES
    • Valore: 57443A552A46DB...D55E28D412B943565,29F43A772CB6AF...1D04F0C67F85FB0B1

    Nel portale di Azure salvare le impostazioni dell'app è un processo in due passaggi: Salva l'impostazione WEBSITE_LOAD_CERTIFICATES chiave-valore, quindi seleziona il pulsante Salva nella parte superiore del pannello.

  8. Selezionare le impostazioni TLS/SSL dell'app. Selezionare Certificati chiave privata (pfx).Select Private Key Certificates (.pfx). Usare il processo Di importazione del certificato dell'insieme di credenziali delle chiavi. Usare il processo due volte per importare il certificato del sito per la comunicazione HTTPS e il certificato di firma del token del server autofirmato Identity del sito.

  9. Passare al pannello Domini personalizzati. Nel sito Web del registrar del dominio usare l'indirizzo IP e l'ID di verifica del dominio personalizzato per configurare il dominio. Una configurazione di dominio tipica include:

    • Un record con un host di @ e un valore dell'indirizzo IP del portale di Azure.
    • Record TXT con un host di asuid e il valore dell'ID di verifica generato da Azure e fornito dal portale di Azure.

    Assicurarsi di salvare correttamente le modifiche nel sito Web del registrar del dominio. Alcuni siti Web di registrazione richiedono un processo in due passaggi per salvare i record di dominio: uno o più record vengono salvati singolarmente, seguiti dall'aggiornamento della registrazione del dominio con un pulsante separato.

  10. Tornare al pannello Domini personalizzati nel portale di Azure. Selezionare Aggiungi dominio personalizzato. Selezionare l'opzione Record A. Specificare il dominio e selezionare Convalida. Se i record di dominio sono corretti e propagati in Internet, il portale consente di selezionare il pulsante Aggiungi dominio personalizzato.

    Possono essere necessari alcuni giorni prima che le modifiche alla registrazione del dominio vengano propagate tra i server dei nomi di dominio Internet (DNS) dopo l'elaborazione da parte del registrar di dominio. Se i record di dominio non vengono aggiornati entro tre giorni lavorativi, verificare che i record siano impostati correttamente con il registrar del dominio e contattare il supporto tecnico.

  11. Nel pannello Domini personalizzati lo stato SSL per il dominio è contrassegnato come Not Secure. Selezionare il collegamento Aggiungi associazione . Selezionare il certificato HTTPS del sito dall'insieme di credenziali delle chiavi per l'associazione di dominio personalizzata.

  12. In Visual Studio aprire il file delle impostazioni dell'app del progetto Server (appsettings.json o appsettings.Production.json). Nella configurazione del Identity server aggiungere la sezione seguente Key . Specificare l'oggetto del certificato autofirmato per la Name chiave. Nell'esempio seguente il nome comune del certificato assegnato nell'insieme di credenziali delle chiavi è IdentityServerSigning, che restituisce un oggetto di CN=IdentityServerSigning:

    "IdentityServer": {
    
      ...
    
      "Key": {
        "Type": "Store",
        "StoreName": "My",
        "StoreLocation": "CurrentUser",
        "Name": "CN=IdentityServerSigning"
      }
    },
    
  13. In Visual Studio creare un profilo di pubblicazione del servizio app Azure per il progetto Server. Nella barra dei menu selezionare Build >Publish>New>Azure> app Azure Service (Windows o Linux). Quando Visual Studio è connesso a una sottoscrizione di Azure, è possibile impostare La visualizzazione delle risorse di Azure in base al tipo di risorsa. Passare all'interno dell'elenco app Web per trovare il servizio app per l'app e selezionarlo. Selezionare Fine.

  14. Quando Visual Studio torna alla finestra Pubblica , vengono rilevate automaticamente le dipendenze dell'insieme di credenziali delle chiavi e del servizio di database di SQL Server.

    Non sono necessarie modifiche di configurazione alle impostazioni predefinite per il servizio insieme di credenziali delle chiavi.

    A scopo di test, il database SQLite locale di un'app, configurato per impostazione predefinita dal Blazor modello, può essere distribuito con l'app senza configurazione aggiuntiva. La configurazione di un database diverso per Identity Server in produzione non rientra nell'ambito di questo articolo. Per altre informazioni, vedere le risorse del database nei set di documentazione seguenti:

  15. Selezionare il collegamento Modifica sotto il nome del profilo di distribuzione nella parte superiore della finestra. Modificare l'URL di destinazione con l'URL del dominio personalizzato del sito , ad esempio https://www.contoso.com. Salva le impostazioni.

  16. Pubblica l'app. Visual Studio apre una finestra del browser e richiede il sito nel dominio personalizzato.

La documentazione di Azure contiene dettagli aggiuntivi sull'uso dei servizi di Azure e dei domini personalizzati con l'associazione TLS in servizio app, incluse informazioni sull'uso di record CNAME invece dei record A. Per ulteriori informazioni, vedi le seguenti risorse:

È consigliabile usare una nuova finestra del browser in modalità privata (ad esempio, la modalità InPrivate di Microsoft Edge o la modalità In incognito di Google Chrome) per ogni esecuzione del test dell'app dopo una modifica apportata all'app, alla configurazione dell'app o ai servizi di Azure nella portale di Azure. Il ritardo di un'esecuzione di test precedente può comportare cookiel'autenticazione o l'autorizzazione non riuscite durante il test del sito anche quando la configurazione del sito è corretta. Per altre informazioni su come configurare Visual Studio per aprire una nuova finestra del browser privato per ogni esecuzione di test, vedere la sezione s e i dati del Cookiesito.

Quando servizio app configurazione viene modificata nella portale di Azure, gli aggiornamenti in genere diventano effettivi rapidamente, ma non sono istantanei. In alcuni casi, è necessario attendere un breve periodo per il riavvio di un servizio app per rendere effettiva una modifica della configurazione.

Se si risolve un Identity problema di caricamento del certificato per la firma della chiave del server, eseguire il comando seguente in una shell dei comandi di PowerShell portale di Azure Kudu. Il comando fornisce un elenco di certificati a cui l'app può accedere dall'archivio CurrentUser>My certificati. L'output include oggetti certificato e identificazioni personali utili durante il debug di un'app:

Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList

Risoluzione dei problemi

Registrazione

Per abilitare la registrazione di debug o traccia per Blazor WebAssembly l'autenticazione, vedere la sezione Registrazione dell'autenticazione lato client di ASP.NET Core Blazor con il selettore della versione dell'articolo impostato su ASP.NET Core 7.0 o versione successiva.

Errori comuni

  • Configurazione errata dell'app o Identity del provider (IP)

    Gli errori più comuni sono causati da una configurazione errata. Di seguito sono riportati alcuni esempi:

    • A seconda dei requisiti dello scenario, un'autorità mancante o non corretta, istanza, ID tenant, dominio tenant, ID client o URI di reindirizzamento impedisce a un'app di autenticare i client.
    • Gli ambiti di richiesta non corretti impediscono ai client di accedere agli endpoint DELL'API Web del server.
    • Autorizzazioni DELL'API server non corrette o mancanti impediscono ai client di accedere agli endpoint DELL'API Web del server.
    • L'esecuzione dell'app in una porta diversa da quella configurata nell'URI di reindirizzamento della registrazione dell'app ip. Si noti che non è necessaria una porta per Microsoft Entra ID e un'app in esecuzione in un localhost indirizzo di test di sviluppo, ma la configurazione della porta dell'app e la porta in cui è in esecuzione l'app devono corrispondere per gli indirizzi nonlocalhost .

    Le sezioni di configurazione di questo articolo illustrano esempi della configurazione corretta. Controllare attentamente ogni sezione dell'articolo alla ricerca di errori di configurazione di app e IP.

    Se la configurazione è corretta:

    • Analizzare i log delle applicazioni.

    • Esaminare il traffico di rete tra l'app client e l'app IP o server con gli strumenti di sviluppo del browser. Spesso, un messaggio di errore esatto o un messaggio con un indizio sulla causa del problema viene restituito al client dall'app IP o server dopo aver effettuato una richiesta. Strumenti di sviluppo materiale sussidiario sono disponibili negli articoli seguenti:

    • Per le versioni di Blazor in cui viene usato un JStoken JWT (ON Web Token), decodificare il contenuto del token usato per autenticare un client o accedere a un'API Web del server, a seconda della posizione in cui si verifica il problema. Per altre informazioni, vedere Esaminare il contenuto di un token Web ON (JWT).For more information, see Inspect the content of a JSON Web Token (JWT).

    Il team della documentazione risponde al feedback e ai bug dei documenti negli articoli (aprire un problema dalla sezione Commenti e suggerimenti della pagina ), ma non è in grado di fornire supporto tecnico. Sono disponibili diversi forum di supporto pubblico per facilitare la risoluzione dei problemi di un'app. Consigliamo quanto segue:

    I forum precedenti non sono di proprietà o controllati da Microsoft.

    Per i report sui bug del framework non riservati, non sensibili e non riservati, aprire un problema con l'unità del prodotto ASP.NET Core. Non aprire un problema con l'unità di prodotto fino a quando non hai approfondito la causa di un problema e non puoi risolverlo autonomamente e con l'aiuto della community in un forum di supporto pubblico. L'unità prodotto non è in grado di risolvere i problemi relativi alle singole app interrotte a causa di semplici errori di configurazione o casi d'uso che coinvolgono servizi di terze parti. Se un report è sensibile o riservato in natura o descrive un potenziale difetto di sicurezza nel prodotto che gli utenti malintenzionati possono sfruttare, vedere Segnalazione di problemi di sicurezza e bug (dotnet/aspnetcorerepository GitHub).

  • Client non autorizzato per ME-ID

    info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Autorizzazione non riuscita. Questi requisiti non sono stati soddisfatti: DenyAnonymousAuthorizationRequirement: richiede un utente autenticato.

    Errore di callback di accesso da ME-ID:

    • Errore: unauthorized_client
    • Description (Descrizione): AADB2C90058: The provided application is not configured to allow public clients.

    Per risolvere l'errore:

    1. Nella portale di Azure accedere al manifesto dell'app.
    2. Impostare l'attributo allowPublicClient su null o .true

Cookies e i dati del sito

Cookies e i dati del sito possono persistere tra gli aggiornamenti delle app e interferire con i test e la risoluzione dei problemi. Cancellare quanto segue quando si apportano modifiche al codice dell'app, modifiche all'account utente con il provider o modifiche alla configurazione dell'app del provider:

  • Account di accesso cookieutente
  • App cookies
  • Dati del sito memorizzati nella cache e archiviati

Un approccio per evitare che i dati del sito e di s persistenti cookieinterferiscano con i test e la risoluzione dei problemi consiste nel:

  • Configurare un browser
    • Usare un browser per i test che è possibile configurare per eliminare tutti i cookie dati del sito e ogni volta che il browser viene chiuso.
    • Assicurarsi che il browser venga chiuso manualmente o dall'IDE per qualsiasi modifica apportata alla configurazione dell'app, dell'utente di test o del provider.
  • Usare un comando personalizzato per aprire un browser in modalità InPrivate o In incognito in Visual Studio:
    • Aprire la finestra di dialogo Sfoglia con dal pulsante Esegui di Visual Studio.
    • Seleziona il pulsante Aggiungi.
    • Specificare il percorso del browser nel campo Programma . I percorsi eseguibili seguenti sono percorsi di installazione tipici per Windows 10. Se il browser è installato in un percorso diverso o non si usa Windows 10, specificare il percorso dell'eseguibile del browser.
      • Microsoft Edge: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox: C:\Program Files\Mozilla Firefox\firefox.exe
    • Nel campo Argomenti specificare l'opzione della riga di comando usata dal browser per aprire in modalità InPrivate o Incognito. Alcuni browser richiedono l'URL dell'app.
      • Microsoft Edge: usare -inprivate.
      • Google Chrome: usare --incognito --new-window {URL}, dove il segnaposto {URL} è l'URL da aprire (ad esempio, https://localhost:5001).
      • Mozilla Firefox: usare -private -url {URL}, dove il segnaposto {URL} è l'URL da aprire (ad esempio, https://localhost:5001).
    • Specificare un nome nel campo Nome descrittivo. Ad esempio, Firefox Auth Testing.
    • Selezionare il pulsante OK.
    • Per evitare di dover selezionare il profilo del browser per ogni iterazione di test con un'app, impostare il profilo come predefinito con il pulsante Imposta come predefinito .
    • Assicurarsi che il browser sia chiuso dall'IDE per qualsiasi modifica apportata all'app, all'utente di test o alla configurazione del provider.

Aggiornamenti di app

Un'app funzionante potrebbe non riuscire immediatamente dopo l'aggiornamento di .NET Core SDK nel computer di sviluppo o la modifica delle versioni dei pacchetti all'interno dell'app. In alcuni casi i pacchetti incoerenti possono interrompere un'app quando si eseguono aggiornamenti principali. La maggior parte di questi problemi può essere risolta attenendosi alle istruzioni seguenti:

  1. Cancellare le cache dei pacchetti NuGet del sistema locale eseguendo dotnet nuget locals all --clear da una shell dei comandi.
  2. Eliminare le cartelle e obj del bin progetto.
  3. Ripristinare e ricompilare il progetto.
  4. Eliminare tutti i file nella cartella di distribuzione nel server prima di ridistribuire l'app.

Nota

L'uso di versioni del pacchetto incompatibili con il framework di destinazione dell'app non è supportato. Per informazioni su un pacchetto, usare La raccolta NuGet o Esplora pacchetti FuGet.

Eseguire l'app Server

Durante il test e la risoluzione dei problemi di una soluzione ospitataBlazor WebAssembly, assicurarsi di eseguire l'app dal Server progetto.

Esaminare l'utente

Il componente seguente User può essere usato direttamente nelle app o funge da base per un'ulteriore personalizzazione.

User.razor:

@page "/user"
@attribute [Authorize]
@using System.Text.Json
@using System.Security.Claims
@inject IAccessTokenProvider AuthorizationService

<h1>@AuthenticatedUser?.Identity?.Name</h1>

<h2>Claims</h2>

@foreach (var claim in AuthenticatedUser?.Claims ?? Array.Empty<Claim>())
{
    <p class="claim">@(claim.Type): @claim.Value</p>
}

<h2>Access token</h2>

<p id="access-token">@AccessToken?.Value</p>

<h2>Access token claims</h2>

@foreach (var claim in GetAccessTokenClaims())
{
    <p>@(claim.Key): @claim.Value.ToString()</p>
}

@if (AccessToken != null)
{
    <h2>Access token expires</h2>

    <p>Current time: <span id="current-time">@DateTimeOffset.Now</span></p>
    <p id="access-token-expires">@AccessToken.Expires</p>

    <h2>Access token granted scopes (as reported by the API)</h2>

    @foreach (var scope in AccessToken.GrantedScopes)
    {
        <p>Scope: @scope</p>
    }
}

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

    public ClaimsPrincipal AuthenticatedUser { get; set; }
    public AccessToken AccessToken { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        var state = await AuthenticationState;
        var accessTokenResult = await AuthorizationService.RequestAccessToken();

        if (!accessTokenResult.TryGetToken(out var token))
        {
            throw new InvalidOperationException(
                "Failed to provision the access token.");
        }

        AccessToken = token;

        AuthenticatedUser = state.User;
    }

    protected IDictionary<string, object> GetAccessTokenClaims()
    {
        if (AccessToken == null)
        {
            return new Dictionary<string, object>();
        }

        // header.payload.signature
        var payload = AccessToken.Value.Split(".")[1];
        var base64Payload = payload.Replace('-', '+').Replace('_', '/')
            .PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=');

        return JsonSerializer.Deserialize<IDictionary<string, object>>(
            Convert.FromBase64String(base64Payload));
    }
}

Esaminare il contenuto di un JStoken WEB ON (JWT)

Per decodificare un JStoken JWT (ON Web Token), usare lo strumento di jwt.ms Microsoft. I valori nell'interfaccia utente non lasciano mai il browser.

JWT codificato di esempio (abbreviato per la visualizzazione):

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1j ... bQdHBHGcQQRbW7Wmo6SWYG4V_bU55Ug_PW4pLPr20tTS8Ct7_uwy9DWrzCMzpD-EiwT5IjXwlGX3IXVjHIlX50IVIydBoPQtadvT7saKo1G5Jmutgq41o-dmz6-yBMKV2_nXA25Q

Esempio JWT decodificato dallo strumento per un'app che esegue l'autenticazione in Azure AAD B2C:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk"
}.{
  "exp": 1610059429,
  "nbf": 1610055829,
  "ver": "1.0",
  "iss": "https://mysiteb2c.b2clogin.com/5cc15ea8-a296-4aa3-97e4-226dcc9ad298/v2.0/",
  "sub": "5ee963fb-24d6-4d72-a1b6-889c6e2c7438",
  "aud": "70bde375-fce3-4b82-984a-b247d823a03f",
  "nonce": "b2641f54-8dc4-42ca-97ea-7f12ff4af871",
  "iat": 1610055829,
  "auth_time": 1610055822,
  "idp": "idp.com",
  "tfp": "B2C_1_signupsignin"
}.[Signature]

Risorse aggiuntive