共用方式為


在 ASP.NET Core Blazor WebAssembly 中使用圖形 API

注意

這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前的版本,請參閱 本文的 .NET 9 版本。

本文說明如何在 Blazor WebAssembly 應用程式中使用 Microsoft Graph,這可讓應用程式存取 Microsoft Cloud 資源。

涵蓋兩種方法:

  • Graph SDKMicrosoft Graph SDK 簡化建置可存取 Microsoft Graph 的高品質、有效率且可復原的應用程式。 選取本文頂端的 [Graph SDK] 按鈕,即可採用此方法。

  • 使用 Graph API 的具名 HttpClient具名 HttpClient 可直接對 Microsoft Graph 發出 Microsoft Graph API 要求。 選取本文頂端的 [使用圖形 API 的具名 HttpClient] 按鈕,即可採用此方法。

本文中的指引並非用來取代 Microsoft Graph 文件與其他 Microsoft 文件集中的 Azure 安全性指引。 在生產環境中實作 Microsoft Graph 之前,請先評估本文其他資源一節中的安全性指引。 請依照 Microsoft 最佳做法,限縮應用程式的漏洞。

由下列 Microsoft Graph 和 Azure 範例所提供使用 Microsoft Graph Blazor WebAssembly 的其他方法:

若要對上述兩個範例的其中之一提供意見反應,請在範例的 GitHub 存放庫上提出問題。 如果您要對 Azure 範例提出問題,請在開放式留言中提供連至該範例的連結,因為 Azure 範例存放庫 (Azure-Samples) 包含許多範例。 詳述問題並視需要包含範例程序碼。 將可重現問題或錯誤的精簡應用程式放入 GitHub。 請務必先從範例中移除 Azure 帳戶設定資料,再將其提交至公用存放庫。

若要對本文或 ASP.NET Core 提供意見反應或尋求協助,請參閱 ASP.NET Core Blazor 基本概念

重要

本文所述的案例適用於使用 Microsoft Entra(ME-ID)作為 identity 提供者(而非 AAD B2C)時。 目前不支援搭配用戶端 Blazor WebAssembly 應用程式和 AAD B2C identity 提供者使用 Microsoft Graph,因為應用程式需要用戶端密碼,這無法在用戶端 Blazor 應用程式中受到保護。 針對使用 Graph API 的 AAD B2C 獨立 Blazor WebAssembly 應用程式,建立後端伺服器 (Web) API 以代表使用者存取 Graph API。 用戶端應用程式會驗證並授權使用者呼叫 Web API,以安全地存取 Microsoft Graph,並將資料從伺服器端 Web API 傳回用戶端 Blazor 應用程式。 用戶端密碼會在伺服器型 Web API 中安全地維護,而不是在用戶端上的 Blazor 應用程式中。 永不將用戶端密碼儲存在用戶端 Blazor 應用程式中。

支援使用託管的 Blazor WebAssembly 應用程式;其中,Server 應用程式會使用 Graph SDK/API 透過 Web API 將 Graph 資料提供給 Client 應用程式。 如需詳細資訊,請參閱本文的託管的 Blazor WebAssembly 解決方案一節。

本文中的範例會利用新的 .NET/C# 功能。 在.NET 7 或更早版本中使用這些範例時,需要略為修改。 不過,對於所有 ASP.NET Core 版本,涉及與 Microsoft Graph 互動的文字和程式碼範例都相同。

下列指引適用於 Microsoft Graph v5

在 Blazor 應用程式中使用的 Microsoft Graph SDK,稱為 Microsoft Graph .NET 用戶端程式庫

Graph SDK 範例需要獨立 Blazor WebAssembly 應用程式中的下列套件參考。 如果應用程式已啟用 MSAL 驗證,則已參考前兩個套件,例如,遵循使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式中的指導建立應用程式時。

Graph SDK 範例需要獨立 Blazor WebAssembly 應用程式或託管 Blazor WebAssembly 解決方案的 Client 應用程式中的下列套件參考。 如果應用程式已啟用 MSAL 驗證,則已參考前兩個套件,例如,遵循使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式中的指導建立應用程式時。

注意

如需將套件新增至 .NET 應用程式的指引,請參閱在套件取用工作流程 (NuGet 文件)安裝及管理套件底下的文章。 在 NuGet.org 確認正確的套件版本。

