Share via


Authentifizierung und Autorisierung in ASP.NET Core Blazor Hybrid

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird die ASP.NET Core-Unterstützung für die Konfiguration und Verwaltung von Sicherheit und ASP.NET Core Identity in Blazor Hybrid-Apps beschrieben.

Die Authentifizierung in Blazor Hybrid-Apps wird von nativen Plattformbibliotheken verarbeitet, da sie erweiterte Sicherheitsgarantien bieten, die die Browsersandbox nicht bereitstellen kann. Die Authentifizierung nativer Apps verwendet einen betriebssystemspezifischen Mechanismus oder ein Verbundprotokoll wie z. B. OpenID Connect (OIDC). Folgen Sie den Anleitungen für den Identitätsanbieter, den Sie für die App ausgewählt haben, und führen Sie dann die weitere Integration der Identität in Blazor anhand der Anleitungen in diesem Artikel durch.

Die Integration der Authentifizierung muss für Razor-Komponenten und -Dienste die folgenden Ziele erreichen:

  • Verwenden der Abstraktionen im Paket Microsoft.AspNetCore.Components.Authorization, z. B AuthorizeView
  • Reagieren auf Änderungen im Authentifizierungskontext
  • Zugreifen auf Anmeldeinformationen, die von der App über den Identitätsanbieter bereitgestellt werden, z. B. Zugriffstoken zum Ausführen autorisierter API-Aufrufe

Nachdem die Authentifizierung einer .NET MAUI-, WPF- oder Windows Forms-App hinzugefügt wurde und Benutzer sich erfolgreich anmelden und abmelden können, integrieren Sie die Authentifizierung in Blazor, um den authentifizierten Benutzer für Razor-Komponenten und -Dienste verfügbar zu machen. Führen Sie die folgenden Schritte aus:

  • Verweisen Sie auf das Microsoft.AspNetCore.Components.Authorization-Paket.

    Hinweis

    Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

  • Implementieren Sie einen benutzerdefinierten AuthenticationStateProvider. Hierbei handelt es sich um die Abstraktion, die von Razor-Komponenten verwendet wird, um auf Informationen zum authentifizierten Benutzer zuzugreifen und Updates zu empfangen, wenn sich der Authentifizierungszustand ändert.

  • Registrieren Sie den benutzerdefinierten Authentifizierungsstatusanbieter im Abhängigkeitsinjektionscontainer.

.NET MAUI-Apps verwenden Xamarin.Essentials: Web Authenticator: Mit der WebAuthenticator-Klasse kann die App browserbasierte Authentifizierungsflows initiieren, die auf einen Rückruf an eine bestimmte URL lauschen, die für die App registriert ist.

Weitere Anleitungen finden Sie in den folgenden Ressourcen:

Windows Forms-Apps verwenden Microsoft Identity Platform für die Integration in Microsoft Entra ID (ME-ID) und AAD B2C. Weitere Informationen finden Sie unter Übersicht über die Microsoft-Authentifizierungsbibliothek (MSAL).

Erstellen eines benutzerdefinierten AuthenticationStateProvider ohne Updates zu Benutzeränderungen

Wenn die App den Benutzer unmittelbar nach dem Starten der App authentifiziert und der authentifizierte Benutzer für die gesamte App-Lebensdauer bleibt, sind keine Benachrichtigungen zu Benutzeränderungen erforderlich, und die App stellt nur Informationen zum authentifizierten Benutzer bereit. In diesem Szenario meldet sich der Benutzer beim Öffnen der App an, und die App zeigt den Anmeldebildschirm erneut an, nachdem sich der Benutzer abmeldet. Der folgende ExternalAuthStateProvider ist die Beispielimplementierung eines benutzerdefinierten AuthenticationStateProvider für dieses Authentifizierungsszenario.

Hinweis

Der folgende benutzerdefinierte AuthenticationStateProvider deklariert keinen Namespace, damit das Codebeispiel auf jede Blazor Hybrid-App angewendet werden kann. Es empfiehlt sich jedoch, den Namespace Ihrer App bereitzustellen, wenn Sie das Beispiel in einer Produktions-App implementieren.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private readonly Task<AuthenticationState> authenticationState;

    public ExternalAuthStateProvider(AuthenticatedUser user) => 
        authenticationState = Task.FromResult(new AuthenticationState(user.Principal));

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        authenticationState;
}

public class AuthenticatedUser
{
    public ClaimsPrincipal Principal { get; set; } = new();
}

In den nachstehenden Schritten werden folgende Vorgänge beschrieben:

  • Hinzufügen erforderlicher Namespaces.
  • Hinzufügen der Autorisierungsdienste und Blazor-Abstraktionen zur Dienstsammlung.
  • Erstellen der Dienstsammlung.
  • Auflösen des AuthenticatedUser-Diensts, um den Anspruchsprinzipal des authentifizierten Benutzers festzulegen. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.
  • Zurückgeben des erstellten Hosts.

