Verwenden der Graph-API mit der Blazor WebAssembly von ASP.NET Core

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 erläutert, wie Sie die Microsoft Graph-API in Blazor WebAssembly-Apps verwenden. Dabei handelt es sich um eine RESTful-Web-API, mit der Apps auf Ressourcen des Microsoft Cloud-Diensts zugreifen können.

Für die direkte Interaktion mit Microsoft Graph in Blazor-Apps stehen zwei Ansätze zur Verfügung:

  • Graph SDK: Die Microsoft Graph SDKs sollen die Entwicklung hochwertiger, effizienter und resilienter Anwendungen vereinfachen, die auf Microsoft Graph zugreifen. Wählen Sie oben in diesem Artikel die Schaltfläche Graph SDK aus, um diesen Ansatz zu verwenden.

  • Benannter HttpClient mit Graph-API: Ein benannter HttpClient kann Web-API-Anforderungen direkt an die Graph-API ausgeben. Wählen Sie oben in diesem Artikel die Schaltfläche Benannter HttpClient mit Graph-API aus, um diesen Ansatz zu verwenden.

Die Anleitungen in diesem Artikel sind nicht als Ersatz für die primäre Microsoft Graph-Dokumentation und weitere Azure-Sicherheitsanleitungen in anderen Microsoft-Dokumentationsreihen gedacht. Werten Sie den Sicherheitsleitfaden im Abschnitt Weitere Ressourcen dieses Artikels aus, bevor Sie Microsoft Graph in einer Produktionsumgebung implementieren. Befolgen Sie alle bewährten Methoden von Microsoft, um die Angriffsfläche Ihrer Apps zu begrenzen.

Wichtig

Die in diesem Artikel beschriebenen Szenarien gelten für die Verwendung von Microsoft Entra (ME-ID) als Identitätsanbieter, nicht für AAD B2C. Die Verwendung von Microsoft Graph mit einer clientseitigen Blazor WebAssembly-App und dem AAD B2C-Identitätsanbieter wird derzeit nicht unterstützt, da für die App ein geheimer Clientschlüssel erforderlich wäre, der in der clientseitigen Blazor-App nicht gesichert werden kann. Verwenden Sie für eine eigenständige AAD B2C-Blazor WebAssembly-App eine Graph-API, erstellen Sie eine Back-End-Server-API (Web-API), um im Namen von Benutzern auf die Graph-API zuzugreifen. Die clientseitige App authentifiziert und autorisiert Benutzer für den Aufruf der Web-API, um sicher auf Microsoft Graph zuzugreifen und Daten an die clientseitige Blazor-App zurückzugeben. Der geheime Clientschlüssel wird sicher in der serverbasierten Web-API verwaltet, nicht in der Blazor-App auf dem Client. Speichern Sie einen geheimen Clientschlüssel niemals in einer clientseitigen Blazor-App.

Die Verwendung einer gehosteten Blazor WebAssembly-App wird unterstützt, wenn die Server-App das Graph SDK bzw. die -API verwendet, um Graph-Daten für die Client-App über die Web-API bereitzustellen. Weitere Informationen finden Sie im Abschnitt Gehostete Blazor WebAssembly-Lösungen in diesem Artikel.

Die Beispiele in diesem Artikel nutzen neue .NET-/C#-Funktionen. Wenn Sie die Beispiele mit .NET 7 oder früher verwenden, sind geringfügige Änderungen erforderlich. Die Text- und Codebeispiele, die sich auf die Interaktion mit Microsoft Graph beziehen, sind jedoch für alle Versionen von ASP.NET Core identisch.

Der folgende Leitfaden gilt für Microsoft Graph v5.

Das Microsoft Graph SDK für die Verwendung in Blazor-Apps wird als Microsoft Graph .NET-Clientbibliothek bezeichnet.

Die Graph SDK-Beispiele erfordern die folgenden Paketverweise in der eigenständigen Blazor WebAssembly-App. Auf die ersten beiden Pakete wird bereits verwiesen, wenn die App für die MSAL-Authentifizierung aktiviert wurde, z. B. beim Erstellen der App durch Befolgen der Anweisungen in Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Die Graph SDK-Beispiele erfordern die folgenden Paketverweise in der eigenständigen Blazor WebAssembly-App oder der Client-App einer gehosteten Blazor WebAssembly-Lösung. Auf die ersten beiden Pakete wird bereits verwiesen, wenn die App für die MSAL-Authentifizierung aktiviert wurde, z. B. beim Erstellen der App durch Befolgen der Anweisungen in Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

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.

Erteilen Sie im Azure-Portal delegierte Berechtigungen (Bereiche)† für Microsoft Graph-Daten, auf die die App im Namen eines Benutzers zugreifen kann. Im Beispiel in diesem Artikel sollte die Registrierung der App delegierte Berechtigungen zum Lesen von Benutzerdaten enthalten (Microsoft.Graph>User.Read Bereich in API-Berechtigungen, Typ: Delegiert). Der User.Read-Bereich ermöglicht Benutzern die Anmeldung bei der App und der App das Lesen der Profil- und Unternehmensinformationen angemeldeter Benutzer. Weitere Informationen finden Sie unter Übersicht über Berechtigungen und Zustimmung in der Microsoft-Identitätsplattform und Übersicht über Microsoft Graph-Berechtigungen.

Hinweis

Berechtigungen und Bereiche bedeuten dasselbe und werden in der Sicherheitsdokumentation und im Azure-Portal synonym verwendet. In diesem Artikel werden Bereich/Bereiche verwendet, wenn auf Graph-API-Berechtigungen verwiesen wird.

Nachdem Sie die Microsoft Graph-API-Bereiche zur App-Registrierung im Azure-Portal hinzugefügt haben, fügen Sie der Datei wwwroot/appsettings.json in der App die folgende Konfiguration mit App-Einstellungen hinzu, die die Graph-Basis-URL mit der Microsoft Graph-Version und Bereiche enthält. Im folgenden Beispiel wird der User.Read-Bereich für die Beispiele in späteren Abschnitten dieses Artikels angegeben. Die Groß-/Kleinschreibung wird bei Bereichen nicht beachtet.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com/{VERSION}/",
  "Scopes": [
    "user.read"
  ]
}

Im vorherigen Beispiel steht der Platzhalter {VERSION} für die Version der Microsoft Graph-API (z. B. v1.0). Der nachstehende Schrägstrich ist erforderlich.