在 Azure 入口網站中,為 Microsoft Graph 資料授與委派的權限 (範圍)†,應用程式應該能夠代表使用者存取。 針對本文中的範例,應用程式的註冊應該包含讀取使用者資料的委派權限 (API 權限中的 Microsoft.Graph>User.Read 範圍,類型:委派)。 User.Read 範圍允許使用者登入應用程式,並且允許應用程式讀取已登入使用者的設定檔和公司資訊。 如需詳細資訊,請參閱 Microsoft identity 平台的權限和同意概觀 以及 Microsoft Graph 權限概觀

權限範圍代表相同的事項,在安全性文件和 Azure 入口網站中交換使用。 除非文字指的是 Azure 入口網站,否則本文會在指稱 Graph 權限時使用「範圍」

範圍不區分大小寫,因此 User.Readuser.read 相同。 請隨意使用任一格式,但建議在應用程式碼之間選擇一致的格式。

在 Azure 入口網站的應用程式註冊中新增 Microsoft Graph API 範圍之後,請將下列應用程式設定組態新增至 wwwroot/appsettings.json 檔案,其中包括 Graph 基底 URL 以及 Microsoft Graph 版本和範圍。 在下列範例中,會針對本文後續幾節中的範例指定 User.Read 範圍。 範圍不區分大小寫。

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

在上述範例中,{VERSION} 預留位置是 Microsoft Graph API 的版本 (例如:v1.0)。

以下是使用 ME-ID 作為其 identity 提供者的應用程式的完整 wwwroot/appsettings.json 組態檔,其中,讀取使用者資料(user.read 範圍)是為 Microsoft Graph 而指定:

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

在上述範例中,{TENANT ID} 預留位置是目錄 (租用戶) 識別碼,而 {CLIENT ID} 預留位置是應用程式 (用戶端) 識別碼。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

將下列 GraphClientExtensions 類別新增至獨立應用程式。 範圍會提供給 AuthenticateRequestAsync 方法中 AccessTokenRequestOptionsScopes 屬性。

將下列 GraphClientExtensions 類別新增至獨立應用程式或託管 Blazor WebAssembly解決方案Client 應用程式。 範圍會提供給 AuthenticateRequestAsync 方法中 AccessTokenRequestOptionsScopes 屬性。

未取得存取權杖時,下列程式碼不會設定 Graph 要求的持有人授權標頭。

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[]>() ??
                        [ "user.read" ]
                });

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

重要

如需上述程式碼為何使用 DefaultAccessTokenScopes 來新增範圍,而非使用 AdditionalScopesToConsent 的說明,請參閱 DefaultAccessTokenScopesAdditionalScopesToConsent 一節。

Program 檔案中,使用 AddGraphClient 擴充方法新增 Graph 用戶端服務與設定。 如果應用程式設定檔中找不到這些設定,下列程式碼會預設為 1.0 版 Microsoft Graph 基底位址與 User.Read 範圍:

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

使用 Graph SDK 從元件呼叫圖形 API

下列 UserData 元件會使用插入的 GraphServiceClient 取得使用者的 ME-ID 設定檔資料,並顯示其行動電話號碼。

對於您在 ME-ID 中建立的任何測試使用者,請確定您在 Azure 入口網站中為使用者的 ME-ID 設定檔提供了行動電話號碼。

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();
    }
}

