Поделиться через


Использование API Graph в ASP.NET Core Blazor WebAssembly

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

В текущем выпуске см . версию .NET 8 этой статьи.

В этой статье объясняется, как использовать Microsoft Graph в Blazor WebAssembly приложениях, что позволяет приложениям получать доступ к ресурсам Microsoft Cloud.

Рассматриваются два подхода:

  • Пакет SDK Graph. Пакет SDK Microsoft Graph упрощает создание высококачественных, эффективных и устойчивых приложений, обращаюющихся к Microsoft Graph. Нажмите кнопку SDK Graph в верхней части этой статьи, чтобы применить этот подход.

  • Именованный HttpClient с API Graph: именованный HttpClientможет выдавать запросы API Microsoft Graph непосредственно в Microsoft Graph. Нажмите кнопку "Именованный HttpClient" с помощью API Graph в верхней части этой статьи, чтобы применить этот подход.

Рекомендации в этой статье не предназначены для замены документации по Microsoft Graph и руководства по безопасности Azure в других наборах документации Майкрософт. Оцените рекомендации по безопасности в разделе "Дополнительные ресурсы " этой статьи перед реализацией Microsoft Graph в рабочей среде. Следуйте рекомендациям Корпорации Майкрософт, чтобы ограничить уязвимости приложений.

Дополнительные подходы к работе с Microsoft Graph и Blazor WebAssembly предоставляются следующими примерами Microsoft Graph и Azure:

  • Пример Blazor WebAssembly приложения Microsoft Graph: пример использует подход на основе фабрики с пакетом SDK Microsoft Graph для получения данных Office 365. В примере используется по умолчанию HttpClient для выполнения запросов клиента Graph. Если необходимо использовать значение по умолчанию HttpClient с базовым адресом приложения (BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) для других целей, например для загрузки данных из веб-корня приложения, рекомендуется рефакторинг фабрики клиентов Graph использовать именованный выделенный HttpClient для запросов Graph.
  • ASP.NET Core 8.0 Blazor WebAssembly | автономное приложение | вход пользователя, защищенный доступ к веб-API (Microsoft Graph) | Платформа Майкрософтidentity: пример — это прогрессивное веб-приложение (PWA), использующее обработчик сообщений авторизации и HttpClient получение данных Graph. Как и в предыдущем примере, этот пример также использует значение по умолчанию HttpClient для выполнения запросов клиента Graph. Вместо использования пакета SDK Microsoft Graph образец извлекает данные учетной записи пользователя в формате JSON с помощью запроса API Microsoft Graph в компоненте. Это аналогичная методика с подходом Api Graph с именем HttpClient в этой статье (используйте кнопку в верхней части этой статьи, чтобы просмотреть рекомендации), за исключением того, что руководство этой статьи использует именованный выделенный HttpClient для запросов Graph.

Чтобы предоставить отзыв об обоих предыдущих примерах, откройте проблему в репозитории GitHub примера. Если вы открываете проблему для примера Azure, укажите ссылку на пример в открываемом комментарии, так как пример репозитория Azure (Azure-Samples) содержит множество примеров. Подробно опишите проблему и включите пример кода по мере необходимости. Поместите минимальное приложение в GitHub, которое воспроизводит проблему или ошибку. Не забудьте удалить данные конфигурации учетной записи Azure из примера, прежде чем зафиксировать его в общедоступный репозиторий.

Чтобы получить отзывы или обратиться за помощью к этой статье или ASP.NET Core, см. ASP.NET основные принципы.Blazor

Внимание