Im Folgenden sehen Sie ein Beispiel für eine vollständige wwwroot/appsettings.json-Konfigurationsdatei für eine App, die ME-ID als Identitätsanbieter verwendet, wobei das Lesen von Benutzerdaten (user.read-Bereich) für Microsoft Graph angegeben wird:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0/",
    "Scopes": [
      "user.read"
    ]
  }
}

Im vorherigen Beispiel ist der {TENANT ID}-Platzhalter die Verzeichnis-ID (Mandant), und der {CLIENT ID}-Platzhalter ist die Anwendungs-ID (Client). Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Fügen Sie der eigenständigen App die folgende GraphClientExtensions-Klasse hinzu. Die Bereiche werden für die Scopes-Eigenschaft der AccessTokenRequestOptions in der AuthenticateRequestAsync-Methode bereitgestellt.

Fügen Sie der eigenständigen App oder der Client-App einer gehosteten Blazor WebAssemblyProjektmappe die GraphClientExtensions-Klasse hinzu. Die Bereiche werden für die Scopes-Eigenschaft der AccessTokenRequestOptions in der AuthenticateRequestAsync-Methode bereitgestellt.

Wenn kein Zugriffstoken abgerufen wird, wird mit dem folgenden Code kein Bearertoken-Autorisierungsheader für Graph-Anforderungen festgelegt.

GraphClientExtensions.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using IAccessTokenProvider = 
    Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider;

namespace BlazorSample;

internal static class GraphClientExtensions
{
    public static IServiceCollection AddGraphClient(
            this IServiceCollection services, string? baseUrl, List<string>? scopes)
    {
        if (string.IsNullOrEmpty(baseUrl) || scopes?.Count == 0)
        {
            return services;
        }

        services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
            options =>
            {
                scopes?.ForEach((scope) =>
                {
                    options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
                });
            });

        services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();

        services.AddScoped(sp =>
        {
            return new GraphServiceClient(
                new HttpClient(),
                sp.GetRequiredService<IAuthenticationProvider>(),
                baseUrl);
        });

        return services;
    }

    private class GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, 
        IConfiguration config) : IAuthenticationProvider
    {
        private readonly IConfiguration config = config;

        public IAccessTokenProvider TokenProvider { get; } = tokenProvider;

        public async Task AuthenticateRequestAsync(RequestInformation request, 
            Dictionary<string, object>? additionalAuthenticationContext = null, 
            CancellationToken cancellationToken = default)
        {
            var result = await TokenProvider.RequestAccessToken(
                new AccessTokenRequestOptions()
                {
                    Scopes = 
                        config.GetSection("MicrosoftGraph:Scopes").Get<string[]>()
                });

            if (result.TryGetToken(out var token))
            {
                request.Headers.Add("Authorization", 
                    $"{CoreConstants.Headers.Bearer} {token.Value}");
            }
        }
    }
}

Wichtig

Im Abschnitt DefaultAccessTokenScopes Vergleich AdditionalScopesToConsent finden Sie eine Erläuterung dazu, warum der vorangehende Code DefaultAccessTokenScopes anstatt AdditionalScopesToConsent verwendet, um die Bereiche hinzuzufügen.

Fügen Sie in der Program-Datei die Graph-Clientdienste und die Konfiguration mit der AddGraphClient-Erweiterungsmethode hinzu:

var baseUrl = builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

Aufrufen der Graph-API aus einer Komponente mithilfe des Graph SDK

In der folgenden UserData-Komponente wird ein injizierter GraphServiceClient verwendet, um die Daten zum ME-ID-Profil des Benutzers abzurufen und seine Mobiltelefonnummer anzuzeigen.

Stellen Sie für jeden Testbenutzer, den Sie in ME-ID erstellen, sicher, dass Sie dem ME-ID-Profil des Benutzers im Azure-Portal eine Mobiltelefonnummer zuweisen.

UserData.razor:

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Graph
@attribute [Authorize]
@inject GraphServiceClient Client

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(user?.MobilePhone))
{
    <p>Mobile Phone: @user.MobilePhone</p>
}

@code {
    private Microsoft.Graph.Models.User? user;

    protected override async Task OnInitializedAsync()
    {
        user = await Client.Me.GetAsync();
    }
}