NavMenu 元件中新增元件頁面的連結 (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>

提示

若要將使用者新增至應用程式,請參閱使用或不使用應用程式角色將使用者指派給應用程式註冊一節。

在本機使用 Graph SDK 進行測試時,我們建議針對每個測試使用新的 InPrivate/incognito 瀏覽器工作模式,以防止徘徊的 cookieie 干擾測試。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

使用 Graph SDK 自訂使用者宣告

在下列範例中,應用程式會從 ME-ID 使用者設定檔的資料,為使用者建立行動電話號碼和辦公室位置宣告。 應用程式必須在 ME-ID 中設定 User.Read 圖形 API 範圍。 此案例的任何測試使用者在其 ME-ID 設定檔中都必須有行動電話號碼和辦公室位置 (可透過 Azure 入口網站來新增)。

在下列自訂使用者帳戶處理站中:

  • 納入了 ILogger (logger),以便您在 CreateUserAsync 方法中記錄資訊或錯誤。
  • 如果擲回 AccessTokenNotAvailableException,則會將使用者重新導向至 identity 提供者以登入其帳戶。 要求存取權杖失敗時,可以採取其他或不同的動作。 例如,應用程式可以記錄 AccessTokenNotAvailableException 並建立支援票證以進一步調查。
  • 架構的 RemoteUserAccount 代表使用者帳戶。 如果應用程式需要擴充 RemoteUserAccount 的自訂使用者帳戶類別,請在下列程式碼中將您的自訂使用者帳戶類別替換為 RemoteUserAccount

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;

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 = string.Join("/",
        config.GetSection("MicrosoftGraph")["BaseUrl"] ?? 
            "https://graph.microsoft.com",
        config.GetSection("MicrosoftGraph")["Version"] ??
            "v1.0");

    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;
    }
}

設定 MSAL 驗證,以使用自訂使用者帳戶處理站。

確認 Program 檔案使用的是 Microsoft.AspNetCore.Components.WebAssembly.Authentication 命名空間:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

本節中的範例是基於透過 wwwroot/appsettings.json 檔案中的 MicrosoftGraph 區段讀取基底 URL 以及應用程式設定的版本和範圍的方法。 依照本文先前的指引,Program 檔案中應已有以下幾行:

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

Program 檔案中,尋找對 AddMsalAuthentication 擴充方法的呼叫。 將程式碼更新為以下內容,包括呼叫 AddAccountClaimsPrincipalFactory 以使用 CustomAccountFactory 新增帳戶宣告主體處理站。

如果應用程式會使用擴充 RemoteUserAccount 的自訂使用者帳戶類別,請在下列程式碼中將自訂使用者帳戶類別替換為 RemoteUserAccount

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

您可以在使用者向 ME-ID 進行驗證後,使用下列 UserClaims 元件審視使用者的宣告:

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;
    }
}

NavMenu 元件中新增元件頁面的連結 (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>

在本機使用 Graph SDK 進行測試時,我們建議針對每個測試使用新的 InPrivate/incognito 瀏覽器工作模式,以防止徘徊的 cookieie 干擾測試。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

下列指引適用於 Microsoft Graph v4。 如果您要將應用程式從 SDK v4 升級至 v5,請參閱 Microsoft Graph .NET SDK v5 變更記錄和升級指南

在 Blazor 應用程式中使用的 Microsoft Graph SDK,稱為 Microsoft Graph .NET 用戶端程式庫

Graph SDK 範例需要獨立 Blazor WebAssembly 應用程式中的下列套件參考。 如果應用程式已啟用 MSAL 驗證,則已參考前兩個套件,例如,遵循使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式中的指導建立應用程式時。

Graph SDK 範例需要獨立 Blazor WebAssembly 應用程式或託管 Blazor WebAssembly 解決方案的 Client 應用程式中的下列套件參考。 如果應用程式已啟用 MSAL 驗證,則已參考前兩個套件,例如,遵循使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式中的指導建立應用程式時。

注意

如需將套件新增至 .NET 應用程式的指引,請參閱在套件取用工作流程 (NuGet 文件)安裝及管理套件底下的文章。 在 NuGet.org 確認正確的套件版本。

在 Azure 入口網站中,為 Microsoft Graph 資料授與委派的權限 (範圍)†,應用程式應該能夠代表使用者存取。 針對本文中的範例,應用程式的註冊應該包含讀取使用者資料的委派權限 (API 權限中的 Microsoft.Graph>User.Read 範圍,類型:委派)。 User.Read 範圍允許使用者登入應用程式,並且允許應用程式讀取已登入使用者的設定檔和公司資訊。 如需詳細資訊,請參閱 Microsoft identity 平台的權限和同意概觀 以及 Microsoft Graph 權限概觀

權限範圍代表相同的事項,在安全性文件和 Azure 入口網站中交換使用。 除非文字指的是 Azure 入口網站,否則本文會在指稱 Graph 權限時使用「範圍」

範圍不區分大小寫,因此 User.Readuser.read 相同。 請隨意使用任一格式,但建議在應用程式碼之間選擇一致的格式。

在 Azure 入口網站的應用程式註冊中新增 Microsoft Graph API 範圍之後,請將下列應用程式設定組態新增至 wwwroot/appsettings.json 檔案,其中包括 Graph 基底 URL 以及 Microsoft Graph 版本和範圍。 在下列範例中,會針對本文後續幾節中的範例指定 User.Read 範圍。 範圍不區分大小寫。

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

在上述範例中,{VERSION} 預留位置是 Microsoft Graph API 的版本 (例如:v1.0)。

以下是使用 ME-ID 作為其 identity 提供者的應用程式的完整 wwwroot/appsettings.json 組態檔,其中,讀取使用者資料(user.read 範圍)是為 Microsoft Graph 而指定:

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

在上述範例中,{TENANT ID} 預留位置是目錄 (租用戶) 識別碼,而 {CLIENT ID} 預留位置是應用程式 (用戶端) 識別碼。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

將下列 GraphClientExtensions 類別新增至獨立應用程式。 範圍會提供給 AuthenticateRequestAsync 方法中 AccessTokenRequestOptionsScopes 屬性。 IHttpProvider.OverallTimeout 會從預設值 100 秒擴充至 300 秒,讓 HttpClient 有更多時間接收來自 Microsoft Graph 的回應。

將下列 GraphClientExtensions 類別新增至獨立應用程式或託管 Blazor WebAssembly解決方案Client 應用程式。 範圍會提供給 AuthenticateRequestAsync 方法中 AccessTokenRequestOptionsScopes 屬性。 IHttpProvider.OverallTimeout 會從預設值 100 秒擴充至 300 秒,讓 HttpClient 有更多時間接收來自 Microsoft Graph 的回應。

未取得存取權杖時,下列程式碼不會設定 Graph 要求的持有人授權標頭。

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()
        {
        }
    }
}

