Share via


Autenticazione e autorizzazione

Suggerimento

Questo contenuto è un estratto dell'eBook, Enterprise Application Patterns Using .NETMAUI, disponibile in .NET Docs o come PDF scaricabile gratuitamente che può essere letto offline.

Enterprise Application Patterns Using .NET MAUI eBook cover thumbnail.

L'autenticazione è il processo di recupero delle credenziali di identificazione, ad esempio nome e password da un utente e convalida di tali credenziali in base a un'autorità. L'entità che ha inviato le credenziali è considerata un'identità autenticata se le credenziali sono valide. Una volta stabilita un'identità, un processo di autorizzazione determina se tale identità ha accesso a una determinata risorsa.

Esistono molti approcci per l'integrazione dell'autenticazione e dell'autorizzazione in un'app .NET MAUI che comunica con un'applicazione Web ASP.NET, tra cui l'uso di ASP.NET Core Identity, provider di autenticazione esterni come Microsoft, Google, Facebook o Twitter e il middleware di autenticazione. L'app multipiattaforma eShopOnContainers esegue l'autenticazione e l'autorizzazione con un microservizio di identità in contenitori che usa IdentityServer 4. L'app richiede token di sicurezza da IdentityServer per autenticare un utente o accedere a una risorsa. Per consentire a IdentityServer di emettere token per conto di un utente, l'utente deve accedere a IdentityServer. IdentityServer, tuttavia, non fornisce un'interfaccia utente o un database per l'autenticazione. Pertanto, nell'applicazione di riferimento eShopOnContainers, ASP.NET Core Identity viene usato a questo scopo.

Autenticazione

L'autenticazione è necessaria quando un'applicazione deve conoscere l'identità dell'utente corrente. ASP.NET il meccanismo principale di Core per identificare gli utenti è il sistema di appartenenza ASP.NET Core Identity, che archivia le informazioni utente in un archivio dati configurato dallo sviluppatore. In genere, questo archivio dati sarà un archivio EntityFramework, anche se gli archivi personalizzati o i pacchetti di terze parti possono essere usati per archiviare le informazioni sull'identità in Archiviazione di Azure, DocumentDB o in altre posizioni.

Per gli scenari di autenticazione che usano un archivio dati utente locale e rendono persistenti le informazioni sull'identità tra le richieste tramite cookie (come in genere avviene per le applicazioni Web di ASP.NET), ASP.NET Core Identity è una soluzione adatta. Tuttavia, i cookie non sono sempre un mezzo naturale per rendere persistenti e trasmettere i dati. Ad esempio, un'applicazione Web ASP.NET Core che espone gli endpoint RESTful a cui si accede da un'app in genere dovrà usare l'autenticazione del token di connessione perché i cookie non possono essere usati in questo scenario. Tuttavia, i token di connessione possono essere facilmente recuperati e inclusi nell'intestazione di autorizzazione delle richieste Web effettuate dall'app.

Emissione di token di connessione con IdentityServer 4

IdentityServer 4 è un framework OpenID Connect open source e OAuth 2.0 per ASP.NET Core, che può essere usato per molti scenari di autenticazione e autorizzazione, inclusi l'emissione di token di sicurezza per gli utenti locali ASP.NET Core Identity.

Nota

OpenID Connect e OAuth 2.0 sono molto simili, pur avendo responsabilità diverse.

OpenID Connect è un livello di autenticazione sopra il protocollo OAuth 2.0. OAuth 2 è un protocollo che consente alle applicazioni di richiedere token di accesso da un servizio token di sicurezza e usarli per comunicare con le API. Questa delega riduce la complessità sia nelle applicazioni client che nelle API, perché l'autenticazione e l'autorizzazione possono essere centralizzate.

OpenID Connect e OAuth 2.0 combinano i due principali problemi di sicurezza relativi all'autenticazione e all'accesso alle API, e IdentityServer 4 è un'implementazione di questi protocolli.

Nelle applicazioni che usano la comunicazione diretta da client a microservizi, ad esempio l'applicazione di riferimento eShopOnContainers, un microservizio di autenticazione dedicato che funge da servizio token di sicurezza (STS) può essere usato per autenticare gli utenti, come illustrato nel diagramma seguente. Per altre informazioni sulla comunicazione diretta da client a microservizi, vedere Microservizi.