Hinzufügen eines Links zur Seite der Komponente in der NavMenu-Komponente (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Tipp

Informationen zum Hinzufügen von Benutzern zu einer App finden Sie im Abschnitt Zuweisen von Benutzern zu einer App-Registrierung mit oder ohne App-Rollen.

Wenn Sie SDK mit dem Graph lokal testen, empfehlen wir, für jeden Test eine neue private/Inkognito-Browsersitzung zu verwenden, um zu verhindern, dass vorhandene cookie die Tests stören. Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Anpassen von Benutzeransprüchen mit dem Graph SDK

Im folgenden Beispiel erstellt die App Ansprüche für die Mobiltelefonnummer und den Bürostandort eines Benutzers aus den Daten in seinem ME-ID-Benutzerprofil. Für die App muss der Graph-API-Bereich User.Read in ME-ID konfiguriert sein. Alle Testbenutzer für dieses Szenario müssen über eine Mobiltelefonnummer und einen Bürostandort in ihrem ME-ID-Profil verfügen, die über das Azure-Portal hinzugefügt werden können.

In der folgenden benutzerdefinierten Benutzerkontofactory:

  • Für den Fall, dass Sie Informationen oder Fehler in der CreateUserAsync-Methode protokollieren möchten, ist der Einfachheit halber ein ILogger (logger) enthalten.
  • Im Fall, dass eine AccessTokenNotAvailableException ausgelöst wird, wird der Benutzer zum Identitätsanbieter umgeleitet, um sich bei ihrem Konto anzumelden. Zusätzliche oder andere Aktionen können ausgeführt werden, wenn beim Anfordern eines Zugriffstokens ein Fehler auftritt. Beispielsweise kann die App die AccessTokenNotAvailableException protokollieren und ein Supportticket für die weitere Untersuchung erstellen.
  • Das RemoteUserAccount des Frameworks stellt das Konto des Benutzers dar. Wenn die App eine benutzerdefinierte Benutzerkontoklasse erfordert, mit der RemoteUserAccount erweitert wird, tauschen Sie die benutzerdefinierte Benutzerkontoklasse im folgenden Code gegen RemoteUserAccount aus.

CustomAccountFactory.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions.Authentication;

namespace BlazorSample;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor,
        IServiceProvider serviceProvider, ILogger<CustomAccountFactory> logger,
        IConfiguration config) 
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IServiceProvider serviceProvider = serviceProvider;
    private readonly string? baseUrl = 
        config.GetSection("MicrosoftGraph")["BaseUrl"];

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

        if (initialUser.Identity is not null &&
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null && !string.IsNullOrEmpty(baseUrl))
            {
                try
                {
                    var client = new GraphServiceClient(
                        new HttpClient(),
                        serviceProvider
                            .GetRequiredService<IAuthenticationProvider>(),
                        baseUrl);

                    var user = await client.Me.GetAsync();

                    if (user is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            user.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            user.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

Konfigurieren Sie MSAL-Authentifizierung für die Verwendung der benutzerdefinierten Benutzerkontofactory.

Vergewissern Sie sich, dass die Datei Program den Microsoft.AspNetCore.Components.WebAssembly.Authentication-Namespace verwendet:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Das Beispiel in diesem Abschnitt baut auf dem Ansatz auf, die Basis-URL mit Version und Bereiche aus der App-Konfiguration über den Abschnitt MicrosoftGraph in der Datei wwwroot/appsettings.json zu lesen. Die folgenden Zeilen sollten bereits in der Program-Datei gemäß dem Leitfadens weiter oben in diesem Artikel enthalten sein:

var baseUrl = builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

Suchen Sie in der Program-Datei nach dem Aufruf der AddMsalAuthentication-Erweiterungsmethode. Aktualisieren Sie den Code wie folgt, der einen Aufruf von AddAccountClaimsPrincipalFactory enthält, der eine Kontoanspruchs-Prinzipalfactory mit CustomAccountFactory hinzufügt.

Wenn die App eine benutzerdefinierte Benutzerkontoklasse verwendet, mit der RemoteUserAccount erweitert wird, tauschen Sie die benutzerdefinierte Benutzerkontoklasse im folgenden Code gegen RemoteUserAccount aus.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount,
        CustomAccountFactory>();

Sie können die folgende UserClaims-Komponente verwenden, um die Ansprüche des Benutzers zu untersuchen, nachdem sich der Benutzer bei ME-ID authentifiziert hat:

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}
else
{
    <p>No claims found.</p>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Hinzufügen eines Links zur Seite der Komponente in der NavMenu-Komponente (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Wenn Sie SDK mit dem Graph lokal testen, empfehlen wir, für jeden Test eine neue private/Inkognito-Browsersitzung zu verwenden, um zu verhindern, dass vorhandene cookie die Tests stören. Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Der folgende Leitfaden gilt für Microsoft Graph v4. Wenn Sie ein Upgrade einer App von SDK v4 auf v5 durchführen, lesen Sie das Microsoft Graph .NET SDK v5-Änderungsprotokoll und Upgradehandbuch.

Das Microsoft Graph SDK für die Verwendung in Blazor-Apps wird als Microsoft Graph .NET-Clientbibliothek bezeichnet.

Die Graph SDK-Beispiele erfordern die folgenden Paketverweise in der eigenständigen Blazor WebAssembly-App. Auf die ersten beiden Pakete wird bereits verwiesen, wenn die App für die MSAL-Authentifizierung aktiviert wurde, z. B. beim Erstellen der App durch Befolgen der Anweisungen in Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Die Graph SDK-Beispiele erfordern die folgenden Paketverweise in der eigenständigen Blazor WebAssembly-App oder der Client-App einer gehosteten Blazor WebAssembly-Lösung. Auf die ersten beiden Pakete wird bereits verwiesen, wenn die App für die MSAL-Authentifizierung aktiviert wurde, z. B. beim Erstellen der App durch Befolgen der Anweisungen in Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

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.

Erteilen Sie im Azure-Portal delegierte Berechtigungen (Bereiche)† für Microsoft Graph-Daten, auf die die App im Namen eines Benutzers zugreifen kann. Im Beispiel in diesem Artikel sollte die Registrierung der App delegierte Berechtigungen zum Lesen von Benutzerdaten enthalten (Microsoft.Graph>User.Read Bereich in API-Berechtigungen, Typ: Delegiert). Der User.Read-Bereich ermöglicht Benutzern die Anmeldung bei der App und der App das Lesen der Profil- und Unternehmensinformationen angemeldeter Benutzer. Weitere Informationen finden Sie unter Übersicht über Berechtigungen und Zustimmung in der Microsoft-Identitätsplattform und Übersicht über Microsoft Graph-Berechtigungen.

Hinweis

Berechtigungen und Bereiche bedeuten dasselbe und werden in der Sicherheitsdokumentation und im Azure-Portal synonym verwendet. In diesem Artikel werden Bereich/Bereiche verwendet, wenn auf Graph-API-Berechtigungen verwiesen wird.

Nachdem Sie die Microsoft Graph-API-Bereiche zur App-Registrierung im Azure-Portal hinzugefügt haben, fügen Sie der Datei wwwroot/appsettings.json in der App die folgende Konfiguration mit App-Einstellungen hinzu, die die Graph-Basis-URL mit der Microsoft Graph-Version und Bereiche enthält. Im folgenden Beispiel wird der User.Read-Bereich für die Beispiele in späteren Abschnitten dieses Artikels angegeben. Die Groß-/Kleinschreibung wird bei Bereichen nicht beachtet.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com/{VERSION}/",
  "Scopes": [
    "user.read"
  ]
}

Im vorherigen Beispiel steht der Platzhalter {VERSION} für die Version der Microsoft Graph-API (z. B. v1.0). Der nachstehende Schrägstrich ist erforderlich.

Im Folgenden sehen Sie ein Beispiel für eine vollständige wwwroot/appsettings.json-Konfigurationsdatei für eine App, die ME-ID als Identitätsanbieter verwendet, wobei das Lesen von Benutzerdaten (user.read-Bereich) für Microsoft Graph angegeben wird:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0/",
    "Scopes": [
      "user.read"
    ]
  }
}

Im vorherigen Beispiel ist der {TENANT ID}-Platzhalter die Verzeichnis-ID (Mandant), und der {CLIENT ID}-Platzhalter ist die Anwendungs-ID (Client). Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Fügen Sie der eigenständigen App die folgende GraphClientExtensions-Klasse hinzu. Die Bereiche werden für die Scopes-Eigenschaft der AccessTokenRequestOptions in der AuthenticateRequestAsync-Methode bereitgestellt. Das IHttpProvider.OverallTimeout wird vom Standardwert von 100 Sekunden auf 300 Sekunden erweitert, um HttpClient mehr Zeit zum Empfangen einer Antwort von Microsoft Graph zur Verfügung zu stellen.

Fügen Sie der eigenständigen App oder der Client-App einer gehosteten Blazor WebAssemblyProjektmappe die GraphClientExtensions-Klasse hinzu. Die Bereiche werden für die Scopes-Eigenschaft der AccessTokenRequestOptions in der AuthenticateRequestAsync-Methode bereitgestellt. Das IHttpProvider.OverallTimeout wird vom Standardwert von 100 Sekunden auf 300 Sekunden erweitert, um HttpClient mehr Zeit zum Empfangen einer Antwort von Microsoft Graph zur Verfügung zu stellen.

Wenn kein Zugriffstoken abgerufen wird, wird mit dem folgenden Code kein Bearertoken-Autorisierungsheader für Graph-Anforderungen festgelegt.

GraphClientExtensions.cs:

using System.Net.Http.Headers;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Graph;

namespace BlazorSample;

internal static class GraphClientExtensions
{
    public static IServiceCollection AddGraphClient(
        this IServiceCollection services, string? baseUrl, List<string>? scopes)
    {
        if (string.IsNullOrEmpty(baseUrl) || scopes?.Count == 0)
        {
            return services;
        }

        services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
            options =>
            {
                scopes?.ForEach((scope) =>
                {
                    options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
                });
            });

        services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();

        services.AddScoped<IHttpProvider, HttpClientHttpProvider>(sp =>
            new HttpClientHttpProvider(new HttpClient()));

        services.AddScoped(sp =>
        {
            return new GraphServiceClient(
                baseUrl,
                sp.GetRequiredService<IAuthenticationProvider>(),
                sp.GetRequiredService<IHttpProvider>());
        });

        return services;
    }

    private class GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, 
        IConfiguration config) : IAuthenticationProvider
    {
        private readonly IConfiguration config = config;

        public IAccessTokenProvider TokenProvider { get; } = tokenProvider;

        public async Task AuthenticateRequestAsync(HttpRequestMessage request)
        {
            var result = await TokenProvider.RequestAccessToken(
                new AccessTokenRequestOptions()
                { 
                    Scopes = config.GetSection("MicrosoftGraph:Scopes").Get<string[]>()
                });

            if (result.TryGetToken(out var token))
            {
                request.Headers.Authorization ??= new AuthenticationHeaderValue(
                    "Bearer", token.Value);
            }
        }
    }

    private class HttpClientHttpProvider(HttpClient client) : IHttpProvider
    {
        private readonly HttpClient client = client;

        public ISerializer Serializer { get; } = new Serializer();

        public TimeSpan OverallTimeout { get; set; } = TimeSpan.FromSeconds(300);

        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
        {
            return client.SendAsync(request);
        }

        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
            HttpCompletionOption completionOption,
            CancellationToken cancellationToken)
        {
            return client.SendAsync(request, completionOption, cancellationToken);
        }

        public void Dispose()
        {
        }
    }
}