Fügen Sie in der Methode MauiProgram.CreateMauiApp von MauiProgram.cs Namespaces für Microsoft.AspNetCore.Components.Authorization und System.Security.Claims hinzu:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Entfernen Sie die folgende Codezeile, die eine erstellte Microsoft.Maui.Hosting.MauiApp zurückgibt:

- return builder.Build();

Ersetzen Sie die obige Codezeile durch den folgenden Code. Fügen Sie OpenID/MSAL-Code hinzu, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<AuthenticatedUser>();
var host = builder.Build();

var authenticatedUser = host.Services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

return host;

In den nachstehenden Schritten werden folgende Vorgänge beschrieben:

  • Hinzufügen erforderlicher Namespaces.
  • Hinzufügen der Autorisierungsdienste und Blazor-Abstraktionen zur Dienstsammlung.
  • Erstellen der Dienstsammlung und Hinzufügen der erstellten Dienstsammlung als Ressource zum ResourceDictionary der App.
  • Auflösen des AuthenticatedUser-Diensts, um den Anspruchsprinzipal des authentifizierten Benutzers festzulegen. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.
  • Zurückgeben des erstellten Hosts.

Fügen Sie im Konstruktor von MainWindow (MainWindow.xaml.cs) Namespaces für Microsoft.AspNetCore.Components.Authorization und System.Security.Claims hinzu:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Entfernen Sie die folgende Codezeile, die die erstellte Dienstsammlung dem ResourceDictionary der App als Ressource hinzufügt:

- Resources.Add("services", serviceCollection.BuildServiceProvider());

Ersetzen Sie die obige Codezeile durch den folgenden Code. Fügen Sie OpenID/MSAL-Code hinzu, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<AuthenticatedUser>();
var services = serviceCollection.BuildServiceProvider();
Resources.Add("services", services);

var authenticatedUser = services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

In den nachstehenden Schritten werden folgende Vorgänge beschrieben:

  • Hinzufügen erforderlicher Namespaces.
  • Hinzufügen der Autorisierungsdienste und Blazor-Abstraktionen zur Dienstsammlung.
  • Erstellen der Dienstsammlung und Hinzufügen der erstellten Dienstsammlung zum Dienstanbieter der App.
  • Auflösen des AuthenticatedUser-Diensts, um den Anspruchsprinzipal des authentifizierten Benutzers festzulegen. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

Fügen Sie im Konstruktor von Form1 (Form1.cs) Namespaces für Microsoft.AspNetCore.Components.Authorization und System.Security.Claims hinzu:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Entfernen Sie die folgende Codezeile, die die erstellte Dienstsammlung auf den Dienstanbieter der App festlegt:

- blazorWebView1.Services = services.BuildServiceProvider();

Ersetzen Sie die obige Codezeile durch den folgenden Code. Fügen Sie OpenID/MSAL-Code hinzu, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<AuthenticatedUser>();
var serviceCollection = services.BuildServiceProvider();
blazorWebView1.Services = serviceCollection;

var authenticatedUser = serviceCollection.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Erstellen eines benutzerdefinierten AuthenticationStateProvider mit Updates zu Benutzeränderungen

Um den Benutzer während der Ausführung der Blazor-App zu aktualisieren, rufen Sie NotifyAuthenticationStateChanged innerhalb der AuthenticationStateProvider-Implementierung über einen der folgenden Ansätze auf:

Signalisieren eines Authentifizierungsupdates von außerhalb der BlazorWebView (Option 1)

Ein benutzerdefinierter AuthenticationStateProvider kann einen globalen Dienst verwenden, um ein Authentifizierungsupdate zu signalisieren. Es wird empfohlen, durch den Dienst ein Ereignis anzubieten, das der AuthenticationStateProvider abonnieren kann. Dieses Ereignis ruft dann NotifyAuthenticationStateChanged auf.

Hinweis

Der folgende benutzerdefinierte AuthenticationStateProvider deklariert keinen Namespace, damit das Codebeispiel auf jede Blazor Hybrid-App angewendet werden kann. Es empfiehlt sich jedoch, den Namespace Ihrer App bereitzustellen, wenn Sie das Beispiel in einer Produktions-App implementieren.

ExternalAuthStateProvider.cs:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private AuthenticationState currentUser;

    public ExternalAuthStateProvider(ExternalAuthService service)
    {
        currentUser = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            currentUser = new AuthenticationState(newUser);
            NotifyAuthenticationStateChanged(Task.FromResult(currentUser));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(currentUser);
}

public class ExternalAuthService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

            if (UserChanged is not null)
            {
                UserChanged(currentUser);
            }
        }
    }
}

Fügen Sie in der Methode MauiProgram.CreateMauiApp von MauiProgram.cs einen Namespace für Microsoft.AspNetCore.Components.Authorization hinzu:

using Microsoft.AspNetCore.Components.Authorization;

Fügen Sie der Dienstsammlung die Autorisierungsdienste und Blazor-Abstraktionen hinzu:

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<ExternalAuthService>();

Fügen Sie im Konstruktor von MainWindow (MainWindow.xaml.cs) einen Namespace für Microsoft.AspNetCore.Components.Authorization hinzu:

using Microsoft.AspNetCore.Components.Authorization;

Fügen Sie der Dienstsammlung die Autorisierungsdienste und Blazor-Abstraktionen hinzu:

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<ExternalAuthService>();

Fügen Sie im Konstruktor von Form1 (Form1.cs) einen Namespace für Microsoft.AspNetCore.Components.Authorization hinzu:

using Microsoft.AspNetCore.Components.Authorization;

Fügen Sie der Dienstsammlung die Autorisierungsdienste und Blazor-Abstraktionen hinzu:

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<ExternalAuthService>();

Überall, wo die App einen Benutzer authentifiziert, wird der ExternalAuthService-Dienst aufgelöst:

var authService = host.Services.GetRequiredService<ExternalAuthService>();

Führen Sie Ihren benutzerdefinierten OpenID/MSAL-Code aus, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters. Der authentifizierte Benutzer (authenticatedUser im folgenden Beispiel) ist ein neuer ClaimsPrincipal, der auf einer neuen ClaimsIdentity basiert.

Legen Sie den aktuellen Benutzer auf den authentifizierten Benutzer fest:

authService.CurrentUser = authenticatedUser;

Eine Alternative zum vorherigen Ansatz besteht darin, den Benutzerprinzipal in System.Threading.Thread.CurrentPrincipal und nicht über einen Dienst festzulegen, um die Verwendung des Abhängigkeitsinjektionscontainers zu vermeiden:

public class CurrentThreadUserAuthenticationStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(
            new AuthenticationState(Thread.CurrentPrincipal as ClaimsPrincipal ?? 
                new ClaimsPrincipal(new ClaimsIdentity())));
}

Mithilfe des alternativen Ansatzes werden der Dienstsammlung nur Autorisierungsdienste (AddAuthorizationCore) und CurrentThreadUserAuthenticationStateProvider (.TryAddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()) hinzugefügt.

Verarbeiten der Authentifizierung innerhalb der BlazorWebView (Option 2)

Ein benutzerdefinierter AuthenticationStateProvider kann zusätzliche Methoden einschließen, um die Anmeldung und Abmeldung auszulösen und den Benutzer zu aktualisieren.

Hinweis

Der folgende benutzerdefinierte AuthenticationStateProvider deklariert keinen Namespace, damit das Codebeispiel auf jede Blazor Hybrid-App angewendet werden kann. Es empfiehlt sich jedoch, den Namespace Ihrer App bereitzustellen, wenn Sie das Beispiel in einer Produktions-App implementieren.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(new AuthenticationState(currentUser));

    public Task LogInAsync()
    {
        var loginTask = LogInAsyncCore();
        NotifyAuthenticationStateChanged(loginTask);

        return loginTask;

        async Task<AuthenticationState> LogInAsyncCore()
        {
            var user = await LoginWithExternalProviderAsync();
            currentUser = user;

            return new AuthenticationState(currentUser);
        }
    }

    private Task<ClaimsPrincipal> LoginWithExternalProviderAsync()
    {
        /*
            Provide OpenID/MSAL code to authenticate the user. See your identity 
            provider's documentation for details.

            Return a new ClaimsPrincipal based on a new ClaimsIdentity.
        */
        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity());

        return Task.FromResult(authenticatedUser);
    }

    public void Logout()
    {
        currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(
            Task.FromResult(new AuthenticationState(currentUser)));
    }
}

Im vorherigen Beispiel:

  • Durch den Aufruf von LogInAsyncCore wird der Anmeldevorgang ausgelöst.
  • Der Aufruf von NotifyAuthenticationStateChanged meldet, dass ein Update ausgeführt wird, sodass die App während des Anmelde- oder Abmeldevorgangs eine temporäre Benutzeroberfläche bereitstellen kann.
  • Durch die Rückgabe von loginTask wird die Aufgabe zurückgegeben, damit die Komponente, die die Anmeldung ausgelöst hat, warten und nach Abschluss der Aufgabe reagieren kann.
  • Die Methode LoginWithExternalProviderAsync wird vom Entwickler implementiert, um den Benutzer mit dem SDK des Identitätsanbieters anzumelden. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters. Der authentifizierte Benutzer (authenticatedUser) ist ein neuer ClaimsPrincipal, der auf einer neuen ClaimsIdentity basiert.