Authentication by a dedicated authentication microservice.

L'app multipiattaforma eShopOnContainers comunica con il microservizio identity, che usa IdentityServer 4 per eseguire l'autenticazione e il controllo di accesso per le API. Di conseguenza, l'app multipiattaforma richiede token da IdentityServer, per l'autenticazione di un utente o per l'accesso a una risorsa:

  • L'autenticazione degli utenti con IdentityServer viene ottenuta dall'app multipiattaforma che richiede un token di identità, che rappresenta il risultato di un processo di autenticazione. Contiene almeno un identificatore per l'utente e informazioni su come e quando l'utente viene autenticato. Può anche includere dati di identità aggiuntivi.
  • L'accesso a una risorsa con IdentityServer viene ottenuto dall'app multipiattaforma che richiede un token di accesso, che consente l'accesso a una risorsa API. I client richiedono token di accesso e li inoltrano all'API. I token di accesso contengono informazioni sul client e sull'utente, se presenti. Le API usano quindi tali informazioni per autorizzare l'accesso ai dati.

Nota

Un client deve essere registrato con IdentityServer prima di poter richiedere correttamente i token. Per altre informazioni sull'aggiunta di client, vedere Definizione dei client.

Aggiunta di IdentityServer a un'applicazione Web

Affinché un'applicazione Web ASP.NET Core usi IdentityServer 4, è necessario aggiungerla alla soluzione Visual Studio dell'applicazione Web. Per altre informazioni, vedere Installazione e panoramica nella documentazione di IdentityServer. Dopo aver incluso IdentityServer nella soluzione Visual Studio dell'applicazione Web, è necessario aggiungerlo alla pipeline di elaborazione delle richieste HTTP per gestire le richieste agli endpoint OpenID Connect e OAuth 2.0. Questo risultato viene ottenuto nel metodo Configure nella classe Startup dell'applicazione Web, come illustrato nell'esempio di codice seguente:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseIdentity();
}

L'ordine è importante nella pipeline di elaborazione delle richieste HTTP dell'applicazione Web. Pertanto, IdentityServer deve essere aggiunto alla pipeline prima del framework dell'interfaccia utente che implementa la schermata di accesso.

Configurazione di IdentityServer

IdentityServer deve essere configurato nel metodo ConfigureServices nella classe Startup dell'applicazione Web chiamando il metodo services.AddIdentityServer, come illustrato nell'esempio di codice seguente dell'applicazione di riferimento eShopOnContainers:

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddIdentityServer(x => x.IssuerUri = "null")
        .AddSigningCredential(Certificate.Get())
        .AddAspNetIdentity<ApplicationUser>()
        .AddConfigurationStore(builder =>
            builder.UseSqlServer(connectionString, options =>
                options.MigrationsAssembly(migrationsAssembly)))
        .AddOperationalStore(builder =>
            builder.UseSqlServer(connectionString, options =>
                options.MigrationsAssembly(migrationsAssembly)))
        .Services.AddTransient<IProfileService, ProfileService>();
}

Dopo aver chiamato il metodo services.AddIdentityServer, vengono chiamate API Fluent aggiuntive per configurare quanto segue:

  • Credenziali usate per la firma.
  • Risorse API e identità a cui gli utenti potrebbero richiedere l'accesso.
  • Client che si connetteranno ai token di richiesta.
  • Identità di ASP.NET Core.

Suggerimento

Caricare dinamicamente la configurazione di IdentityServer 4. Le API di IdentityServer 4 consentono di configurare IdentityServer da un elenco in memoria di oggetti di configurazione. Nell'applicazione di riferimento eShopOnContainers, queste raccolte in memoria sono hardcoded nell'applicazione. Tuttavia, negli scenari di produzione possono essere caricati dinamicamente da un file di configurazione o da un database.

Per informazioni sulla configurazione di IdentityServer per l'uso di Identità di ASP.NET Core, vedere Uso di Identità di ASP.NET Core nella documentazione di IdentityServer.

Configurazione delle risorse API