Wichtig

Im Abschnitt DefaultAccessTokenScopes Vergleich AdditionalScopesToConsent finden Sie eine Erläuterung dazu, warum der vorangehende Code DefaultAccessTokenScopes anstatt AdditionalScopesToConsent verwendet, um die Bereiche hinzuzufügen.

Fügen Sie in der Program-Datei die Graph-Clientdienste und die Konfiguration mit der AddGraphClient-Erweiterungsmethode hinzu:

var baseUrl = builder.Configuration
    .GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

Aufrufen der Graph-API aus einer Komponente mithilfe des Graph SDK

In der folgenden UserData-Komponente wird ein injizierter GraphServiceClient verwendet, um die Daten zum ME-ID-Profil des Benutzers abzurufen und seine Mobiltelefonnummer anzuzeigen. Stellen Sie für jeden Testbenutzer, den Sie in ME-ID erstellen, sicher, dass Sie dem ME-ID-Profil des Benutzers im Azure-Portal eine Mobiltelefonnummer zuweisen.

UserData.razor:

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Graph
@attribute [Authorize]
@inject GraphServiceClient Client

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(user?.MobilePhone))
{
    <p>Mobile Phone: @user.MobilePhone</p>
}

@code {
    private Microsoft.Graph.User? user;

    protected override async Task OnInitializedAsync()
    {
        var request = Client.Me.Request();
        user = await request.GetAsync();
    }
}