重要

如需上述程式碼為何使用 DefaultAccessTokenScopes 來新增範圍,而非使用 AdditionalScopesToConsent 的說明,請參閱 DefaultAccessTokenScopesAdditionalScopesToConsent 一節。

Program 檔案中,使用 AddGraphClient 擴充方法新增 Graph 用戶端服務和設定:

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

使用 Graph SDK 從元件呼叫圖形 API

下列 UserData 元件會使用插入的 GraphServiceClient 取得使用者的 ME-ID 設定檔資料,並顯示其行動電話號碼。 對於您在 ME-ID 中建立的任何測試使用者,請確定您在 Azure 入口網站中為使用者的 ME-ID 設定檔提供了行動電話號碼。

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();
    }
}

NavMenu 元件中新增元件頁面的連結 (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>

提示

若要將使用者新增至應用程式,請參閱使用或不使用應用程式角色將使用者指派給應用程式註冊一節。

在本機使用 Graph SDK 進行測試時,我們建議針對每個測試使用新的 InPrivate/incognito 瀏覽器工作模式,以防止徘徊的 cookieie 干擾測試。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

使用 Graph SDK 自訂使用者宣告

在下列範例中,應用程式會從 ME-ID 使用者設定檔的資料,為使用者建立行動電話號碼和辦公室位置宣告。 應用程式必須在 ME-ID 中設定 User.Read 圖形 API 範圍。 此案例的任何測試使用者在其 ME-ID 設定檔中都必須有行動電話號碼和辦公室位置 (可透過 Azure 入口網站來新增)。

在下列自訂使用者帳戶處理站中:

  • 納入了 ILogger (logger),以便您在 CreateUserAsync 方法中記錄資訊或錯誤。
  • 如果擲回 AccessTokenNotAvailableException,則會將使用者重新導向至 identity 提供者以登入其帳戶。 要求存取權杖失敗時,可以採取其他或不同的動作。 例如,應用程式可以記錄 AccessTokenNotAvailableException 並建立支援票證以進一步調查。
  • 架構的 RemoteUserAccount 代表使用者帳戶。 如果應用程式需要擴充 RemoteUserAccount 的自訂使用者帳戶類別,請在下列程式碼中將您的自訂使用者帳戶類別替換為 RemoteUserAccount

CustomAccountFactory.cs

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

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;
    }
}