Quando si configurano le risorse API, il metodo AddInMemoryApiResources prevede una raccolta IEnumerable<ApiResource>. L'esempio di codice seguente illustra il metodo GetApis che fornisce questa raccolta nell'applicazione di riferimento eShopOnContainers:

public static IEnumerable<ApiResource> GetApis()
{
    return new List<ApiResource>
    {
        new ApiResource("orders", "Orders Service"),
        new ApiResource("basket", "Basket Service")
    };
}

Questo metodo specifica che IdentityServer deve proteggere gli ordini e le API carrello. Pertanto, i token di accesso gestiti da IdentityServer saranno necessari quando si effettuano chiamate a queste API. Per altre informazioni sul tipo di ApiResource, vedere risorsa API nella documentazione di IdentityServer 4.

Configurazione delle risorse di identità

Quando si configurano le risorse di identità, il metodo AddInMemoryIdentityResources prevede una raccolta IEnumerable<IdentityResource>. Le risorse di identità sono dati come ID utente, nome o indirizzo di posta elettronica. A ogni risorsa di identità è associato un nome univoco e possono essere assegnati tipi di attestazione arbitrari, che verranno inclusi nel token di identità per l'utente. L'esempio di codice seguente illustra il metodo GetResources che fornisce questa raccolta nell'applicazione di riferimento eShopOnContainers:

public static IEnumerable<IdentityResource> GetResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };
}

La specifica OpenID Connect specifica alcune risorse di identità standard. Il requisito minimo è che il supporto venga fornito per l'emissione di un ID univoco per gli utenti. Questa operazione viene ottenuta esponendo la risorsa di identità IdentityResources.OpenId.

Nota

La classe IdentityResources supporta tutti gli ambiti definiti nella specifica OpenID Connect (openid, email, profilo, telefono e indirizzo).

IdentityServer supporta anche la definizione di risorse di identità personalizzate. Per altre informazioni, vedere Definizione delle risorse di identità personalizzate nella documentazione di IdentityServer. Per altre informazioni sul tipo IdentityResource, vedere Identity Resource nella documentazione di IdentityServer 4.

Configurazione dei client

I client sono applicazioni che possono richiedere token da IdentityServer. In genere, le impostazioni seguenti devono essere definite per ogni client come minimo:

  • ID client univoco.
  • Interazioni consentite con il servizio token (noto come tipo di concessione).
  • Posizione in cui vengono inviati i token di identità e di accesso (noto come URI di reindirizzamento).
  • Elenco di risorse a cui è consentito l'accesso al client (noto come ambiti).

Quando si configurano i client, il metodo AddInMemoryClients prevede una raccolta IEnumerable<Client>. L'esempio di codice seguente illustra la configurazione per l'app multipiattaforma eShopOnContainers nel metodo GetClients che fornisce questa raccolta nell'applicazione di riferimento eShopOnContainers:

public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl)
{
    return new List<Client>
    {
        // Omitted for brevity
        new Client
        {
            ClientId = "xamarin",
            ClientName = "eShop Xamarin OpenId Client",
            AllowedGrantTypes = GrantTypes.Hybrid,
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            RedirectUris = { clientsUrl["Xamarin"] },
            RequireConsent = false,
            RequirePkce = true,
            PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
            AllowedCorsOrigins = { "http://eshopxamarin" },
            AllowedScopes = new List<string>
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.OfflineAccess,
                "orders",
                "basket"
            },
            AllowOfflineAccess = true,
            AllowAccessTokensViaBrowser = true
        },
    };
}

Questa configurazione specifica i dati per le proprietà seguenti:

Proprietà Descrizione
ClientId ID univoco per il client.
ClientName Nome visualizzato del client, usato per la registrazione e la schermata di consenso.
AllowedGrantTypes Specifica il modo in cui un client vuole interagire con IdentityServer. Per altre informazioni, vedere Configurazione del flusso di autenticazione.
ClientSecrets Specifica le credenziali del segreto client usate quando si richiedono token dall'endpoint del token.
RedirectUris Specifica gli URI consentiti a cui restituire token o codici di autorizzazione.
RequireConsent Specifica se è necessaria una schermata di consenso.
RequirePkce Specifica se i client che usano un codice di autorizzazione devono inviare una chiave di prova.
PostLogoutRedirectUris Specifica gli URI consentiti da reindirizzare a dopo la disconnessione.
AllowedCorsOrigins Specifica l'origine del client in modo che IdentityServer possa consentire chiamate tra le origini dall'origine.
AllowedScopes Specifica le risorse a cui il client ha accesso. Per impostazione predefinita, un client non ha accesso ad alcuna risorsa.
AllowOfflineAccess Specifica se il client può richiedere token di aggiornamento.