Сценарии, описанные в этой статье, применяются к использованию Microsoft Entra (ME-ID) в качестве identity поставщика, а не AAD B2C. Использование Microsoft Graph с клиентским Blazor WebAssembly приложением и поставщиком AAD B2C identity в настоящее время не поддерживается, так как приложению потребуется секрет клиента, который не может быть защищен в клиентском Blazor приложении. Для автономного Blazor WebAssembly приложения AAD B2C используйте API Graph, создайте внутренний API сервера (веб-) для доступа к API Graph от имени пользователей. Клиентское приложение проходит проверку подлинности и разрешает пользователям вызывать веб-API для безопасного доступа к Microsoft Graph и возврата данных клиентскому Blazor приложению из веб-API на основе сервера. Секрет клиента безопасно сохраняется в веб-API на основе сервера, а не в Blazor приложении на клиенте. Никогда не храните секрет клиента в клиентском Blazor приложении.

Использование размещенного Blazor WebAssembly приложения поддерживается, где Server приложение использует пакет SDK или API Graph для предоставления данных Graph приложению Client через веб-API. Дополнительные сведения см. в разделе "Размещенные Blazor WebAssembly решения " этой статьи.

Примеры, приведенные в этой статье, используют новые функции .NET/C#. При использовании примеров с .NET 7 или более ранней версией требуются незначительные изменения. Однако примеры текста и кода, относящиеся к взаимодействию с Microsoft Graph, одинаковы для всех версий ASP.NET Core.

Следующее руководство относится к Microsoft Graph версии 5.

Пакет SDK Microsoft Graph для использования в Blazor приложениях называется клиентской библиотекой Microsoft Graph .NET.

В примерах пакета SDK Graph требуются следующие ссылки на пакеты в автономном Blazor WebAssembly приложении. На первые два пакета уже ссылается, если приложение было включено для проверки подлинности MSAL, например при создании приложения, следуя инструкциям в разделе "Защита автономного приложения ASP.NET CoreBlazor WebAssembly" с помощью идентификатора Microsoft Entra.

В примерах пакета SDK Graph требуются следующие ссылки на пакеты в автономном Blazor WebAssembly приложении или Client приложении размещенного Blazor WebAssembly решения. На первые два пакета уже ссылается, если приложение было включено для проверки подлинности MSAL, например при создании приложения, следуя инструкциям в разделе "Защита автономного приложения ASP.NET CoreBlazor WebAssembly" с помощью идентификатора Microsoft Entra.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

В портал Azure предоставьте делегированные разрешения (области)† для данных Microsoft Graph, к которым приложение должно иметь доступ от имени пользователя. Например, в этой статье регистрация приложения должна включать делегированное разрешение на чтение данных пользователя (Microsoft.Graph>User.Read область в разрешениях API, тип: Делегировано). Область User.Read позволяет пользователям входить в приложение и позволяет приложению считывать сведения о профиле и компании пользователей, вошедшего в систему. Дополнительные сведения см. в разделе "Обзор разрешений и согласия" на платформе Майкрософт identity и обзор разрешений Microsoft Graph.

†Permissions и области означают то же самое и используются взаимозаменяемо в документации по безопасности и портал Azure. Если текст не ссылается на портал Azure, в этой статье используются /области областей при обращении к разрешениям Graph.

Области являются нечувствительными к регистру, поэтому User.Read они совпадают user.read. Вы можете использовать любой формат, но мы рекомендуем согласованный выбор в коде приложения.

После добавления областей API Microsoft Graph в регистрацию приложения в портал Azure добавьте следующую конфигурацию wwwroot/appsettings.json параметров приложения в файл в приложении, который включает базовый URL-адрес Graph с версией и областями Microsoft Graph. В следующем примере User.Read область указывается для примеров в последующих разделах этой статьи. Области не учитывает регистр.

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

В предыдущем примере заполнитель — это версия API Microsoft Graph (например: {VERSION} v1.0).

Ниже приведен пример полного wwwroot/appsettings.json файла конфигурации для приложения, использующего ME-ID в качестве identity поставщика, где для Microsoft Graph заданы данные пользователя (user.read область).