Fügen Sie der Dienstsammlung die Autorisierungsdienste und die Blazor-Abstraktion in der Methode MauiProgram.CreateMauiApp von MauiProgram.cs hinzu:

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

Fügen Sie der Dienstsammlung die Autorisierungsdienste und die Blazor-Abstraktion im Konstruktor (MainWindow.xaml.cs) von MainWindow hinzu:

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

Fügen Sie der Dienstsammlung die Autorisierungsdienste und die Blazor-Abstraktion im Konstruktor (Form1.cs) von Form1 hinzu:

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

Die folgende Komponente LoginComponent veranschaulicht die Anmeldung eines Benutzers. In einer typischen App wird die Komponente LoginComponent nur in einer übergeordneten Komponente angezeigt, wenn der Benutzer nicht bei der App angemeldet ist.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Login">Log in</button>

@code
{
    public async Task Login()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .LogInAsync();
    }
}

Die folgende Komponente LogoutComponent veranschaulicht die Abmeldung eines Benutzers. In einer typischen App wird die Komponente LogoutComponent nur in einer übergeordneten Komponente angezeigt, wenn der Benutzer bei der App angemeldet ist.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Logout">Log out</button>

@code
{
    public async Task Logout()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .Logout();
    }
}

Zugreifen auf andere Authentifizierungsinformationen

Blazor definiert keine Abstraktion zum Umgang mit anderen Anmeldeinformationen, z. B. Zugriffstoken für HTTP-Anforderungen an Web-APIs. Es wird empfohlen, die Anweisungen des Identitätsanbieters zu befolgen, um die Anmeldeinformationen des Benutzers mit den Grundtypen zu verwalten, die vom SDK des Identitätsanbieters bereitgestellt werden.

Es ist üblich, dass Identitätsanbieter-SDKs einen Tokenspeicher für Benutzeranmeldeinformationen verwenden, die auf dem Gerät gespeichert sind. Wenn der Tokenspeichergrundtyp des SDK dem Dienstcontainer hinzugefügt wird, wird der Grundtyp des SDK in der App genutzt.

Das Blazor-Framework ist nicht über die Authentifizierungsanmeldeinformationen eines Benutzers informiert und interagiert nicht mit den Anmeldeinformationen, sodass der Code der App flexibel jedem Ansatz folgen kann, der für Sie am geeignetsten ist. Befolgen Sie jedoch die allgemeinen Sicherheitsanweisungen im nächsten Abschnitt, Weitere Überlegungen zur Authentifizierungssicherheit, wenn Sie Authentifizierungscode in einer App implementieren.

Weitere Überlegungen zur Authentifizierungssicherheit

Der Authentifizierungsprozess erfolgt außerhalb von Blazor, und Entwicklern wird empfohlen, die Anweisungen des Identitätsanbieters zu beachten, um zusätzliche Sicherheitshinweise zu erhalten.

Beachten Sie beim Implementieren der Authentifizierung folgende Hinweise:

  • Vermeiden Sie die Authentifizierung im Kontext der Web View. Vermeiden Sie beispielsweise die Verwendung einer JavaScript-OAuth-Bibliothek zum Durchführen des Authentifizierungsflows. In einer Single-Page-App werden Authentifizierungstoken nicht in JavaScript ausgeblendet und können von böswilligen Benutzern leicht erkannt und für schädliche Zwecke verwendet werden. Für native Apps besteht dieses Risiko nicht, da native Apps nur Token außerhalb des Browserkontexts abrufen können. Dadurch können schädliche Drittanbieterskripts die Token nicht stehlen und die App nicht kompromittieren.
  • Sie sollten den Authentifizierungsworkflow nicht selbst implementieren. In den meisten Fällen behandeln Plattformbibliotheken den Authentifizierungsworkflow sicher und verwenden dabei den Browser des Systems anstelle einer benutzerdefinierten Web View, die gehackt werden kann.
  • Vermeiden Sie die Verwendung des Web View-Steuerelements der Plattform zum Ausführen der Authentifizierung. Nutzen Sie stattdessen nach Möglichkeit den Browser des Systems.
  • Vermeiden Sie das Übergeben der Token an den Dokumentkontext (JavaScript). In einigen Situationen ist eine JavaScript-Bibliothek innerhalb des Dokuments erforderlich, um einen autorisierten Aufruf eines externen Diensts auszuführen. Anstatt das Token über JS Interop für JavaScript verfügbar zu machen, gehen Sie folgendermaßen vor:
    • Stellen Sie ein generiertes temporäres Token für die Bibliothek und innerhalb der Web View bereit.
    • Fangen Sie die ausgehende Netzwerkanforderung im Code ab.
    • Ersetzen Sie das temporäre Token durch das reale Token, und bestätigen Sie die Gültigkeit des Anforderungsziels.

Zusätzliche Ressourcen