Configurazione del flusso di autenticazione

Il flusso di autenticazione tra un client e IdentityServer può essere configurato specificando i tipi di concessione nella proprietà Client.AllowedGrantTypes. Le specifiche OpenID Connect e OAuth 2.0 definiscono diversi flussi di autenticazione, tra cui:

Flusso di autenticazione Descrizione
Implicito Questo flusso è ottimizzato per le applicazioni basate su browser e deve essere usato solo per l'autenticazione dell’utente, o per le richieste di autenticazione e token di accesso. Tutti i token vengono trasmessi tramite il browser e pertanto non sono consentite funzionalità avanzate come i token di aggiornamento.
Codice di autorizzazione Questo flusso offre la possibilità di recuperare i token in un canale back, anziché il canale anteriore del browser, supportando anche l'autenticazione client.
Ibrido Questo flusso è una combinazione dei tipi di concessione del codice implicito e di autorizzazione. Il token di identità viene trasmesso tramite il canale del browser e contiene la risposta del protocollo firmata e altri artefatti, ad esempio il codice di autorizzazione. Dopo aver convalidato correttamente la risposta, il canale back deve essere usato per recuperare l'accesso e il token di aggiornamento.

Suggerimento

Prendere in considerazione l'uso del flusso di autenticazione ibrida. Il flusso di autenticazione ibrida riduce una serie di attacchi che si applicano al canale del browser ed è il flusso consigliato per le applicazioni native che vogliono recuperare i token di accesso (ed eventualmente aggiornare i token).

Per altre informazioni sui flussi di autenticazione, vedere Concedere tipi nella documentazione di IdentityServer 4.

Esecuzione dell'autenticazione

Per consentire a IdentityServer di emettere token per conto di un utente, l'utente deve accedere a IdentityServer. IdentityServer, tuttavia, non fornisce un'interfaccia utente o un database per l'autenticazione. Pertanto, nell'applicazione di riferimento eShopOnContainers, ASP.NET Core Identity viene usato a questo scopo.

L'app multipiattaforma eShopOnContainers esegue l'autenticazione con IdentityServer con il flusso di autenticazione ibrida, illustrato nel diagramma seguente.

High-level overview of the sign in process.

Viene effettuata una richiesta di accesso a <base endpoint>:5105/connect/authorize. Dopo l'autenticazione riuscita, IdentityServer restituisce una risposta di autenticazione contenente un codice di autorizzazione e un token di identità. Il codice di autorizzazione viene inviato a <base endpoint>:5105/connect/token, che risponde con token di accesso, identità e aggiornamento.

L'app multipiattaforma eShopOnContainers si disconnette da IdentityServer inviando una richiesta a <base endpoint>:5105/connect/endsession con parametri aggiuntivi. Dopo la disconnessione, IdentityServer risponde inviando un URI di reindirizzamento post-disconnessione all'app multipiattaforma. Il diagramma seguente illustra questo processo.

High-level overview of the sign out process.

Nell'app multipiattaforma eShopOnContainers la comunicazione con IdentityServer viene eseguita dalla classe IdentityService, che implementa l'interfaccia IIdentityService. Questa interfaccia specifica che la classe di implementazione deve fornire metodi CreateAuthorizationRequest, CreateLogoutRequest e GetTokenAsync.

Accesso

Quando l'utente tocca il pulsante LOGIN su LoginView, viene eseguita l’oggetto SignInCommand nella classe LoginViewModel, che a sua volta esegue il metodo SignInAsync. L'esempio di codice seguente illustra il metodo:

private async Task SignInAsync()
{
    await IsBusyFor(
        async () =>
        {
            LoginUrl = _identityService.CreateAuthorizationRequest();

            IsValid = true;
            IsLogin = true;
        });
}