Hinzufügen eines Links zur Seite der Komponente in der NavMenu-Komponente (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Tipp

Informationen zum Hinzufügen von Benutzern zu einer App finden Sie im Abschnitt Zuweisen von Benutzern zu einer App-Registrierung mit oder ohne App-Rollen.

Wenn Sie SDK mit dem Graph lokal testen, empfehlen wir, für jeden Test eine neue private/Inkognito-Browsersitzung zu verwenden, um zu verhindern, dass vorhandene cookie die Tests stören. Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Anpassen von Benutzeransprüchen mit dem Graph SDK

Im folgenden Beispiel erstellt die App Ansprüche für die Mobiltelefonnummer und den Bürostandort eines Benutzers aus den Daten in seinem ME-ID-Benutzerprofil. Für die App muss der Graph-API-Bereich User.Read in ME-ID konfiguriert sein. Alle Testbenutzer für dieses Szenario müssen über eine Mobiltelefonnummer und einen Bürostandort in ihrem ME-ID-Profil verfügen, die über das Azure-Portal hinzugefügt werden können.

In der folgenden benutzerdefinierten Benutzerkontofactory:

  • Für den Fall, dass Sie Informationen oder Fehler in der CreateUserAsync-Methode protokollieren möchten, ist der Einfachheit halber ein ILogger (logger) enthalten.
  • Im Fall, dass eine AccessTokenNotAvailableException ausgelöst wird, wird der Benutzer zum Identitätsanbieter umgeleitet, um sich bei ihrem Konto anzumelden. Zusätzliche oder andere Aktionen können ausgeführt werden, wenn beim Anfordern eines Zugriffstokens ein Fehler auftritt. Beispielsweise kann die App die AccessTokenNotAvailableException protokollieren und ein Supportticket für die weitere Untersuchung erstellen.
  • Das RemoteUserAccount des Frameworks stellt das Konto des Benutzers dar. Wenn die App eine benutzerdefinierte Benutzerkontoklasse erfordert, mit der RemoteUserAccount erweitert wird, tauschen Sie die benutzerdefinierte Benutzerkontoklasse im folgenden Code gegen RemoteUserAccount aus.

CustomAccountFactory.cs:

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

namespace BlazorSample;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor, 
        IServiceProvider serviceProvider, ILogger<CustomAccountFactory> logger)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IServiceProvider serviceProvider = serviceProvider;

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

        if (initialUser.Identity is not null && 
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null)
            {
                try
                {
                    var client = ActivatorUtilities
                        .CreateInstance<GraphServiceClient>(serviceProvider);
                    var request = client.Me.Request();
                    var user = await request.GetAsync();

                    if (user is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            user.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            user.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

Konfigurieren Sie MSAL-Authentifizierung für die Verwendung der benutzerdefinierten Benutzerkontofactory.

Vergewissern Sie sich, dass die Datei Program den Microsoft.AspNetCore.Components.WebAssembly.Authentication-Namespace verwendet:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Das Beispiel in diesem Abschnitt baut auf dem Ansatz auf, die Basis-URL mit Version und Bereiche aus der App-Konfiguration über den Abschnitt MicrosoftGraph in der Datei wwwroot/appsettings.json zu lesen. Die folgenden Zeilen sollten bereits in der Program-Datei gemäß dem Leitfadens weiter oben in diesem Artikel enthalten sein:

var baseUrl = string.Join("/", 
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

Suchen Sie in der Program-Datei nach dem Aufruf der AddMsalAuthentication-Erweiterungsmethode. Aktualisieren Sie den Code wie folgt, der einen Aufruf von AddAccountClaimsPrincipalFactory enthält, der eine Kontoanspruchs-Prinzipalfactory mit CustomAccountFactory hinzufügt.

Wenn die App eine benutzerdefinierte Benutzerkontoklasse verwendet, mit der RemoteUserAccount erweitert wird, tauschen Sie die benutzerdefinierte Benutzerkontoklasse im folgenden Code gegen RemoteUserAccount aus.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount,
        CustomAccountFactory>();

Sie können die folgende UserClaims-Komponente verwenden, um die Ansprüche des Benutzers zu untersuchen, nachdem sich der Benutzer bei ME-ID authentifiziert hat:

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}
else
{
    <p>No claims found.</p>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Hinzufügen eines Links zur Seite der Komponente in der NavMenu-Komponente (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Wenn Sie SDK mit dem Graph lokal testen, empfehlen wir, für jeden Test eine neue private/Inkognito-Browsersitzung zu verwenden, um zu verhindern, dass vorhandene cookie die Tests stören. Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

In den folgenden Beispielen wird ein benannter HttpClient für Graph-API-Aufrufe verwendet, um die Mobiltelefonnummer eines Benutzers zum Verarbeiten eines Anrufs abzurufen oder die Ansprüche eines Benutzers so anzupassen, dass sie einen Mobiltelefonnummer-Anspruch und einen Bürostandortanspruch enthalten.

Für die Beispiele werden Paketverweise für das Paket Microsoft.Extensions.Http für die eigenständige Blazor WebAssembly-App benötigt.

Für die Beispiele ist ein Paketverweis für Microsoft.Extensions.Http für die eigenständige Blazor WebAssembly-App oder die Client-App einer gehosteten Blazor WebAssembly-Lösung erforderlich.

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.

Erteilen Sie im Azure-Portal delegierte Berechtigungen (Bereiche)† für Microsoft Graph-Daten, auf die die App im Namen eines Benutzers zugreifen kann. Im Beispiel in diesem Artikel sollte die Registrierung der App delegierte Berechtigungen zum Lesen von Benutzerdaten enthalten (Microsoft.Graph>User.Read Bereich in API-Berechtigungen, Typ: Delegiert). Der User.Read-Bereich ermöglicht Benutzern die Anmeldung bei der App und der App das Lesen der Profil- und Unternehmensinformationen angemeldeter Benutzer. Weitere Informationen finden Sie unter Übersicht über Berechtigungen und Zustimmung in der Microsoft-Identitätsplattform und Übersicht über Microsoft Graph-Berechtigungen.

Hinweis

Berechtigungen und Bereiche bedeuten dasselbe und werden in der Sicherheitsdokumentation und im Azure-Portal synonym verwendet. In diesem Artikel werden Bereich/Bereiche verwendet, wenn auf Graph-API-Berechtigungen verwiesen wird.

Nachdem Sie die Microsoft Graph-API-Bereiche zur App-Registrierung im Azure-Portal hinzugefügt haben, fügen Sie der Datei wwwroot/appsettings.json in der App die folgende Konfiguration mit App-Einstellungen hinzu, die die Graph-Basis-URL mit der Microsoft Graph-Version und Bereiche enthält. Im folgenden Beispiel wird der User.Read-Bereich für die Beispiele in späteren Abschnitten dieses Artikels angegeben. Die Groß-/Kleinschreibung wird bei Bereichen nicht beachtet.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com/{VERSION}/",
  "Scopes": [
    "user.read"
  ]
}

Im vorherigen Beispiel steht der Platzhalter {VERSION} für die Version der Microsoft Graph-API (z. B. v1.0). Der nachstehende Schrägstrich ist erforderlich.

Im Folgenden sehen Sie ein Beispiel für eine vollständige wwwroot/appsettings.json-Konfigurationsdatei für eine App, die ME-ID als Identitätsanbieter verwendet, wobei das Lesen von Benutzerdaten (user.read-Bereich) für Microsoft Graph angegeben wird:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0/",
    "Scopes": [
      "user.read"
    ]
  }
}

Im vorherigen Beispiel ist der {TENANT ID}-Platzhalter die Verzeichnis-ID (Mandant), und der {CLIENT ID}-Platzhalter ist die Anwendungs-ID (Client). Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Erstellen Sie die folgende GraphAuthorizationMessageHandler-Klasse und -Projektkonfiguration in der Program-Datei für das Arbeiten mit der Graph-API. Die Basis-URL und die Bereiche werden für den Handler aus der Konfiguration bereitgestellt.

GraphAuthorizationMessageHandler.cs:

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

namespace BlazorSample;

public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
    public GraphAuthorizationMessageHandler(IAccessTokenProvider provider,
        NavigationManager navigation, IConfiguration config)
        : base(provider, navigation)
    {
        ConfigureHandler(
            authorizedUrls: [ config.GetSection("MicrosoftGraph")["BaseUrl"] ?? 
                string.Empty ],
            scopes: config.GetSection("MicrosoftGraph:Scopes").Get<List<string>>());
    }
}

Konfigurieren Sie in der Program-Datei den benannten HttpClient für die Graph-API:

builder.Services.AddTransient<GraphAuthorizationMessageHandler>();

builder.Services.AddHttpClient("GraphAPI",
        client => client.BaseAddress = new Uri(
            builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ?? 
                string.Empty))
    .AddHttpMessageHandler<GraphAuthorizationMessageHandler>();

Im vorherigen Beispiel wird der GraphAuthorizationMessageHandlerDelegatingHandler als vorübergehender Dienst für AddHttpMessageHandler registriert. Die vorübergehende Registrierung wird für IHttpClientFactory empfohlen, da sie eigene DI-Bereiche verwaltet. Weitere Informationen finden Sie in den folgenden Ressourcen:

Aufrufen der Graph-API aus einer Komponente mithilfe eines benannten HttpClient

Die UserInfo.cs-Klasse legt die erforderlichen Benutzerprofileigenschaften mit dem JsonPropertyNameAttribute-Attribut und dem JSON-Namen fest, der von ME-ID verwendet wird. Im folgenden Beispiel werden Eigenschaften für die Mobiltelefonnummer und den Bürostandort des Benutzers eingerichtet.

UserInfo.cs:

using System.Text.Json.Serialization;

namespace BlazorSample;

public class UserInfo
{
    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; set; }

    [JsonPropertyName("officeLocation")]
    public string? OfficeLocation { get; set; }
}