Die Authentifizierung in Blazor Hybrid-Apps wird von nativen Plattformbibliotheken verarbeitet, da sie erweiterte Sicherheitsgarantien bieten, die die Browsersandbox nicht bereitstellen kann. Die Authentifizierung nativer Apps verwendet einen betriebssystemspezifischen Mechanismus oder ein Verbundprotokoll wie z. B. OpenID Connect (OIDC). Folgen Sie den Anleitungen für den Identitätsanbieter, den Sie für die App ausgewählt haben, und führen Sie dann die weitere Integration der Identität in Blazor anhand der Anleitungen in diesem Artikel durch.

Die Integration der Authentifizierung muss für Razor-Komponenten und -Dienste die folgenden Ziele erreichen:

  • Verwenden der Abstraktionen im Paket Microsoft.AspNetCore.Components.Authorization, z. B AuthorizeView
  • Reagieren auf Änderungen im Authentifizierungskontext
  • Zugreifen auf Anmeldeinformationen, die von der App über den Identitätsanbieter bereitgestellt werden, z. B. Zugriffstoken zum Ausführen autorisierter API-Aufrufe

Nachdem die Authentifizierung einer .NET MAUI-, WPF- oder Windows Forms-App hinzugefügt wurde und Benutzer sich erfolgreich anmelden und abmelden können, integrieren Sie die Authentifizierung in Blazor, um den authentifizierten Benutzer für Razor-Komponenten und -Dienste verfügbar zu machen. Führen Sie die folgenden Schritte aus:

  • Verweisen Sie auf das Microsoft.AspNetCore.Components.Authorization-Paket.

    Hinweis

    Einen Leitfaden zum Hinzufügen von Paketen zu .NET-Apps finden Sie in Installieren und Verwalten von Paketen unter Workflow der Nutzung von Paketen (NuGet-Dokumentation). Überprüfen Sie unter NuGet.org, ob die richtige Paketversion verwendet wird.

  • Implementieren Sie einen benutzerdefinierten AuthenticationStateProvider. Hierbei handelt es sich um die Abstraktion, die von Razor-Komponenten verwendet wird, um auf Informationen zum authentifizierten Benutzer zuzugreifen und Updates zu empfangen, wenn sich der Authentifizierungszustand ändert.

  • Registrieren Sie den benutzerdefinierten Authentifizierungsstatusanbieter im Abhängigkeitsinjektionscontainer.

.NET MAUI-Apps verwenden Xamarin.Essentials: Web Authenticator: Mit der WebAuthenticator-Klasse kann die App browserbasierte Authentifizierungsflows initiieren, die auf einen Rückruf an eine bestimmte URL lauschen, die für die App registriert ist.

Weitere Anleitungen finden Sie in den folgenden Ressourcen:

Windows Forms-Apps verwenden Microsoft Identity Platform für die Integration in Microsoft Entra ID (ME-ID) und AAD B2C. Weitere Informationen finden Sie unter Übersicht über die Microsoft-Authentifizierungsbibliothek (MSAL).

Erstellen eines benutzerdefinierten AuthenticationStateProvider ohne Updates zu Benutzeränderungen

Wenn die App den Benutzer unmittelbar nach dem Starten der App authentifiziert und der authentifizierte Benutzer für die gesamte App-Lebensdauer bleibt, sind keine Benachrichtigungen zu Benutzeränderungen erforderlich, und die App stellt nur Informationen zum authentifizierten Benutzer bereit. In diesem Szenario meldet sich der Benutzer beim Öffnen der App an, und die App zeigt den Anmeldebildschirm erneut an, nachdem sich der Benutzer abmeldet. Der folgende ExternalAuthStateProvider ist die Beispielimplementierung eines benutzerdefinierten AuthenticationStateProvider für dieses Authentifizierungsszenario.

Hinweis

Der folgende benutzerdefinierte AuthenticationStateProvider deklariert keinen Namespace, damit das Codebeispiel auf jede Blazor Hybrid-App angewendet werden kann. Es empfiehlt sich jedoch, den Namespace Ihrer App bereitzustellen, wenn Sie das Beispiel in einer Produktions-App implementieren.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private readonly Task<AuthenticationState> authenticationState;

    public ExternalAuthStateProvider(AuthenticatedUser user) => 
        authenticationState = Task.FromResult(new AuthenticationState(user.Principal));

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        authenticationState;
}

public class AuthenticatedUser
{
    public ClaimsPrincipal Principal { get; set; } = new();
}

In den nachstehenden Schritten werden folgende Vorgänge beschrieben:

  • Hinzufügen erforderlicher Namespaces.
  • Hinzufügen der Autorisierungsdienste und Blazor-Abstraktionen zur Dienstsammlung.
  • Erstellen der Dienstsammlung.
  • Auflösen des AuthenticatedUser-Diensts, um den Anspruchsprinzipal des authentifizierten Benutzers festzulegen. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.
  • Zurückgeben des erstellten Hosts.