設定 MSAL 驗證,以使用自訂使用者帳戶處理站。

確認 Program 檔案使用的是 Microsoft.AspNetCore.Components.WebAssembly.Authentication 命名空間:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

本節中的範例是基於透過 wwwroot/appsettings.json 檔案中的 MicrosoftGraph 區段讀取基底 URL 以及應用程式設定的版本和範圍的方法。 依照本文先前的指引,Program 檔案中應已有以下幾行:

var baseUrl = string.Join("/",
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
        "https://graph.microsoft.com",
    builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
        "v1.0");
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>() ?? [ "user.read" ];

builder.Services.AddGraphClient(baseUrl, scopes);

Program 檔案中,尋找對 AddMsalAuthentication 擴充方法的呼叫。 將程式碼更新為以下內容,包括呼叫 AddAccountClaimsPrincipalFactory 以使用 CustomAccountFactory 新增帳戶宣告主體處理站。

如果應用程式會使用擴充 RemoteUserAccount 的自訂使用者帳戶類別,請在下列程式碼中將自訂使用者帳戶類別替換為 RemoteUserAccount

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

您可以在使用者向 ME-ID 進行驗證後,使用下列 UserClaims 元件審視使用者的宣告:

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;
    }
}

NavMenu 元件中新增元件頁面的連結 (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>

在本機使用 Graph SDK 進行測試時,我們建議針對每個測試使用新的 InPrivate/incognito 瀏覽器工作模式,以防止徘徊的 cookieie 干擾測試。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

下列範例使用具名 HttpClient 呼叫圖形 API 以取得使用者的行動電話號碼,藉以處理通話,或自訂使用者的宣告,以包含行動電話號碼宣告和辦公室位置宣告。

這些範例需要獨立 Blazor WebAssembly 應用程式的 Microsoft.Extensions.Http 套件參考。

這些範例需要獨立 Blazor WebAssembly 應用程式或託管 Blazor WebAssembly 解決方案的 Client 應用程式的 Microsoft.Extensions.Http 套件參考。

注意

如需將套件新增至 .NET 應用程式的指引,請參閱在套件取用工作流程 (NuGet 文件)安裝及管理套件底下的文章。 在 NuGet.org 確認正確的套件版本。

在 Azure 入口網站中,為 Microsoft Graph 資料授與委派的權限 (範圍)†,應用程式應該能夠代表使用者存取。 針對本文中的範例,應用程式的註冊應該包含讀取使用者資料的委派權限 (API 權限中的 Microsoft.Graph>User.Read 範圍,類型:委派)。 User.Read 範圍允許使用者登入應用程式,並且允許應用程式讀取已登入使用者的設定檔和公司資訊。 如需詳細資訊,請參閱 Microsoft identity 平台的權限和同意概觀 以及 Microsoft Graph 權限概觀

權限範圍代表相同的事項,在安全性文件和 Azure 入口網站中交換使用。 除非文字指的是 Azure 入口網站,否則本文會在指稱 Graph 權限時使用「範圍」

範圍不區分大小寫,因此 User.Readuser.read 相同。 請隨意使用任一格式,但建議在應用程式碼之間選擇一致的格式。

在 Azure 入口網站的應用程式註冊中新增 Microsoft Graph API 範圍之後,請將下列應用程式設定組態新增至 wwwroot/appsettings.json 檔案,其中包括 Graph 基底 URL 以及 Microsoft Graph 版本和範圍。 在下列範例中,會針對本文後續幾節中的範例指定 User.Read 範圍。 範圍不區分大小寫。

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

在上述範例中,{VERSION} 預留位置是 Microsoft Graph API 的版本 (例如:v1.0)。

以下是使用 ME-ID 作為其 identity 提供者的應用程式的完整 wwwroot/appsettings.json 組態檔,其中,讀取使用者資料(user.read 範圍)是為 Microsoft Graph 而指定:

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

在上述範例中,{TENANT ID} 預留位置是目錄 (租用戶) 識別碼,而 {CLIENT ID} 預留位置是應用程式 (用戶端) 識別碼。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

Program 檔案中建立下列 GraphAuthorizationMessageHandler 類別和專案設定,以使用 Graph API。 基底 URL 和範圍會透過設定提供給處理常式。

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: [ 
                string.Join("/",
                    config.GetSection("MicrosoftGraph")["BaseUrl"] ??
                        "https://graph.microsoft.com",
                    config.GetSection("MicrosoftGraph")["Version"] ??
                        "v1.0")
            ],
            scopes: config.GetSection("MicrosoftGraph:Scopes")
                        .Get<List<string>>() ?? [ "user.read" ]);
    }
}