In der folgenden UserData-Komponente wird HttpClient für die Graph-API erstellt, um eine Anforderung für die Profildaten des Benutzers auszugeben. Die me-Ressource (me) wird der Basis-URL mit Version für die Graph-API-Anforderung hinzugefügt. Von Graph zurückgegebene JSON-Daten werden in den UserInfo-Klasseneigenschaften deserialisiert. Im folgenden Beispiel wird die Mobiltelefonnummer abgerufen. Sie können ähnlichen Code hinzufügen, um den Bürostandort des ME-ID-Profils des Benutzers einzuschließen, wenn dies gewünscht wird (userInfo.OfficeLocation). Wenn die Zugriffstokenanforderung fehlschlägt, wird der Benutzer umgeleitet, um sich bei der App für ein neues Zugriffstoken anzumelden.

UserData.razor:

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@attribute [Authorize]
@inject IConfiguration Config
@inject IHttpClientFactory ClientFactory

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(userInfo?.MobilePhone))
{
    <p>Mobile Phone: @userInfo.MobilePhone</p>
}

@code {
    private UserInfo? userInfo;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            var client = ClientFactory.CreateClient("GraphAPI");

            userInfo = await client.GetFromJsonAsync<UserInfo>("me");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Hinzufügen eines Links zur Seite der Komponente in der NavMenu-Komponente (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Tipp

Informationen zum Hinzufügen von Benutzern zu einer App finden Sie im Abschnitt Zuweisen von Benutzern zu einer App-Registrierung mit oder ohne App-Rollen.

Die folgende Sequenz beschreibt den neuen Benutzerfluss für Graph-API-Bereiche:

  1. Der neue Benutzer meldet sich zum ersten Mal bei der App an.
  2. Der Benutzer stimmt in der Azure-Zustimmungsoberfläche der Verwendung der App zu.
  3. Der Benutzer greift auf eine Komponentenseite zu, die zum ersten Mal Graph-API-Daten anfordert.
  4. Der Benutzer wird zur Azure-ZustimmungsoberfIäche weitergeleitet, um Graph-API-Bereichen zuzustimmen.
  5. Es werden Graph-API-Benutzerdaten zurückgegeben.

Wenn Sie möchten, dass die Bereichsbereitstellung (Zustimmung für Graph-API-Bereiche) bei der ersten Anmeldung erfolgt, geben Sie die Bereiche der MSAL-Authentifizierung in der Datei Program als Standardzugriffstokenbereiche an:

+ var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
+     .Get<List<string>>() ?? [];

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);

+   foreach (var scope in scopes)
+   {
+       options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
+   }
});

Wichtig

Im Abschnitt DefaultAccessTokenScopes Vergleich AdditionalScopesToConsent finden Sie eine Erläuterung dazu, warum der vorangehende Code DefaultAccessTokenScopes anstatt AdditionalScopesToConsent verwendet, um die Bereiche hinzuzufügen.

Wenn die vorstehenden Änderungen an der App vorgenommen werden, übernimmt der Benutzerfluss die folgende Sequenz:

  1. Der neue Benutzer meldet sich zum ersten Mal bei der App an.
  2. Der Benutzer stimmt in der Azure-Zustimmungsoberfläche der Verwendung der App und Graph-API-Bereichen zu.
  3. Der Benutzer greift auf eine Komponentenseite zu, die zum ersten Mal Graph-API-Daten anfordert.
  4. Es werden Graph-API-Benutzerdaten zurückgegeben.

Wenn Sie mit der Graph-API lokal testen, empfehlen wir Ihnen, für jeden Test eine neue private/Inkognito-Browsersitzung zu verwenden, um zu verhindern, dass vorhandene cookie die Tests stören. Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Anpassen von Benutzeransprüchen mithilfe eines benannten HttpClient

Im folgenden Beispiel erstellt die App Ansprüche für die Mobiltelefonnummer und den Bürostandort des Benutzers aus den Daten in seinem ME-ID-Benutzerprofil. Für die App muss der Graph-API-Bereich User.Read in ME-ID konfiguriert sein. Testbenutzerkonten in ME-ID erfordern Einträge für die Mobiltelefonnummer und den Bürostandort, die ihren Benutzerprofilen über das Azure-Portal hinzugefügt werden können.

Wenn Sie der App die UserInfo-Klasse nicht bereits hinzugefügt haben, indem Sie die Anleitungen weiter oben in diesem Artikel befolgt haben, fügen Sie die folgende Klasse hinzu und legen die erforderlichen Benutzerprofileigenschaften mit dem JsonPropertyNameAttribute-Attribut und dem von ME-ID verwendeten JSON-Namen fest. Im folgenden Beispiel werden Eigenschaften für die Mobiltelefonnummer und den Bürostandort des Benutzers eingerichtet.

UserInfo.cs:

using System.Text.Json.Serialization;

namespace BlazorSample;