Questo metodo richiama il metodo CreateAuthorizationRequest nella classe IdentityService, come illustrato nell'esempio di codice seguente:

public string CreateAuthorizationRequest()
{
    // Create URI to authorization endpoint
    var authorizeRequest = new AuthorizeRequest(GlobalSetting.Instance.IdentityEndpoint);
    // Dictionary with values for the authorize request
    var dic = new Dictionary<string, string>();
    dic.Add("client_id", GlobalSetting.Instance.ClientId);
    dic.Add("client_secret", GlobalSetting.Instance.ClientSecret); 
    dic.Add("response_type", "code id_token");
    dic.Add("scope", "openid profile basket orders locations marketing offline_access");
    dic.Add("redirect_uri", GlobalSetting.Instance.IdentityCallback);
    dic.Add("nonce", Guid.NewGuid().ToString("N"));
    dic.Add("code_challenge", CreateCodeChallenge());
    dic.Add("code_challenge_method", "S256");
    // Add CSRF token to protect against cross-site request forgery attacks.
    var currentCSRFToken = Guid.NewGuid().ToString("N");
    dic.Add("state", currentCSRFToken);
    
    var authorizeUri = authorizeRequest.Create(dic); 
    return authorizeUri;
}

Questo metodo crea l'URI per l'endpoint di autorizzazione identityServer con i parametri necessari. L'endpoint di autorizzazione si trova a /connect/authorize sulla porta 5105 dell'endpoint di base esposto come impostazione utente. Per altre informazioni sulle impostazioni utente, vedere Gestione della configurazione.

Nota

La superficie di attacco dell'app multipiattaforma eShopOnContainers viene ridotta implementando l'estensione Proof Key for Code Exchange (PKCE) in OAuth. PKCE protegge il codice di autorizzazione dall'uso se viene intercettato. Questo risultato viene ottenuto dal client generando un verificatore segreto, il cui hash viene passato nella richiesta di autorizzazione e che viene presentato senza hash durante il riscatto del codice di autorizzazione. Per altre informazioni su PKCE, vedere Proof Key for Code Exchange by OAuth Public Clients (Chiave di prova per Exchange di client pubblici OAuth) nel sito Web Internet Engineering Task Force.

L'URI restituito viene archiviato nella proprietà LoginUrl della classe LoginViewModel. Quando la proprietà IsLogin diventa true, l’ggetto WebView in LoginView diventa visibile. I dati WebView associano la proprietà Source alla proprietà LoginUrl della classe LoginViewModel e una richiesta di accesso a IdentityServer quando la proprietà LoginUrl è impostata sull'endpoint di autorizzazione di IdentityServer. Quando IdentityServer riceve questa richiesta e l'utente non è autenticato, WebView verrà reindirizzato alla pagina di accesso configurata illustrata nell'immagine seguente.

Login page displayed by the WebView.

Al termine dell'accesso, WebView verrà reindirizzato a un URI restituito. Questa navigazione WebView causerà l'esecuzione del metodo NavigateAsync nella classe LoginViewModel, come illustrato nell'esempio di codice seguente:

private async Task NavigateAsync(string url)
{
    var unescapedUrl = System.Net.WebUtility.UrlDecode(url);

    if (unescapedUrl.Equals(GlobalSetting.Instance.LogoutCallback, StringComparison.OrdinalIgnoreCase))
    {
        _settingsService.AuthAccessToken = string.Empty;
        _settingsService.AuthIdToken = string.Empty;
        IsLogin = false;
        LoginUrl = _identityService.CreateAuthorizationRequest();
    }
    else if (unescapedUrl.Contains(GlobalSetting.Instance.Callback, StringComparison.OrdinalIgnoreCase))
    {
        var authResponse = new AuthorizeResponse(url);
        if (!string.IsNullOrWhiteSpace(authResponse.Code))
        {
            var userToken = await _identityService.GetTokenAsync(authResponse.Code);
            string accessToken = userToken.AccessToken;

            if (!string.IsNullOrWhiteSpace(accessToken))
            {
                _settingsService.AuthAccessToken = accessToken;
                _settingsService.AuthIdToken = authResponse.IdentityToken;
                await NavigationService.NavigateToAsync("//Main/Catalog");
            }
        }
    }
}