授權 URL 需要有後置斜線 (/)。 上述程式碼會從應用程式設定建置下列授權 URL,如果缺少應用程式設定,則會預設為下列授權 URL:https://graph.microsoft.com/v1.0/

Program 檔案中,設定圖形 API 的具名 HttpClient

builder.Services.AddTransient<GraphAuthorizationMessageHandler>();

builder.Services.AddHttpClient("GraphAPI",
        client => client.BaseAddress = new Uri(
            string.Join("/",
                builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ??
                    "https://graph.microsoft.com",
                builder.Configuration.GetSection("MicrosoftGraph")["Version"] ??
                    "v1.0",
                string.Empty)))
    .AddHttpMessageHandler<GraphAuthorizationMessageHandler>();

在前述範例中,GraphAuthorizationMessageHandler DelegatingHandler 已註冊為 AddHttpMessageHandler 的暫時性服務。 建議對 IHttpClientFactory 進行暫時性註冊,以管理其本身的 DI 範圍。 如需詳細資訊,請參閱以下資源:

基底位址需要有後置斜線 (/)。 在上述程式碼中,string.Join 的第三個引數為 string.Empty,以確保後置斜線存在:https://graph.microsoft.com/v1.0/

使用具名 HttpClient 從元件呼叫圖形 API

UserInfo.cs 類別會透過 JsonPropertyNameAttribute 屬性和ME-ID 所使用的 JSON 名稱來指定必要的使用者設定檔屬性。 下列範例會設定使用者行動電話和辦公室位置的屬性。

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; }
}

在下列 UserData 元件中,會針對 Graph API 建立 HttpClient,以發出使用者設定檔資料的要求。 me 資源 (me) 會新增至圖形 API 要求的基底 URL 與版本。 Graph 傳回的 JSON 資料會還原序列化為 UserInfo 類別屬性。 在下列範例中,會取得行動電話號碼。 如果您想要的話,可以新增類似的程式碼,以包含使用者的 ME-ID 設定檔辦公室位置 (userInfo.OfficeLocation)。 如果存取權杖要求失敗,則會將使用者重新導向,以登入應用程式並取得新的存取權杖。

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();
        }
    }
}

NavMenu 元件中新增元件頁面的連結 (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>

提示

若要將使用者新增至應用程式,請參閱使用或不使用應用程式角色將使用者指派給應用程式註冊一節。

下列順序描述 Graph API 範圍的新使用者流程:

  1. 新使用者第一次登入應用程式。
  2. 使用者同意在 Azure 同意 UI 中使用應用程式。
  3. 使用者會第一次存取要求 Graph API 資料的元件頁面。
  4. 使用者會被重新導向至 Azure 同意 UI,以同意 Graph API 範圍。
  5. 系統會傳回 Graph API 使用者資料。

如果您想要在初始登入時進行範圍佈建 (Graph API 範圍的同意),請將範圍提供給 MSAL 驗證作為 Program 檔案中的預設存取權杖範圍:

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

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

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

重要

如需上述程式碼為何使用 DefaultAccessTokenScopes 來新增範圍,而非使用 AdditionalScopesToConsent 的說明,請參閱 DefaultAccessTokenScopesAdditionalScopesToConsent 一節。

對應用程式進行上述變更時,使用者流程會採用下列順序:

  1. 新使用者第一次登入應用程式。
  2. 使用者同意在 Azure 同意 UI 中使用應用程式和 Graph API 範圍。
  3. 使用者會第一次存取要求 Graph API 資料的元件頁面。
  4. 系統會傳回 Graph API 使用者資料。

在本機使用圖形 API 進行測試時,我們建議針對每個測試使用新的 InPrivate/incognito 瀏覽器工作模式,以防止徘徊的 cookie 干擾測試。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

使用具名 HttpClient 自訂使用者宣告

在下列範例中,應用程式會從 ME-ID 使用者設定檔的資料,為使用者建立行動電話號碼和辦公室位置宣告。 應用程式必須在 ME-ID 中設定 User.Read 圖形 API 範圍。 要在 ME-ID 中測試使用者帳戶,必須輸入行動電話號碼和辦公室位置,這些資料可透過 Azure 入口網站新增至使用者設定檔。

如果您尚未依照本文先前的指引將 UserInfo 類別新增至應用程式,請新增下列類別,並使用 ME-ID 所使用的 JsonPropertyNameAttribute 屬性和 JSON 名稱指定必要的使用者設定檔屬性。 下列範例會設定使用者行動電話和辦公室位置的屬性。

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; }
}