Fügen Sie in der Methode MauiProgram.CreateMauiApp von MauiProgram.cs Namespaces für Microsoft.AspNetCore.Components.Authorization und System.Security.Claims hinzu:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Entfernen Sie die folgende Codezeile, die eine erstellte Microsoft.Maui.Hosting.MauiApp zurückgibt:

- return builder.Build();

Ersetzen Sie die obige Codezeile durch den folgenden Code. Fügen Sie OpenID/MSAL-Code hinzu, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<AuthenticatedUser>();
var host = builder.Build();

var authenticatedUser = host.Services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

return host;

In den nachstehenden Schritten werden folgende Vorgänge beschrieben:

  • Hinzufügen erforderlicher Namespaces.
  • Hinzufügen der Autorisierungsdienste und Blazor-Abstraktionen zur Dienstsammlung.
  • Erstellen der Dienstsammlung und Hinzufügen der erstellten Dienstsammlung als Ressource zum ResourceDictionary der App.
  • Auflösen des AuthenticatedUser-Diensts, um den Anspruchsprinzipal des authentifizierten Benutzers festzulegen. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.
  • Zurückgeben des erstellten Hosts.

Fügen Sie im Konstruktor von MainWindow (MainWindow.xaml.cs) Namespaces für Microsoft.AspNetCore.Components.Authorization und System.Security.Claims hinzu:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Entfernen Sie die folgende Codezeile, die die erstellte Dienstsammlung dem ResourceDictionary der App als Ressource hinzufügt:

- Resources.Add("services", serviceCollection.BuildServiceProvider());

Ersetzen Sie die obige Codezeile durch den folgenden Code. Fügen Sie OpenID/MSAL-Code hinzu, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<AuthenticatedUser>();
var services = serviceCollection.BuildServiceProvider();
Resources.Add("services", services);

var authenticatedUser = services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

In den nachstehenden Schritten werden folgende Vorgänge beschrieben:

  • Hinzufügen erforderlicher Namespaces.
  • Hinzufügen der Autorisierungsdienste und Blazor-Abstraktionen zur Dienstsammlung.
  • Erstellen der Dienstsammlung und Hinzufügen der erstellten Dienstsammlung zum Dienstanbieter der App.
  • Auflösen des AuthenticatedUser-Diensts, um den Anspruchsprinzipal des authentifizierten Benutzers festzulegen. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

Fügen Sie im Konstruktor von Form1 (Form1.cs) Namespaces für Microsoft.AspNetCore.Components.Authorization und System.Security.Claims hinzu:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Entfernen Sie die folgende Codezeile, die die erstellte Dienstsammlung auf den Dienstanbieter der App festlegt:

- blazorWebView1.Services = services.BuildServiceProvider();

Ersetzen Sie die obige Codezeile durch den folgenden Code. Fügen Sie OpenID/MSAL-Code hinzu, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters.

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<AuthenticatedUser>();
var serviceCollection = services.BuildServiceProvider();
blazorWebView1.Services = serviceCollection;

var authenticatedUser = serviceCollection.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Erstellen eines benutzerdefinierten AuthenticationStateProvider mit Updates zu Benutzeränderungen

Um den Benutzer während der Ausführung der Blazor-App zu aktualisieren, rufen Sie NotifyAuthenticationStateChanged innerhalb der AuthenticationStateProvider-Implementierung über einen der folgenden Ansätze auf:

Signalisieren eines Authentifizierungsupdates von außerhalb der BlazorWebView (Option 1)

Ein benutzerdefinierter AuthenticationStateProvider kann einen globalen Dienst verwenden, um ein Authentifizierungsupdate zu signalisieren. Es wird empfohlen, durch den Dienst ein Ereignis anzubieten, das der AuthenticationStateProvider abonnieren kann. Dieses Ereignis ruft dann NotifyAuthenticationStateChanged auf.

Hinweis

Der folgende benutzerdefinierte AuthenticationStateProvider deklariert keinen Namespace, damit das Codebeispiel auf jede Blazor Hybrid-App angewendet werden kann. Es empfiehlt sich jedoch, den Namespace Ihrer App bereitzustellen, wenn Sie das Beispiel in einer Produktions-App implementieren.

ExternalAuthStateProvider.cs:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private AuthenticationState currentUser;

    public ExternalAuthStateProvider(ExternalAuthService service)
    {
        currentUser = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            currentUser = new AuthenticationState(newUser);
            NotifyAuthenticationStateChanged(Task.FromResult(currentUser));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(currentUser);
}

public class ExternalAuthService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

            if (UserChanged is not null)
            {
                UserChanged(currentUser);
            }
        }
    }
}

Fügen Sie in der Methode MauiProgram.CreateMauiApp von MauiProgram.cs einen Namespace für Microsoft.AspNetCore.Components.Authorization hinzu:

using Microsoft.AspNetCore.Components.Authorization;

Fügen Sie der Dienstsammlung die Autorisierungsdienste und Blazor-Abstraktionen hinzu:

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<ExternalAuthService>();

Fügen Sie im Konstruktor von MainWindow (MainWindow.xaml.cs) einen Namespace für Microsoft.AspNetCore.Components.Authorization hinzu:

using Microsoft.AspNetCore.Components.Authorization;

Fügen Sie der Dienstsammlung die Autorisierungsdienste und Blazor-Abstraktionen hinzu:

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<ExternalAuthService>();

Fügen Sie im Konstruktor von Form1 (Form1.cs) einen Namespace für Microsoft.AspNetCore.Components.Authorization hinzu:

using Microsoft.AspNetCore.Components.Authorization;

Fügen Sie der Dienstsammlung die Autorisierungsdienste und Blazor-Abstraktionen hinzu:

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<ExternalAuthService>();

Überall, wo die App einen Benutzer authentifiziert, wird der ExternalAuthService-Dienst aufgelöst:

var authService = host.Services.GetRequiredService<ExternalAuthService>();

Führen Sie Ihren benutzerdefinierten OpenID/MSAL-Code aus, um den Benutzer zu authentifizieren. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters. Der authentifizierte Benutzer (authenticatedUser im folgenden Beispiel) ist ein neuer ClaimsPrincipal, der auf einer neuen ClaimsIdentity basiert.

Legen Sie den aktuellen Benutzer auf den authentifizierten Benutzer fest:

authService.CurrentUser = authenticatedUser;

Eine Alternative zum vorherigen Ansatz besteht darin, den Benutzerprinzipal in System.Threading.Thread.CurrentPrincipal und nicht über einen Dienst festzulegen, um die Verwendung des Abhängigkeitsinjektionscontainers zu vermeiden:

public class CurrentThreadUserAuthenticationStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(
            new AuthenticationState(Thread.CurrentPrincipal as ClaimsPrincipal ?? 
                new ClaimsPrincipal(new ClaimsIdentity())));
}

Mithilfe des alternativen Ansatzes werden der Dienstsammlung nur Autorisierungsdienste (AddAuthorizationCore) und CurrentThreadUserAuthenticationStateProvider (.AddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()) hinzugefügt.

Verarbeiten der Authentifizierung innerhalb der BlazorWebView (Option 2)

Ein benutzerdefinierter AuthenticationStateProvider kann zusätzliche Methoden einschließen, um die Anmeldung und Abmeldung auszulösen und den Benutzer zu aktualisieren.

Hinweis

Der folgende benutzerdefinierte AuthenticationStateProvider deklariert keinen Namespace, damit das Codebeispiel auf jede Blazor Hybrid-App angewendet werden kann. Es empfiehlt sich jedoch, den Namespace Ihrer App bereitzustellen, wenn Sie das Beispiel in einer Produktions-App implementieren.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(new AuthenticationState(currentUser));

    public Task LogInAsync()
    {
        var loginTask = LogInAsyncCore();
        NotifyAuthenticationStateChanged(loginTask);

        return loginTask;

        async Task<AuthenticationState> LogInAsyncCore()
        {
            var user = await LoginWithExternalProviderAsync();
            currentUser = user;

            return new AuthenticationState(currentUser);
        }
    }

    private Task<ClaimsPrincipal> LoginWithExternalProviderAsync()
    {
        /*
            Provide OpenID/MSAL code to authenticate the user. See your identity 
            provider's documentation for details.

            Return a new ClaimsPrincipal based on a new ClaimsIdentity.
        */
        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity());

        return Task.FromResult(authenticatedUser);
    }

    public void Logout()
    {
        currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(
            Task.FromResult(new AuthenticationState(currentUser)));
    }
}

Im vorherigen Beispiel:

  • Durch den Aufruf von LogInAsyncCore wird der Anmeldevorgang ausgelöst.
  • Der Aufruf von NotifyAuthenticationStateChanged meldet, dass ein Update ausgeführt wird, sodass die App während des Anmelde- oder Abmeldevorgangs eine temporäre Benutzeroberfläche bereitstellen kann.
  • Durch die Rückgabe von loginTask wird die Aufgabe zurückgegeben, damit die Komponente, die die Anmeldung ausgelöst hat, warten und nach Abschluss der Aufgabe reagieren kann.
  • Die Methode LoginWithExternalProviderAsync wird vom Entwickler implementiert, um den Benutzer mit dem SDK des Identitätsanbieters anzumelden. Weitere Informationen finden Sie in der Dokumentation Ihres Identitätsanbieters. Der authentifizierte Benutzer (authenticatedUser) ist ein neuer ClaimsPrincipal, der auf einer neuen ClaimsIdentity basiert.