Questo metodo analizza la risposta di autenticazione contenuta nell'URI restituito e, purché sia presente un codice di autorizzazione valido, effettua una richiesta all'endpoint token di IdentityServer, passando il codice di autorizzazione, il classificatore segreto PKCE e altri parametri obbligatori. L'endpoint del token si trova a /connect/token sulla porta 5105 dell'endpoint di base esposto come impostazione utente. Per altre informazioni sulle impostazioni utente, vedere Gestione della configurazione).

Suggerimento

Assicurarsi di convalidare gli URI restituiti. Anche se l'app multipiattaforma eShopOnContainers non convalida l'URI restituito, la procedura consigliata consiste nel verificare che l'URI restituito faccia riferimento a una posizione nota per evitare attacchi di reindirizzamento aperto.

Se l'endpoint del token riceve un codice di autorizzazione valido e un classificatore segreto PKCE, risponde con un token di accesso, un token di identità e un token di aggiornamento. Il token di accesso (che consente l'accesso alle risorse API) e il token di identità vengono archiviati come impostazioni dell'applicazione, e viene eseguita la navigazione nella pagina. Pertanto, l'effetto complessivo nell'app multipiattaforma eShopOnContainers è questo: a condizione che gli utenti siano in grado di eseguire correttamente l'autenticazione con IdentityServer, vengono spostati alla route //Main/Catalog, ovvero un oggetto TabbedPage che visualizza CatalogView come scheda selezionata.

Per informazioni sullo spostamento tra le pagine, vedere Navigazione. Per informazioni sulla modalità di esecuzione di un metodo del modello di visualizzazione, vedere Richiamo dello spostamento tramite comportamenti. Per informazioni sulle impostazioni dell'applicazione, vedere Gestione della configurazione.

Nota

EShopOnContainers consente anche un accesso fittizio quando l'app è configurata per l'uso di servizi fittizi in SettingsView. In questa modalità, l'app non comunica con IdentityServer, consentendo invece all'utente di accedere usando le credenziali.

Disconnessione

Quando l'utente tocca il pulsante LOG OUT in ProfileView, viene eseguito l’oggetto LogoutCommand nella classe ProfileViewModel, che esegue il metodo LogoutAsync. Questo metodo esegue lo spostamento di pagina alla pagina LoginView, passando un'istanza LogoutParameter impostata su true come parametro.

Quando viene creata e navigata una vista, viene eseguito il metodo InitializeAsync del modello di visualizzazione associato alla visualizzazione, che esegue quindi il metodo Logout della classe LoginViewModel, illustrato nell'esempio di codice seguente:

private void Logout()
{
    var authIdToken = Settings.AuthIdToken;
    var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);

    if (!string.IsNullOrEmpty(logoutRequest))
    {
        // Logout
        LoginUrl = logoutRequest;
    }
    
    // Omitted for brevity
}

Questo metodo richiama il metodo CreateLogoutRequest nella classe IdentityService, passando il token di identità recuperato dalle impostazioni dell'applicazione come parametro. Per altre informazioni sulle impostazioni dell'applicazione, vedere Gestione della configurazione. L'esempio di codice seguente illustra il metodo CreateLogoutRequest:

public string CreateLogoutRequest(string token)
{
    // Omitted for brevity

    var (endpoint, callback) =
        (GlobalSetting.Instance.LogoutEndpoint, GlobalSetting.Instance.LogoutCallback);

    return $"{endpoint}?id_token_hint={token}&post_logout_redirect_uri={callback}";
}

Questo metodo crea l'URI per l'endpoint di sessione finale di IdentityServer con i parametri necessari. L'endpoint della sessione finale si trova a /connect/endsession sulla porta 5105 dell'endpoint di base esposto come impostazione utente.

L'URI restituito viene archiviato nella proprietà LoginUrl della classe LoginViewModel. Mentre la proprietà IsLogin è true, WebView in LoginView è visibile. I dati WebView associano la proprietà Source alla proprietà LoginUrl della classe LoginViewModel, effettuando una richiesta di disconnessione a IdentityServer quando la proprietà LoginUrl è impostata sull'endpoint della sessione finale di IdentityServer. La disconnessione si verifica quando IdentityServer riceve questa richiesta, purché l'utente sia connesso. L'autenticazione viene rilevata con un cookie gestito dal middleware di autenticazione dei cookie da ASP.NET Core. Di conseguenza, la disconnessione da IdentityServer rimuove il cookie di autenticazione e invia un URI di reindirizzamento post-disconnessione al client.