在下列自訂使用者帳戶處理站中:

  • 納入了 ILogger (logger),以便您在 CreateUserAsync 方法中記錄資訊或錯誤。
  • 如果擲回 AccessTokenNotAvailableException,則會將使用者重新導向至 identity 提供者以登入其帳戶。 要求存取權杖失敗時,可以採取其他或不同的動作。 例如,應用程式可以記錄 AccessTokenNotAvailableException 並建立支援票證以進一步調查。
  • 架構的 RemoteUserAccount 代表使用者帳戶。 如果應用程式需要擴充 RemoteUserAccount 的自訂使用者帳戶類別,請在下列程式碼中將自訂使用者帳戶類別替換為 RemoteUserAccount

CustomAccountFactory.cs

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

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 驗證已設定為使用自訂使用者帳戶處理站。 首先,確認 Program 檔案使用 Microsoft.AspNetCore.Components.WebAssembly.Authentication 命名空間:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Program 檔案中,尋找對 AddMsalAuthentication 擴充方法的呼叫。 將程式碼更新為以下內容,包括呼叫 AddAccountClaimsPrincipalFactory 以使用 CustomAccountFactory 新增帳戶宣告主體處理站。

如果應用程式會使用擴充 RemoteUserAccount 的自訂使用者帳戶類別,請在下列程式碼中將您應用程式的自訂使用者帳戶類別替換為 RemoteUserAccount

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

上述範例適用於將 ME-ID 驗證與 MSAL 搭配使用的應用程式。 OIDC 和 API 驗證也有類似的模式。 如需詳細資訊,請參閱 ASP.NET Core Blazor WebAssembly 的其他安全性案例一文的使用承載宣告自訂使用者一節中的範例。

您可以在使用者向 ME-ID 進行驗證後,使用下列 UserClaims 元件審視使用者的宣告:

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;
    }
}