Fügen Sie der Dienstsammlung die Autorisierungsdienste und die Blazor-Abstraktion in der Methode MauiProgram.CreateMauiApp von MauiProgram.cs hinzu:

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

Fügen Sie der Dienstsammlung die Autorisierungsdienste und die Blazor-Abstraktion im Konstruktor (MainWindow.xaml.cs) von MainWindow hinzu:

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

Fügen Sie der Dienstsammlung die Autorisierungsdienste und die Blazor-Abstraktion im Konstruktor (Form1.cs) von Form1 hinzu:

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

Die folgende Komponente LoginComponent veranschaulicht die Anmeldung eines Benutzers. In einer typischen App wird die Komponente LoginComponent nur in einer übergeordneten Komponente angezeigt, wenn der Benutzer nicht bei der App angemeldet ist.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Login">Log in</button>

@code
{
    public async Task Login()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .LogInAsync();
    }
}

Die folgende Komponente LogoutComponent veranschaulicht die Abmeldung eines Benutzers. In einer typischen App wird die Komponente LogoutComponent nur in einer übergeordneten Komponente angezeigt, wenn der Benutzer bei der App angemeldet ist.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Logout">Log out</button>

@code
{
    public async Task Logout()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .Logout();
    }
}

Zugreifen auf andere Authentifizierungsinformationen

Blazor definiert keine Abstraktion zum Umgang mit anderen Anmeldeinformationen, z. B. Zugriffstoken für HTTP-Anforderungen an Web-APIs. Es wird empfohlen, die Anweisungen des Identitätsanbieters zu befolgen, um die Anmeldeinformationen des Benutzers mit den Grundtypen zu verwalten, die vom SDK des Identitätsanbieters bereitgestellt werden.

Es ist üblich, dass Identitätsanbieter-SDKs einen Tokenspeicher für Benutzeranmeldeinformationen verwenden, die auf dem Gerät gespeichert sind. Wenn der Tokenspeichergrundtyp des SDK dem Dienstcontainer hinzugefügt wird, wird der Grundtyp des SDK in der App genutzt.

Das Blazor-Framework ist nicht über die Authentifizierungsanmeldeinformationen eines Benutzers informiert und interagiert nicht mit den Anmeldeinformationen, sodass der Code der App flexibel jedem Ansatz folgen kann, der für Sie am geeignetsten ist. Befolgen Sie jedoch die allgemeinen Sicherheitsanweisungen im nächsten Abschnitt, Weitere Überlegungen zur Authentifizierungssicherheit, wenn Sie Authentifizierungscode in einer App implementieren.

Weitere Überlegungen zur Authentifizierungssicherheit

Der Authentifizierungsprozess erfolgt außerhalb von Blazor, und Entwicklern wird empfohlen, die Anweisungen des Identitätsanbieters zu beachten, um zusätzliche Sicherheitshinweise zu erhalten.

Beachten Sie beim Implementieren der Authentifizierung folgende Hinweise:

  • Vermeiden Sie die Authentifizierung im Kontext der Web View. Vermeiden Sie beispielsweise die Verwendung einer JavaScript-OAuth-Bibliothek zum Durchführen des Authentifizierungsflows. In einer Single-Page-App werden Authentifizierungstoken nicht in JavaScript ausgeblendet und können von böswilligen Benutzern leicht erkannt und für schädliche Zwecke verwendet werden. Für native Apps besteht dieses Risiko nicht, da native Apps nur Token außerhalb des Browserkontexts abrufen können. Dadurch können schädliche Drittanbieterskripts die Token nicht stehlen und die App nicht kompromittieren.
  • Sie sollten den Authentifizierungsworkflow nicht selbst implementieren. In den meisten Fällen behandeln Plattformbibliotheken den Authentifizierungsworkflow sicher und verwenden dabei den Browser des Systems anstelle einer benutzerdefinierten Web View, die gehackt werden kann.
  • Vermeiden Sie die Verwendung des Web View-Steuerelements der Plattform zum Ausführen der Authentifizierung. Nutzen Sie stattdessen nach Möglichkeit den Browser des Systems.
  • Vermeiden Sie das Übergeben der Token an den Dokumentkontext (JavaScript). In einigen Situationen ist eine JavaScript-Bibliothek innerhalb des Dokuments erforderlich, um einen autorisierten Aufruf eines externen Diensts auszuführen. Anstatt das Token über JS Interop für JavaScript verfügbar zu machen, gehen Sie folgendermaßen vor:
    • Stellen Sie ein generiertes temporäres Token für die Bibliothek und innerhalb der Web View bereit.
    • Fangen Sie die ausgehende Netzwerkanforderung im Code ab.
    • Ersetzen Sie das temporäre Token durch das reale Token, und bestätigen Sie die Gültigkeit des Anforderungsziels.

Zusätzliche Ressourcen