public class UserInfo
{
    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; set; }

    [JsonPropertyName("officeLocation")]
    public string? OfficeLocation { get; set; }
}

In der folgenden benutzerdefinierten Benutzerkontofactory:

  • Für den Fall, dass Sie Informationen oder Fehler in der CreateUserAsync-Methode protokollieren möchten, ist der Einfachheit halber ein ILogger (logger) enthalten.
  • Im Fall, dass eine AccessTokenNotAvailableException ausgelöst wird, wird der Benutzer zum Identitätsanbieter umgeleitet, um sich bei ihrem Konto anzumelden. Zusätzliche oder andere Aktionen können ausgeführt werden, wenn beim Anfordern eines Zugriffstokens ein Fehler auftritt. Beispielsweise kann die App die AccessTokenNotAvailableException protokollieren und ein Supportticket für die weitere Untersuchung erstellen.
  • Das RemoteUserAccount des Frameworks stellt das Konto des Benutzers dar. Wenn die App eine benutzerdefinierte Benutzerkontoklasse benötigt, mit der RemoteUserAccount erweitert wird, tauschen Sie die benutzerdefinierte Benutzerkontoklasse im folgenden Code gegen RemoteUserAccount aus.

CustomAccountFactory.cs:

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

namespace BlazorSample;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor,
        IHttpClientFactory clientFactory,
        ILogger<CustomAccountFactory> logger)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IHttpClientFactory clientFactory = clientFactory;

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

        if (initialUser.Identity is not null && 
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null)
            {
                try
                {
                    var client = clientFactory.CreateClient("GraphAPI");

                    var userInfo = await client.GetFromJsonAsync<UserInfo>("me");

                    if (userInfo is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            userInfo.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            userInfo.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

MSAL-Authentifizierung ist für die Verwendung der benutzerdefinierten Benutzerkontofactory konfiguriert. Vergewissern Sie sich zunächst, dass die Datei Program den Microsoft.AspNetCore.Components.WebAssembly.Authentication-Namespace verwendet:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Suchen Sie in der Program-Datei nach dem Aufruf der AddMsalAuthentication-Erweiterungsmethode. Aktualisieren Sie den Code wie folgt, der einen Aufruf von AddAccountClaimsPrincipalFactory enthält, der eine Kontoanspruchs-Prinzipalfactory mit CustomAccountFactory hinzufügt.

Wenn die App eine benutzerdefinierte Benutzerkontoklasse verwendet, mit der RemoteUserAccount erweitert wird, tauschen Sie die benutzerdefinierte Benutzerkontoklasse im folgenden Code gegen RemoteUserAccount aus.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState, 
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount, 
        CustomAccountFactory>();

Das obige Beispiel gilt für eine App, die die ME-ID-Authentifizierung mit MSAL verwendet. Ähnliche Muster gibt es für die OIDC- und die API-Authentifizierung. Weitere Informationen finden Sie in den Beispielen im Abschnitt Anpassen des Benutzers mit einem Nutzdatenanspruch im Artikel Weitere Sicherheitsszenarien in ASP.NET Core Blazor WebAssembly.

Sie können die folgende UserClaims-Komponente verwenden, um die Ansprüche des Benutzers zu untersuchen, nachdem sich der Benutzer bei ME-ID authentifiziert hat:

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}
else
{
    <p>No claims found.</p>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Hinzufügen eines Links zur Seite der Komponente in der NavMenu-Komponente (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Wenn Sie mit der Graph-API lokal testen, empfehlen wir Ihnen, für jeden Test eine neue private/Inkognito-Browsersitzung zu verwenden, um zu verhindern, dass vorhandene cookie die Tests stören. Weitere Informationen finden Sie unter Sichern einer eigenständigen ASP.NET Core-Blazor WebAssembly-App mit Microsoft Entra ID.

Zuweisen von Benutzern zu einer App-Registrierung mit oder ohne App-Rollen

Sie können mit den folgenden Schritten Benutzer zu einer App-Registrierung hinzufügen und Benutzern Rollen im Azure-Portal zuweisen.

Um einen Benutzer hinzuzufügen, wählen Sie im Bereich „ME-ID“ des Azure-Portals Benutzer aus:

  1. Wählen Sie Neuer Benutzer>Neuen Benutzer erstellen aus.
  2. Verwenden Sie die Vorlage Benutzer erstellen.
  3. Geben Sie im Bereich Identity die Benutzerinformationen an.
  4. Sie können ein anfängliches Kennwort generieren oder ein anfängliches Kennwort zuweisen, das der Benutzer ändert, wenn er sich zum ersten Mal anmeldet. Wenn Sie das vom Portal generierte Kennwort verwenden, notieren Sie es sich.
  5. Klicken Sie auf Erstellen, um die Benutzer*innen zu erstellen. Wenn die Benutzeroberfläche Neuen Benutzer erstellen geschlossen wird, wählen Sie Aktualisieren aus, um die Benutzerliste zu aktualisieren und den neuen Benutzer anzuzeigen.
  6. Weisen Sie für die Beispiele in diesem Artikel dem neuen Benutzer eine Mobiltelefonnummer zu, indem Sie seinen Namen aus der Benutzerliste wählen, Eigenschaftenauswählen und die Kontaktinformationen bearbeiten, um eine Mobiltelefonnummer anzugeben.

So weisen Sie der App Benutzer ohne App-Rollen zu:

  1. Öffnen Sie im ME-ID-Bereich des Azure-Portals Unternehmensanwendungen.
  2. Wählen Sie die App aus der Liste aus.
  3. Wählen Sie Benutzer und Gruppen aus.
  4. Wählen Sie Benutzer/Gruppe hinzufügen aus.
  5. Select a user.
  6. Wählen Sie die Schaltfläche Zuweisen aus.

So weisen Sie der App Benutzer mit App-Rollen zu:

  1. Fügen Sie der App-Registrierung im Azure-Portal Rollen hinzu, indem Sie den Anweisungen in ASP.NET Core Blazor WebAssembly mit Microsoft Entra ID-Gruppen und -Rollen folgen.
  2. Öffnen Sie im ME-ID-Bereich des Azure-Portals Unternehmensanwendungen.
  3. Wählen Sie die App aus der Liste aus.
  4. Wählen Sie Benutzer und Gruppen aus.
  5. Wählen Sie Benutzer/Gruppe hinzufügen aus.
  6. Wählen Sie einen Benutzer aus, und wählen Sie seine Rolle für den Zugriff auf die App aus. Sie können einem Benutzer mehrere Rollen zuweisen, indem Sie den Vorgang zum Hinzufügen des Benutzers zur App wiederholen, bis alle Rollen für einen Benutzer zugewiesen sind. Benutzer mit mehreren Rollen werden in der Liste Benutzer und Gruppen der App-Benutzer einmal für jede zugewiesene Rolle aufgeführt.
  7. Wählen Sie die Schaltfläche Zuweisen aus.

DefaultAccessTokenScopes im Vergleich mit AdditionalScopesToConsent

In den Beispielen in diesem Artikel werden Graph-API-Bereiche mit DefaultAccessTokenScopes anstatt AdditionalScopesToConsent bereitgestellt.

AdditionalScopesToConsent wird nicht verwendet, da es nicht in der Lage ist, Graph-API-Bereiche für Benutzer bereitzustellen, wenn sie sich über die Azure-Zustimmungsoberfläche zum ersten Mal mit MSAL bei der App anmelden. Wenn der Benutzer versucht, zum ersten Mal mit dem Graph SDK auf die Graph-API zuzugreifen, wird er mit einer Ausnahme konfrontiert:

Microsoft.Graph.Models.ODataErrors.ODataError: Access token is empty.

Nachdem ein Benutzer Graph-API-Bereiche über DefaultAccessTokenScopes bereitgestellt hat, kann die App AdditionalScopesToConsent für eine nachfolgende Benutzeranmeldung verwenden. Das Ändern von App-Code ist jedoch nicht sinnvoll, wenn eine Produktions-App das regelmäßige Hinzufügen neuer Benutzer mit delegierten Graph-Bereichen oder das Hinzufügen neuer delegierter Graph-API-Bereiche zur App erfordert.

Die vorherige Erläuterung, wie Bereiche für den Graph-API-Zugriff bereitgestellt werden, wenn sich der Benutzer zum ersten Mal bei der App anmeldet, gilt nur für:

  • Apps, die den Graph SDK übernehmen.
  • Apps, die einen benannten HttpClient für den Graph-API-Zugriff verwenden, der Benutzer auffordert, bei der ersten Anmeldung bei der App Graph-Bereichen zuzustimmen.

Wenn Sie einen benannten HttpClient verwenden, der Benutzer bei der ersten Anmeldung nicht auffordert, Graph-Bereichen zuzustimmen, werden Benutzer für die Zustimmung zu Graph-API-Bereichen zur Azure-Zustimmungsoberfläche weitergeleitet, wenn sie über die DelegatingHandler der vorkonfigurierten API namens HttpClient zum ersten Mal Zugriff auf die Graph-API anfordern. Wenn Graph-Bereichen anfänglich nicht mit dem benannten HttpClient-Ansatz zugestimmt wird, werden weder DefaultAccessTokenScopes noch AdditionalScopesToConsent von der App aufgerufen. Weitere Informationen finden Sie im benannten HttpClient-Bereich in diesem Artikel.

Gehostete Blazor WebAssembly-Lösungen

Die Beispiele in diesem Artikel beziehen sich auf die Verwendung des Graph SDK oder eines benannten HttpClient mit der Graph-API direkt aus einer eigenständigen Blazor WebAssembly-App oder direkt aus der Client-App einer gehosteten Blazor WebAssembly-Lösung. Ein weiteres Szenario, das in diesem Artikel nicht behandelt wird, bezieht sich auf eine Client-Anwendung einer gehosteten Lösung, um die Server-Anwendung der Lösung über die Web-API aufzurufen. Anschließend verwendet die Server-Anwendung das Graph SDK bzw. die Graph-API, um Microsoft Graph aufzurufen und Daten an die Client-Anwendung zurückzugeben. Dies ist zwar ein unterstützter Ansatz, er wird aber in diesem Artikel nicht behandelt. Wenn Sie diesen Ansatz verfolgen möchten, gehen Sie wie folgt vor:

  • Befolgen Sie die Anleitung unter Aufrufen einer Web-API aus einer ASP.NET Core-Blazor-App für die Web-API-Aspekte beim Ausgeben von Anforderungen an die Server-App aus der Client-App und Zurückgeben von Daten an die Client-App.
  • Befolgen Sie die Anleitungen in der primären Microsoft Graph-Dokumentation, um das Graph SDK mit einer typischen ASP.NET Core-App zu verwenden, bei der es sich in diesem Szenario um die Server-App der Lösung handelt. Wenn Sie die Blazor WebAssembly-Projektvorlage verwenden, um die gehostete Blazor WebAssembly-Lösung (ASP.NET Core Hosted/-h|--hosted) mit organisatorischer Autorisierung (einzelne Organisation/SingleOrg oder mehrere Organisationen/MultiOrg) und der Microsoft Graph-Option (Microsoft-Identitätsplattform>Verbundene Dienste>Microsoft Graph-Berechtigungen hinzufügen in Visual Studio oder die --calls-graph-Option mit dem .NET CLI-Befehl dotnet new) verwenden, wird die Server-App der Lösung für die Verwendung des Graph SDK konfiguriert, wenn die Lösung aus der Projektvorlage erstellt wird.

Zusätzliche Ressourcen

Allgemeine Hinweise

  • Dokumentation zu Microsoft Graph
  • Blazor WebAssembly-Beispiel-App für Microsoft Graph: In diesem Beispiel wird veranschaulicht, wie Sie mit dem Microsoft Graph .NET SDK aus Blazor WebAssembly-Apps auf Daten in Office 365 zugreifen.
  • Erstellen von .NET-Apps mit dem Microsoft Graph-Tutorial und der Microsoft Graph ASP.NET Core-Beispiel-App: Diese Ressourcen eignen sich am besten für gehosteteBlazor WebAssembly Lösungen, bei denen die Server-App für den Zugriff auf Microsoft Graph als typische ASP.NET Core-App im Auftrag der Client-App konfiguriert ist. Die Client-App verwendet die Web-API, um Anforderungen an die Server-App für Graph-Daten auszugeben. Obwohl diese Ressourcen nicht direkt für das Aufrufen von Graph von clientseitigenBlazor WebAssembly-Apps gelten, sind die ME-ID-App-Konfiguration und Microsoft Graph-Codierungsmethoden in den verknüpften Ressourcen für eigenständige Blazor WebAssembly-Apps relevant und sollten für allgemeine bewährte Methoden zu Rate gezogen werden.

Sicherheitsleitfaden