NavMenu 元件中新增元件頁面的連結 (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>

在本機使用圖形 API 進行測試時,我們建議針對每個測試使用新的 InPrivate/incognito 瀏覽器工作模式,以防止徘徊的 cookie 干擾測試。 如需詳細資訊,請參閱使用 Microsoft Entra ID 保護 ASP.NET Core Blazor WebAssembly 獨立應用程式

使用或不使用應用程式角色將使用者指派給應用程式註冊

您可以在 Azure 入口網站中使用下列步驟,將使用者新增至應用程式註冊,並將角色指派給使用者。

若要新增使用者,請從 Azure 入口網站的 ME-ID 區域中選取 [使用者]

  1. 選取 [新增使用者]>[建立新使用者]
  2. 使用建立使用者範本。
  3. Identity 區域中提供使用者的資訊。
  4. 您可以產生初始密碼,或指派使用者第一次登入後要變更的初始密碼。 如果您使用入口網站所產生的密碼,請立即記下該密碼。
  5. 選取 [建立] 以建立使用者。 [建立新使用者] 介面關閉時,請選取 [重新整理],以更新使用者清單並顯示新的使用者。
  6. 如需本文中的範例,請從使用者清單中選取其名稱、選取 [屬性],以及編輯連絡資訊以提供行動電話號碼,將行動電話號碼指派給新使用者。

若要在沒有應用程式角色的情況下將使用者指派給應用程式:

  1. 在 Azure 入口網站的 ME-ID 區域中,開啟企業應用程式
  2. 從清單中選取應用程式。
  3. 選取 使用者及群組
  4. 選取 [新增使用者/群組]
  5. 選取使用者。
  6. 選取 [指派] 按鈕。

若要在具有應用程式角色的情況下將使用者指派給應用程式:

  1. 遵循具有 Microsoft Entra ID 群組和角色的 ASP.NET Core Blazor WebAssembly 中的指導,將角色新增至 Azure 入口網站中的應用程式註冊。
  2. 在 Azure 入口網站的 ME-ID 區域中,開啟企業應用程式
  3. 從清單中選取應用程式。
  4. 選取 使用者及群組
  5. 選取 [新增使用者/群組]
  6. 選取使用者,然後選取其角色以存取應用程式。 將多個角色指派給使用者,方法是重複將使用者新增至應用程式的程序,直到為使用者指派所有角色為止。 具有多個角色的使用者會針對應用程式使用者的使用者和群組清單中每個指派角色列出一次。
  7. 選取 [指派] 按鈕。

DefaultAccessTokenScopesAdditionalScopesToConsent

本文中的範例會使用 DefaultAccessTokenScopes 佈建 Graph API 範圍,而不是使用 AdditionalScopesToConsent

不會使用 AdditionalScopesToConsent,因為使用者第一次透過 Azure 同意 UI 使用 MSAL 登入應用程式時,無法為使用者佈建 Graph API 範圍。 當使用者第一次嘗試使用 Graph SDK 存取 Graph API 時,他們遇到例外狀況:

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

在使用者透過 DefaultAccessTokenScopes 佈建 Graph API 範圍之後,應用程式就可以使用 AdditionalScopesToConsent 來進行後續使用者登入。 不過,變更應用程式程式碼對於需要定期新增具有委派 Graph 範圍的新使用者,或將新的委派 Graph API 範圍新增至應用程式的生產應用程式來說並無意義。

先前有關在使用者第一次登入應用程式時,如何佈建 Graph API 存取範圍的討論僅適用於:

  • 採用 Graph SDK 的應用程式。
  • 使用具名 HttpClient 進行 Graph API 存取的應用程式,要求使用者在第一次登入應用程式時同意 Graph 範圍。

使用未要求使用者在第一次登入時同意 Graph 範圍的具名 HttpClient 時,使用者會被重新導向至 Azure 同意 UI 進行 Graph API 範圍同意 當他們第一次要求存取 Graph API 時 透過預先設定具名 HttpClientDelegatingHandler。 當 Graph 範圍一開始未同意具名 HttpClient 方法時,應用程式不會呼叫 DefaultAccessTokenScopesAdditionalScopesToConsent。 如需詳細資訊,請參閱本文的具名 HttpClient 涵蓋範圍

託管的 Blazor WebAssembly 解決方案

本文中的範例說明如何直接從獨立 Blazor WebAssembly 應用程式或託管 Blazor WebAssembly解決方案Client 應用程式使用 Graph SDK 或圖形 API 的具名 HttpClient。 本文未涵蓋另一個案例,即託管解決方案的 Client 應用程式透過 Web API 呼叫解決方案的 Server 應用程式,然後 Server 應用程式使用 Graph SDK/API 呼叫 Microsoft Graph,並將資料傳回至 Client 應用程式。 雖然這是支援的方法,但本文未加以說明。 如果您想要採用此方法:

  • 依照從 ASP.NET Core Blazor 應用程式呼叫 Web API 中的指引,透過 Web API 從 Client 應用程式發出對 Server 應用程式的要求 ,並將資料傳回至 Client 應用程式。
  • 依照主要 Microsoft Graph 文件中的指引,將 Graph SDK 用於一般 ASP.NET Core 應用程式 (在此案例中為解決方案的 Server 應用程式)。 如果您使用 Blazor WebAssembly 專案範本來建立建立託管的 Blazor WebAssembly 解決方案(ASP.NET Core 託管的/-h|--hosted),此解決方案具有組織授權(單一組織/SingleOrg 或多個組織/MultiOrg)和 Microsoft Graph 選項(Visual Studio 中的Microsoft identity 平台>已連線服務>新增 Microsoft Graph 權限 或具有 NET CLI dotnet new命令的 --calls-graph 選項),則解決方案的 Server 應用程式會設定為在透過專案範本建立解決方案時使用 Graph SDK。

其他資源

一般方針

安全性指導