{
  "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} заполнитель — идентификатор приложения (клиента). Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Добавьте следующий GraphClientExtensions класс в автономное приложение. Области предоставляются Scopes свойству AccessTokenRequestOptions метода AuthenticateRequestAsync .

Добавьте следующий класс GraphClientExtensions в изолированное приложение или приложение Client размещенного Blazor WebAssemblyрешения . Области предоставляются Scopes свойству AccessTokenRequestOptions метода AuthenticateRequestAsync .

Если маркер доступа не получен, следующий код не задает заголовок авторизации носителя для запросов 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в разделе "ПротивAdditionalScopesToConsent".DefaultAccessTokenScopes

В файле добавьте клиентские Program службы Graph и конфигурацию с AddGraphClient помощью метода расширения. Следующий код по умолчанию использует базовый адрес и User.Read области Microsoft Graph версии 1.0, если эти параметры не найдены в файле параметров приложения:

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

Вызов API Graph из компонента с помощью пакета SDK для Graph

UserData Следующий компонент использует внедренный GraphServiceClient объект для получения данных профиля ME-ID пользователя и отображения номера мобильного телефона.

Для любого тестового пользователя, который вы создаете в ME-ID, убедитесь, что вы предоставите профилю ME-ID пользователя номер мобильного телефона в портал Azure.

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>

Совет

Сведения о добавлении пользователей в приложение см. в разделе "Назначение пользователей регистрации приложения" с ролями приложений или без нее.

При локальном тестировании с помощью пакета SDK Graph рекомендуется использовать новый сеанс браузера InPrivate/incognito для каждого теста, чтобы предотвратить вмешательство в тесты с помощью файлов cookie. Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Настройка утверждений пользователей с помощью пакета SDK Graph

В следующем примере приложение создает мобильный номер телефона и утверждения о расположении офиса для пользователя из данных профиля пользователя ME-ID. Приложение должно иметь область API Graph, настроенную User.Read в me-ID. Все тестовые пользователи для этого сценария должны иметь номер мобильного телефона и офис в своем профиле ME-ID, который можно добавить через портал Azure.

В следующей фабрике пользовательских учетных записей пользователей:

  • Значение (ILoggerlogger) включается для удобства, если вы хотите регистрировать сведения или ошибки в методе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;

Пример в этом разделе основан на подходе чтения базового URL-адреса с версиями и областями из конфигурации приложения с помощью MicrosoftGraph раздела в wwwroot/appsettings.json файле. Следующие строки уже должны присутствовать в 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>

При локальном тестировании с помощью пакета SDK Graph рекомендуется использовать новый сеанс браузера InPrivate/incognito для каждого теста, чтобы предотвратить вмешательство в тесты с помощью файлов cookie. Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Следующее руководство относится к Microsoft Graph версии 4. Если вы обновляете приложение из пакета SDK версии 4 до версии 5, ознакомьтесь с руководством по изменению и обновлению пакета SDK для Microsoft Graph для .NET версии 5.

Пакет SDK Microsoft Graph для использования в Blazor приложениях называется клиентской библиотекой Microsoft Graph .NET.

В примерах пакета SDK Graph требуются следующие ссылки на пакеты в автономном Blazor WebAssembly приложении. На первые два пакета уже ссылается, если приложение было включено для проверки подлинности MSAL, например при создании приложения, следуя инструкциям в разделе "Защита автономного приложения ASP.NET CoreBlazor WebAssembly" с помощью идентификатора Microsoft Entra.

В примерах пакета SDK Graph требуются следующие ссылки на пакеты в автономном Blazor WebAssembly приложении или Client приложении размещенного Blazor WebAssembly решения. На первые два пакета уже ссылается, если приложение было включено для проверки подлинности MSAL, например при создании приложения, следуя инструкциям в разделе "Защита автономного приложения ASP.NET CoreBlazor WebAssembly" с помощью идентификатора Microsoft Entra.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

В портал Azure предоставьте делегированные разрешения (области)† для данных Microsoft Graph, к которым приложение должно иметь доступ от имени пользователя. Например, в этой статье регистрация приложения должна включать делегированное разрешение на чтение данных пользователя (Microsoft.Graph>User.Read область в разрешениях API, тип: Делегировано). Область User.Read позволяет пользователям входить в приложение и позволяет приложению считывать сведения о профиле и компании пользователей, вошедшего в систему. Дополнительные сведения см. в разделе "Обзор разрешений и согласия" на платформе Майкрософт identity и обзор разрешений Microsoft Graph.

†Permissions и области означают то же самое и используются взаимозаменяемо в документации по безопасности и портал Azure. Если текст не ссылается на портал Azure, в этой статье используются /области областей при обращении к разрешениям Graph.

Области являются нечувствительными к регистру, поэтому User.Read они совпадают user.read. Вы можете использовать любой формат, но мы рекомендуем согласованный выбор в коде приложения.

После добавления областей API Microsoft Graph в регистрацию приложения в портал Azure добавьте следующую конфигурацию wwwroot/appsettings.json параметров приложения в файл в приложении, который включает базовый URL-адрес Graph с версией и областями Microsoft Graph. В следующем примере User.Read область указывается для примеров в последующих разделах этой статьи. Области не учитывает регистр.

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

В предыдущем примере заполнитель — это версия API Microsoft Graph (например: {VERSION} v1.0).

Ниже приведен пример полного wwwroot/appsettings.json файла конфигурации для приложения, использующего ME-ID в качестве identity поставщика, где для Microsoft Graph заданы данные пользователя (user.read область).

{
  "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} заполнитель — идентификатор приложения (клиента). Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Добавьте следующий GraphClientExtensions класс в автономное приложение. Области предоставляются Scopes свойству AccessTokenRequestOptions метода AuthenticateRequestAsync . Значение IHttpProvider.OverallTimeout по умолчанию расширено от 100 секунд до 300 секунд, чтобы получить HttpClient больше времени для получения ответа от Microsoft Graph.

Добавьте следующий класс GraphClientExtensions в изолированное приложение или приложение Client размещенного Blazor WebAssemblyрешения . Области предоставляются Scopes свойству AccessTokenRequestOptions метода AuthenticateRequestAsync . Значение 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в разделе "ПротивAdditionalScopesToConsent".DefaultAccessTokenScopes

В файле добавьте клиентские Program службы Graph и конфигурацию с AddGraphClient помощью метода расширения:

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

Вызов API Graph из компонента с помощью пакета SDK для Graph

UserData Следующий компонент использует внедренный GraphServiceClient объект для получения данных профиля ME-ID пользователя и отображения номера мобильного телефона. Для любого тестового пользователя, который вы создаете в ME-ID, убедитесь, что вы предоставите профилю ME-ID пользователя номер мобильного телефона в портал Azure.

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>

Совет

Сведения о добавлении пользователей в приложение см. в разделе "Назначение пользователей регистрации приложения" с ролями приложений или без нее.

При локальном тестировании с помощью пакета SDK Graph рекомендуется использовать новый сеанс браузера InPrivate/incognito для каждого теста, чтобы предотвратить вмешательство в тесты с помощью файлов cookie. Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Настройка утверждений пользователей с помощью пакета SDK Graph

В следующем примере приложение создает мобильный номер телефона и утверждения о расположении офиса для пользователя из данных профиля пользователя ME-ID. Приложение должно иметь область API Graph, настроенную User.Read в me-ID. Все тестовые пользователи для этого сценария должны иметь номер мобильного телефона и офис в своем профиле ME-ID, который можно добавить через портал Azure.

В следующей фабрике пользовательских учетных записей пользователей:

  • Значение (ILoggerlogger) включается для удобства, если вы хотите регистрировать сведения или ошибки в методе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;

Пример в этом разделе основан на подходе чтения базового URL-адреса с версиями и областями из конфигурации приложения с помощью MicrosoftGraph раздела в wwwroot/appsettings.json файле. Следующие строки уже должны присутствовать в 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>

При локальном тестировании с помощью пакета SDK Graph рекомендуется использовать новый сеанс браузера InPrivate/incognito для каждого теста, чтобы предотвратить вмешательство в тесты с помощью файлов cookie. Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

В следующих примерах используется именованный HttpClient для вызовов API Graph для получения мобильного номера телефона пользователя для обработки звонка или настройки утверждений пользователя для включения утверждения номера мобильного телефона и утверждения расположения офиса.

В примерах требуется ссылка на пакет для Microsoft.Extensions.Http автономного Blazor WebAssembly приложения.

В примерах требуется ссылка на пакет для Microsoft.Extensions.Http автономного Blazor WebAssembly приложения или Client приложения размещенного Blazor WebAssembly решения.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в разделе Способы установки пакетов NuGet в статье Рабочий процесс использования пакета (документация по NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

В портал Azure предоставьте делегированные разрешения (области)† для данных Microsoft Graph, к которым приложение должно иметь доступ от имени пользователя. Например, в этой статье регистрация приложения должна включать делегированное разрешение на чтение данных пользователя (Microsoft.Graph>User.Read область в разрешениях API, тип: Делегировано). Область User.Read позволяет пользователям входить в приложение и позволяет приложению считывать сведения о профиле и компании пользователей, вошедшего в систему. Дополнительные сведения см. в разделе "Обзор разрешений и согласия" на платформе Майкрософт identity и обзор разрешений Microsoft Graph.

†Permissions и области означают то же самое и используются взаимозаменяемо в документации по безопасности и портал Azure. Если текст не ссылается на портал Azure, в этой статье используются /области областей при обращении к разрешениям Graph.

Области являются нечувствительными к регистру, поэтому User.Read они совпадают user.read. Вы можете использовать любой формат, но мы рекомендуем согласованный выбор в коде приложения.

После добавления областей API Microsoft Graph в регистрацию приложения в портал Azure добавьте следующую конфигурацию wwwroot/appsettings.json параметров приложения в файл в приложении, который включает базовый URL-адрес Graph с версией и областями Microsoft Graph. В следующем примере User.Read область указывается для примеров в последующих разделах этой статьи. Области не учитывает регистр.

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

В предыдущем примере заполнитель — это версия API Microsoft Graph (например: {VERSION} v1.0).

Ниже приведен пример полного wwwroot/appsettings.json файла конфигурации для приложения, использующего ME-ID в качестве identity поставщика, где для Microsoft Graph заданы данные пользователя (user.read область).

{
  "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} заполнитель — идентификатор приложения (клиента). Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Создайте следующий GraphAuthorizationMessageHandler класс и конфигурацию проекта в Program файле для работы с API Graph. Базовый 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 В файле настройте именованный HttpClient API Graph:

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, который управляет собственными областями внедрения зависимостей. Дополнительные сведения см. на следующих ресурсах:

Требуется косая черта (/) в базовом адресе. В приведенном выше коде третий аргумент string.Join заключается string.Empty в том, чтобы обеспечить наличие https://graph.microsoft.com/v1.0/косой черты:

Вызов API Graph из компонента с помощью именованного HttpClient

Класс UserInfo.cs назначает требуемые свойства профиля пользователя атрибутом JsonPropertyNameAttribute и именем JSON, используемым ME-ID. В следующем примере настраивается свойства для номера мобильного телефона пользователя и расположения офиса.

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 компоненте HttpClient создается API Graph для выдачи запроса на данные профиля пользователя. Ресурс me (me) добавляется в базовый URL-адрес с версией запроса API Graph. Данные JSON, возвращаемые Graph, десериализируются в 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>

Совет

Сведения о добавлении пользователей в приложение см. в разделе "Назначение пользователей регистрации приложения" с ролями приложений или без нее.

В следующей последовательности описывается новый поток пользователя для областей API Graph:

  1. Новый пользователь впервые входит в приложение.
  2. Пользователь дает согласие на использование приложения в пользовательском интерфейсе согласия Azure.
  3. Пользователь обращается к странице компонента, которая запрашивает данные API Graph в первый раз.
  4. Пользователь перенаправляется в пользовательский интерфейс согласия Azure для предоставления согласия на области API Graph.
  5. Возвращается данные пользователя API Graph.

Если вы предпочитаете, что подготовка области (согласие для областей API Graph) выполняется при первоначальном входе, укажите области проверки подлинности 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в разделе "ПротивAdditionalScopesToConsent".DefaultAccessTokenScopes

При внесении предыдущих изменений в приложение поток пользователя принимает следующую последовательность:

  1. Новый пользователь впервые входит в приложение.
  2. Пользователь дает согласие на использование областей API приложения и Graph в пользовательском интерфейсе согласия Azure.
  3. Пользователь обращается к странице компонента, которая запрашивает данные API Graph в первый раз.
  4. Возвращается данные пользователя API Graph.

При локальном тестировании с помощью API Graph рекомендуется использовать новый сеанс браузера InPrivate/incognito для каждого теста, чтобы предотвратить вмешательство в тестирование файлов cookie. Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Настройка утверждений пользователей с помощью именованного HttpClient

В следующем примере приложение создает мобильный номер телефона и утверждения о расположении офиса для пользователя из данных профиля пользователя ME-ID. Приложение должно иметь область API Graph, настроенную User.Read в me-ID. Тестовые учетные записи пользователей в ME-ID требуют записи для номера мобильного телефона и расположения офиса, которые можно добавить с помощью портал Azure в профили пользователей.

Если вы еще не добавили UserInfo класс в приложение, следуя инструкциям, приведенным ранее в этой статье, добавьте следующий класс и назначьте требуемые свойства профиля пользователя атрибутом JsonPropertyNameAttribute и именем JSON, используемым ME-ID. В следующем примере настраивается свойства для номера мобильного телефона пользователя и расположения офиса.

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

В следующей фабрике пользовательских учетных записей пользователей:

  • Значение (ILoggerlogger) включается для удобства, если вы хотите регистрировать сведения или ошибки в методе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 Graph рекомендуется использовать новый сеанс браузера InPrivate/incognito для каждого теста, чтобы предотвратить вмешательство в тестирование файлов cookie. Дополнительные сведения см. в статье "Защита автономного приложения ASP.NET Core Blazor WebAssembly с помощью идентификатора Microsoft Entra.

Назначение пользователям регистрации приложения с ролями приложения или без нее

Вы можете добавить пользователей в регистрацию приложения и назначить роли пользователям, выполнив следующие действия в портал Azure.

Чтобы добавить пользователя, выберите "Пользователи" в области "ME-ID" портал Azure:

  1. Выберите "Создать пользователя>".
  2. Используйте шаблон создания пользователя.
  3. Укажите сведения о пользователе в Identity этой области.
  4. Вы можете создать исходный пароль или назначить начальный пароль, который пользователь изменяет при первом входе. Если вы используете пароль, созданный порталом, запишите его сейчас.
  5. Нажмите кнопку "Создать" , чтобы создать пользователя. При закрытии нового пользовательского интерфейса выберите "Обновить ", чтобы обновить список пользователей и показать нового пользователя.
  6. В примерах этой статьи назначьте номер мобильного телефона новому пользователю, выбрав свое имя из списка пользователей, выбрав свойства и изменив контактные данные для предоставления номера мобильного телефона.

Чтобы назначить пользователей приложению без ролей приложения:

  1. В области me-ID портал Azure откройте корпоративные приложения.
  2. Выберите приложение из списка.
  3. Выберите Пользователи и группы
  4. Выберите Добавить пользователя или группу.
  5. Выберите пользователя.
  6. Нажмите кнопку Назначить.

Чтобы назначить пользователей приложению ролями приложения, выполните следующие действия.

  1. Добавьте роли в регистрацию приложения в портал Azure после указания в ASP.NET Core Blazor WebAssembly с группами и ролями идентификаторов Microsoft Entra.
  2. В области me-ID портал Azure откройте корпоративные приложения.
  3. Выберите приложение из списка.
  4. Выберите Пользователи и группы
  5. Выберите Добавить пользователя или группу.
  6. Выберите пользователя и выберите свою роль для доступа к приложению. Несколько ролей назначаются пользователю, повторяя процесс добавления пользователя в приложение до тех пор, пока не будут назначены все роли для пользователя. Пользователи с несколькими ролями указываются один раз для каждой назначенной роли в списке пользователей и групп для приложения.
  7. Нажмите кнопку Назначить.

DefaultAccessTokenScopes и AdditionalScopesToConsent

Примеры, приведенные в этой статье, подготавливают области API Graph, а DefaultAccessTokenScopesне AdditionalScopesToConsent.

AdditionalScopesToConsent не используется, так как не удается подготовить области API Graph для пользователей при первом входе в приложение с помощью MSAL через пользовательский интерфейс согласия Azure. Когда пользователь пытается получить доступ к API Graph впервые с помощью пакета SDK Graph, он сталкивается с исключением:

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

После того, как пользователь подготавливает области DefaultAccessTokenScopesAPI Graph через, приложение может использовать AdditionalScopesToConsent для последующего входа пользователя. Однако изменение кода приложения не имеет смысла для рабочего приложения, которое требует периодического добавления новых пользователей с делегированными областями Graph или добавления новых делегированных областей API Graph в приложение.

Предыдущее обсуждение способа подготовки областей доступа к API Graph при первом входе пользователя в приложение применяется только к:

  • Приложения, использующие пакет SDK Graph.
  • Приложения, использующие доступ к API Graph с именем HttpClient , которые запрашивают у пользователей согласие на области Graph при первом входе в приложение.

При использовании именованного HttpClient , который не запрашивает у пользователей согласие на области Graph при первом входе, пользователи перенаправляются в пользовательский интерфейс согласия Azure для областей API Graph при первом запросе доступа к API Graph с помощью DelegatingHandler предварительно настроенного имени HttpClient. Если области Graph изначально не предоставляются с помощью именованного HttpClient подхода, ни DefaultAccessTokenScopes AdditionalScopesToConsent вызываются приложением. Дополнительные сведения см. в описании именованного HttpClient покрытия в этой статье.

Размещенные Blazor WebAssembly решения

Примеры, описанные в этой статье, относятся к использованию пакета SDK Graph или именованного HttpClient API Graph непосредственно из автономного Blazor WebAssembly приложения или непосредственно из Client приложения размещенного Blazor WebAssemblyрешения. Дополнительный сценарий, который не рассматривается в этой статье, предназначен для приложения размещенного решения для вызова Server приложения решения через веб-API, а затем Server приложение использует пакет SDK или API Graph для Client вызова Microsoft Graph и возврата данных в Client приложение. Хотя это поддерживаемый подход, он не рассматривается в этой статье. Если вы хотите применить этот подход:

  • Следуйте инструкциям в руководстве по вызову веб-API из приложения ASP.NET Core Blazor для аспектов веб-API по выдаче запросов Server приложению из Client приложения и возврате данных Client в приложение.
  • Следуйте указаниям в основной документации по Microsoft Graph, чтобы использовать пакет SDK Graph с типичным приложением ASP.NET Core, которое в этом сценарии является Server приложением решения. Если вы используете Blazor WebAssembly шаблон проекта для создания размещенного Blazor WebAssembly решения (ASP.NET Core Hosted-h|--hosted/) с авторизацией организации (одной организацией илиSingleOrg несколькими организациями/MultiOrg) и параметром Microsoft Graph (identitymicrosoft platform>Connected Services>Add Microsoft Graph permissions in Visual Studio или --calls-graph параметром с помощью команды .NET CLI),Server dotnet new приложение решения настроено на использование пакета SDK Graph при создании решения из шаблона проекта.

Дополнительные ресурсы

Общее руководство

Руководство по безопасности