WebView verrà reindirizzato all'URI di reindirizzamento post-disconnessione nell'app multipiattaforma. Questa navigazione WebView causerà l'esecuzione del metodo NavigateAsync nella classe LoginViewModel, come illustrato nell'esempio di codice seguente:

private async Task NavigateAsync(string url)
{
    var unescapedUrl = System.Net.WebUtility.UrlDecode(url);

    if (unescapedUrl.Equals(GlobalSetting.Instance.LogoutCallback, StringComparison.OrdinalIgnoreCase))
    {
        _settingsService.AuthAccessToken = string.Empty;
        _settingsService.AuthIdToken = string.Empty;
        IsLogin = false;
        LoginUrl = _identityService.CreateAuthorizationRequest();
    }
    
    // Omitted for brevity
}

Questo metodo cancella sia il token di identità che il token di accesso dalle impostazioni dell'applicazione. Imposta la proprietà IsLogin su false, causando la visualizzazione invisibile di WebView nella pagina LoginView. Infine, la proprietà LoginUrl viene impostata sull'URI dell'endpoint di autorizzazione identityServer, con i parametri obbligatori, in preparazione alla successiva esecuzione dell'accesso da parte dell'utente.

Per informazioni sullo spostamento tra le pagine, vedere Navigazione. Per informazioni su come la navigazione WebView determina l'esecuzione di un metodo del modello di visualizzazione, vedere Richiamare la navigazione tramite comportamenti. Per informazioni sulle impostazioni dell'applicazione, vedere Gestione della configurazione.

Nota

EShopOnContainers consente anche una disconnessione fittizia quando l'app è configurata per l'uso di servizi fittizi in SettingsView. In questa modalità, l'app non comunica con IdentityServer e cancella invece i token archiviati dalle impostazioni dell'applicazione.

Autorizzazione

Dopo l'autenticazione, ASP.NET API Web core spesso devono autorizzare l'accesso, consentendo a un servizio di rendere disponibili API ad alcuni utenti autenticati, ma non a tutti.

È possibile limitare l'accesso a una route ASP.NET Core applicando un attributo Authorize a un controller o a un'azione, che limita l'accesso al controller o all'azione agli utenti autenticati, come illustrato nell'esempio di codice seguente:

[Authorize]
public sealed class BasketController : Controller
{
    // Omitted for brevity
}

Se un utente non autorizzato tenta di accedere a un controller o a un'azione contrassegnata con l'attributo Authorize, il framework API restituisce un codice di stato HTTP 401 (unauthorized).

Nota

I parametri possono essere specificati nell'attributo Authorize per limitare un'API a utenti specifici. Per altre informazioni, vedere ASP.NET Core Docs: Authorization(Documentazione principale: autorizzazione).

IdentityServer può essere integrato nel flusso di lavoro di autorizzazione in modo che i token di accesso forniscano l'autorizzazione di controllo. Questo approccio è illustrato nel diagramma seguente.

Authorization by access token.

L'app multipiattaforma eShopOnContainers comunica con il microservizio identità e richiede un token di accesso come parte del processo di autenticazione. Il token di accesso viene quindi inoltrato alle API esposte dai microservizi di ordinamento e carrello come parte delle richieste di accesso. I token di accesso contengono informazioni sul client e sull'utente. Le API usano quindi tali informazioni per autorizzare l'accesso ai dati. Per informazioni su come configurare IdentityServer per proteggere le API, vedere Configurazione delle risorse API.

Configurazione di IdentityServer per eseguire l'autorizzazione

Per eseguire l'autorizzazione con IdentityServer, è necessario aggiungere il middleware di autorizzazione alla pipeline di richiesta HTTP dell'applicazione Web. Il middleware viene aggiunto nel metodo ConfigureAuth nella classe Startup dell'applicazione Web, richiamata dal metodo Configure e illustrata nell'esempio di codice seguente dell'applicazione di riferimento eShopOnContainers:

protected virtual void ConfigureAuth(IApplicationBuilder app)
{
    var identityUrl = Configuration.GetValue<string>("IdentityUrl");
    app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
    {
        Authority = identityUrl.ToString(),
        ScopeName = "basket",
        RequireHttpsMetadata = false,
    });
}

Questo metodo garantisce che l'API sia accessibile solo con un token di accesso valido. Il middleware convalida il token in ingresso per assicurarsi che venga inviato da un'autorità di certificazione attendibile e convalida che il token sia valido per essere usato con l'API che la riceve. Pertanto, l'esplorazione del controller di ordinamento o carrello restituirà un codice di stato HTTP 401 (unauthorized), a indicare che è necessario un token di accesso.

Nota

Il middleware di autorizzazione di IdentityServer deve essere aggiunto alla pipeline di richiesta HTTP dell'applicazione Web prima di aggiungere MVC con app.UseMvc() o app.UseMvcWithDefaultRoute().

Effettuare richieste di accesso alle API

Quando si effettuano richieste ai microservizi di ordinamento e carrello, il token di accesso ottenuto da IdentityServer durante il processo di autenticazione deve essere incluso nella richiesta, come illustrato nell'esempio di codice seguente:

var authToken = Settings.AuthAccessToken;
Order = await _ordersService.GetOrderAsync(order.OrderNumber, authToken);

Il token di accesso viene archiviato come impostazione dell'applicazione e viene recuperato dalla risorsa di archiviazione specifica della piattaforma e incluso nella chiamata al metodo GetOrderAsync nella classe OrderService.

Analogamente, il token di accesso deve essere incluso quando si inviano dati a un'API protetta IdentityServer, come illustrato nell'esempio di codice seguente:

var authToken = Settings.AuthAccessToken;
await _basketService.UpdateBasketAsync(
    new CustomerBasket
    {
        BuyerId = userInfo.UserId, 
        Items = BasketItems.ToList()
    }, 
    authToken);

Il token di accesso viene recuperato dalle impostazioni e incluso nella chiamata al metodo UpdateBasketAsync nella classe BasketService.

La classe RequestProvider nell'app multipiattaforma eShopOnContainers usa la classe HttpClient per inviare richieste alle API RESTful esposte dall'applicazione di riferimento eShopOnContainers. Quando si effettuano richieste alle API di ordinamento e carrello, che richiedono l'autorizzazione, è necessario includere un token di accesso valido nella richiesta. A tale scopo, aggiungere il token di accesso alle intestazioni dell'istanza HttpClient, come illustrato nell'esempio di codice seguente:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

La proprietà DefaultRequestHeaders della classe HttpClient espone le intestazioni inviate a ogni richiesta e il token di accesso viene aggiunto all'intestazione Authorization preceduta dalla stringa Bearer. Quando la richiesta viene inviata a un'API RESTful, il valore dell'intestazione Authorization viene estratto e convalidato per assicurarsi che venga inviato da un emittente attendibile e usato per determinare se l'utente ha l'autorizzazione per richiamare l'API che lo riceve.

Per altre informazioni su come l'app multipiattaforma eShopOnContainers effettua richieste Web, vedere Accesso ai dati remoti.

Riepilogo

Esistono molti approcci per l'integrazione dell'autenticazione e dell'autorizzazione in un'app .NET MAUI che comunica con un'applicazione Web ASP.NET. L'app multipiattaforma eShopOnContainers esegue l'autenticazione e l'autorizzazione con un microservizio di identità in contenitori che usa IdentityServer 4. IdentityServer è un framework OpenID Connect open source e OAuth 2.0 per ASP.NET Core che si integra con ASP.NET Core Identity per eseguire l'autenticazione del token di connessione.

L'app multipiattaforma richiede token di sicurezza da IdentityServer per autenticare un utente o accedere a una risorsa. Quando si accede a una risorsa, è necessario includere un token di accesso nella richiesta alle API che richiedono l'autorizzazione. Il middleware di IdentityServer convalida i token di accesso in ingresso per assicurarsi che vengano inviati da un'autorità di certificazione attendibile e che siano validi per essere usati con l'API che li riceve.