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


Защита размещенного приложения ASP.NET Core Blazor WebAssembly с помощью IdentityServer

Внимание

Шаблон проекта Hosted Blazor WebAssembly был удален из платформы при выпуске .NET 8 (ноября 2023 г.). Рекомендации, приведенные в этой статье, поддерживаются только для .NET 7 или более ранних версий. Облачные Blazor WebAssembly приложения, которые обновляются в каждом релизе, продолжают получать техническую поддержку. В качестве альтернативы, рефакторируйте приложение в автономное приложение Blazor WebAssembly или Blazor Web App.

В этой статье описано, как создать новое размещенное решение Blazor WebAssembly, которое использует Duende Identity Server для проверки подлинности пользователей и вызовов API.

Внимание

Компания Duende Software может потребовать лицензионный сбор за использование Duende IdentityServer в рабочей среде. Дополнительные сведения см. в статье Миграция с ASP.NET Core 5.0 на 6.0.

Примечание.

Чтобы настроить автономное или размещенное приложение Blazor WebAssembly для использования существующего внешнего экземпляра Identity Server, следуйте указаниям в статье Защита изолированного приложения ASP.NET Core Blazor WebAssembly с помощью библиотеки проверки подлинности.

Для получения дополнительных сведений о сценариях безопасности после прочтения этой статьи, см. раздел дополнительные сценарии безопасности ASP.NET Core.

Пошаговое руководство

В подразделах пошагового руководства объясняется, как:

  • Blazor Создание приложения
  • Выполнить приложение

Создание приложения Blazor

Чтобы создать новый проект Blazor WebAssembly с механизмом аутентификации, выполните следующие действия:

  1. Создание проекта

  2. Выберите шаблон приложения Blazor WebAssembly. Выберите Далее.

  3. Укажите имя проекта без использования дефисов. Проверьте правильность расположения. Выберите Далее.

    Избегайте использования дефисов (-) в имени проекта, который нарушает формирование идентификатора приложения OIDC. Логика в шаблоне Blazor WebAssembly проекта использует имя проекта для идентификатора приложения OIDC в конфигурации решения, а дефисы не допускаются в идентификаторе приложения OIDC. Варианты в стиле Паскаля (BlazorSample) или с подчеркиванием (Blazor_Sample) также допустимы.

  4. В диалоговом окне "Дополнительные сведения" выберите отдельные учетные записи в качестве типа проверки подлинности для хранения пользователей в приложении с помощью системы ASP.NET CoreIdentity.

  5. Установите флажок Размещение в ASP.NET Core.

  6. Нажмите кнопку Создать, чтобы создать приложение.

Выполнить приложение

Запустите приложение из Server проекта. При использовании Visual Studio выполните одно из следующих действий.

  • Щелкните стрелку раскрывающегося списка рядом с кнопкой "Запустить ". Откройте раздел "Настройка запускаемых проектов " из раскрывающегося списка. Выберите параметр "Единый запуск проекта ". Подтвердите или измените проект запускаемого проекта на Server проект.

  • Убедитесь, что Server проект выделен в Обозревателе решений перед тем как запустить приложение одним из следующих подходов:

    • Нажмите кнопку Запустить.
    • В меню выберите Отладка>Начать отладку.
    • Нажмите клавишу F5.
  • В командной оболочке перейдите в Server папку проекта решения. Выполните команду dotnet watch (или dotnet run).

Части решения

В этом разделе описываются части решения, созданного с использованием шаблона проекта Blazor WebAssembly, и объясняется, как проекты решений Client и Server настроены для справки. В этом разделе нет конкретных рекомендаций для базового рабочего приложения, если вы создали приложение с помощью руководства в разделе "Пошаговое руководство ". Руководство в этом разделе полезно для обновления приложения для проверки подлинности и авторизации пользователей. Однако альтернативный подход к обновлению приложения — создать новое приложение из руководства в разделе "Пошаговое руководство " и переместить компоненты, классы и ресурсы приложения в новое приложение.

Server службы приложений

Этот раздел относится к приложению Server решения.

Регистрируются следующие службы.

  • В файле Program:

    • Entity Framework Core и ASP.NET Core Identity:

      builder.Services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite( ... ));
      builder.Services.AddDatabaseDeveloperPageExceptionFilter();
      
      builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Сервер с дополнительным AddApiAuthorization вспомогательным методом, который настраивает соглашения по умолчанию ASP.NET Core поверх Identity сервера:

      builder.Services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Проверка подлинности с помощью дополнительного AddIdentityServerJwt вспомогательного метода, который настраивает приложение для проверки маркеров JWT, созданных сервером Identity :

      builder.Services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • В Startup.ConfigureServices из Startup.cs:

    • Entity Framework Core и ASP.NET Core Identity:

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(
              Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      

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

      Не сохраняйте секреты приложений, строка подключения, учетные данные, пароли, персональные идентификационные номера (ПИН-коды), частный код C#/.NET или закрытые ключи и токены в клиентском коде, который всегда небезопасн. В тестовых, промежуточных и рабочих средах код на стороне Blazor сервера и веб-API должен использовать безопасные потоки проверки подлинности, избегая хранения учетных данных в файлах кода проекта или конфигурации. Вне локального тестирования разработки рекомендуется избегать использования переменных среды для хранения конфиденциальных данных, так как переменные среды не являются наиболее безопасным подходом. Для локального тестирования разработки средство Secret Manager рекомендуется для защиты конфиденциальных данных. Дополнительные сведения см. в разделе "Безопасное обслуживание конфиденциальных данных и учетных данных".

    • Identity Сервер с дополнительным AddApiAuthorization вспомогательным методом, который настраивает соглашения по умолчанию ASP.NET Core поверх Identity сервера:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Проверка подлинности с помощью дополнительного AddIdentityServerJwt вспомогательного метода, который настраивает приложение для проверки маркеров JWT, созданных сервером Identity :

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Примечание.

При регистрации одной схемы проверки подлинности, эта схема автоматически используется в качестве схемы по умолчанию приложения, и нет необходимости указывать схему в AddAuthentication или через AuthenticationOptions. Дополнительные сведения см. в разделе «Обзор проверки подлинности ASP.NET Core» и объявления ASP.NET Core (aspnet/Announcements #490).

  • В файле Program:
  • В Startup.Configure из Startup.cs:
  • Серверное промежуточное ПО Identity предоставляет конечные точки OpenID Connect (OIDC):

    app.UseIdentityServer();
    
  • Промежуточное программное обеспечение для проверки подлинности отвечает за проверку учетных данных запроса и установку пользователя в контексте запроса.

    app.UseAuthentication();
    
  • Промежуточное ПО для авторизации предоставляет возможности авторизации:

    app.UseAuthorization();
    

Авторизация API

Этот раздел относится к приложению решения Server.

Вспомогательный метод AddApiAuthorization настраивает Identity Server для сценариев ASP.NET Core. Identity Server — это функциональная и расширяемая платформа для повышения уровня безопасности приложений. Identity Сервер предоставляет ненужную сложность для наиболее распространенных сценариев. Следовательно, предусмотрен набор соглашений и параметров конфигурации, которые можно рассматривать в качестве хорошей отправной точки. Когда ваши потребности в проверке подлинности меняются, полный потенциал сервера Identity доступен для настройки проверки подлинности в соответствии с требованиями приложения.

Добавление обработчика проверки подлинности для API, который сосуществует с Identity сервером

Этот раздел относится к приложению Server решения.

Вспомогательный метод AddIdentityServerJwt настраивает схему политики для приложения в качестве обработчика проверки подлинности по умолчанию. Политика настроена так, чтобы разрешить Identity обрабатывать все запросы, перенаправленные на любой подпуть в пространстве URL-адресов под /Identity в Identity. JwtBearerHandler обрабатывает все остальные запросы. Кроме того, этот метод:

  • Регистрирует ресурс API на Identity сервере с областью {PROJECT NAME}API действия по умолчанию, где заполнитель {PROJECT NAME} — это имя проекта при создании приложения.
  • Настраивает промежуточное ПО для токена JWT Bearer, чтобы проверять токены, выданные сервером Identity для приложения.

Контроллер прогноза погоды

Этот раздел относится к приложению Server решения.

В WeatherForecastController (Controllers/WeatherForecastController.cs) к классу применяется атрибут [Authorize]. Атрибут указывает, что пользователь должен быть авторизован на основе политики по умолчанию, чтобы получить доступ к ресурсу. Политика авторизации по умолчанию настроена на использование схемы аутентификации по умолчанию, которая устанавливается при помощи AddIdentityServerJwt. Вспомогательный метод настраивает JwtBearerHandler в качестве обработчика по умолчанию для запросов к приложению.

Контекст базы данных приложения

Этот раздел относится к приложению Server решения.

В поле ApplicationDbContext (Data/ApplicationDbContext.cs) DbContext расширяется ApiAuthorizationDbContext<TUser> , чтобы включить схему для Identity сервера. Класс ApiAuthorizationDbContext<TUser> является производным от IdentityDbContext.

Чтобы получить полный контроль над схемой базы данных, наследуйте один из доступных классов IdentityDbContext и настройте контекст для включения схемы Identity путем вызова builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) в методе OnModelCreating.

Контроллер конфигурации OIDC

Этот раздел относится к приложению Server решения.

В OidcConfigurationController (Controllers/OidcConfigurationController.cs) выполняется настройка конечной точки клиента для работы с параметрами OIDC.

Параметры приложения

Этот раздел относится к программному обеспечению решения Server.

В файле параметров приложения (appsettings.json) в корневом каталоге проекта в разделе IdentityServer описывается список настроенных клиентов. В следующем примере есть один клиент. Имя клиента соответствует Client имени сборки приложения и сопоставляется согласно соглашению с параметром OAuth ClientId. Профиль указывает на настраиваемый тип приложения. Профиль используется внутри для обозначения соглашений, упрощающих процесс настройки сервера.

"IdentityServer": {
  "Clients": {
    "{ASSEMBLY NAME}": {
      "Profile": "IdentityServerSPA"
    }
  }
}

Заполнитель {ASSEMBLY NAME} — это имя сборки приложения Client (например, BlazorSample.Client).

Пакет проверки подлинности

Этот раздел относится к приложению Client решения.

Когда приложение создается для использования отдельных учетных записей пользователей (Individual), оно автоматически получает ссылку на пакет Microsoft.AspNetCore.Components.WebAssembly.Authentication. В пакете содержится набор примитивов, которые помогают приложению проверять подлинность пользователей и получать маркеры для вызова защищенных API.

При добавлении проверки подлинности в приложение вручную добавьте пакет Microsoft.AspNetCore.Components.WebAssembly.Authentication в приложение.

Примечание.

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

HttpClientКонфигурация

Этот раздел относится к приложению Client решения.

В файле Program настройка HttpClient выполняется для предоставления экземпляров HttpClient, которые включают в себя токены доступа при выполнении запросов к API сервера. При создании решения, обозначенного как HttpClient, используется {PROJECT NAME}.ServerAPI, где заполнитель {PROJECT NAME} соответствует имени проекта.

builder.Services.AddHttpClient("{PROJECT NAME}.ServerAPI", 
        client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("{PROJECT NAME}.ServerAPI"));

Заполнитель {PROJECT NAME} указывает на имя проекта во время создания решения. Например, указав имя проекта BlazorSample, вы получите результат с именем HttpClient и BlazorSample.ServerAPI.

Примечание.

Если вы хотите настроить приложение Blazor WebAssembly на использование существующего экземпляра IdentityServer, который не является частью размещенного решения Blazor, измените регистрацию базового адреса HttpClient с IWebAssemblyHostEnvironment.BaseAddress (builder.HostEnvironment.BaseAddress) на URL-адрес конечной точки авторизации API серверного приложения.

Поддержка авторизации API

Этот раздел относится к Client приложению решения.

Поддержка проверки подлинности пользователей подключается в контейнере службы с помощью метода расширения, предоставляемого в пакете Microsoft.AspNetCore.Components.WebAssembly.Authentication. Этот метод настраивает службы, необходимые для взаимодействия с существующей системой авторизации.

builder.Services.AddApiAuthorization();

Конфигурация для приложения загружается по умолчанию из _configuration/{client-id}. По соглашению в качестве идентификатора клиента используется имя сборки приложения. Этот URL-адрес можно изменить, чтобы он указывал на отдельную конечную точку, используя вызов перегрузки с опциями.

Файл Imports

Этот раздел относится к приложению Client решения.

Пространство имен Microsoft.AspNetCore.Components.Authorization становится доступным для всего приложения с помощью файла _Imports.razor:

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using {APPLICATION ASSEMBLY}
@using {APPLICATION ASSEMBLY}.Shared

Index страница

Этот раздел относится к приложению Client решения.

Страница индекса (wwwroot/index.html) содержит сценарий, определяющий AuthenticationService в JavaScript. AuthenticationService обрабатывает низкоуровневые сведения о протоколе OIDC. Приложение внутренне вызывает методы, определенные в сценарии для выполнения операций проверки подлинности.

<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>

Компонент App

Этот раздел относится к приложению решения Client.

Компонент App (App.razor) аналогичен компоненту App, который находится в приложениях Blazor Server:

  • Компонент CascadingAuthenticationState обеспечивает видимость AuthenticationState для остальной части приложения.
  • Компонент AuthorizeRouteView гарантирует, что текущий пользователь имеет право доступа к определенной странице или иным образом работать с компонентом RedirectToLogin.
  • Компонент RedirectToLogin управляет перенаправлением неавторизованных пользователей на страницу входа.

Из-за различий в платформе между выпусками ASP.NET Core в этом разделе отсутствует разметка Razor для компонента App (App.razor). Чтобы изучить разметку этого компонента для конкретного выпуска, используйте один из следующих подходов:

  • создайте приложение, подготовленное для проверки подлинности, на основе шаблона проекта по умолчанию Blazor WebAssembly для версии ASP.NET Core, которую предполагается использовать. Изучите компонент App (App.razor) в созданном приложении;

  • изучите компонент App (App.razor) в справочных материалах. Выберите версию из селектора ветви и найдите компонент в ProjectTemplates папке репозитория, так как App расположение компонента изменилось в течение многих лет.

    Примечание.

    По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение веток или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Компонент RedirectToLogin

Этот раздел относится к приложению решения Client.

Компонент RedirectToLogin (RedirectToLogin.razor):

  • управляет перенаправлением неавторизованных пользователей на страницу входа.
  • Текущий URL-адрес, к которому пользователь пытается получить доступ, сохраняется, чтобы пользователь мог вернуться на эту страницу, если проверка подлинности выполнена успешно.

Изучите компонент RedirectToLogin в справочных материалах. Расположение компонента изменилось с течением времени, поэтому используйте средства поиска GitHub для поиска компонента.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно открывается ветвь репозитория по умолчанию, которая представляет текущую разработку следующей версии выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Компонент LoginDisplay

Этот раздел относится к приложению Client решения.

Компонент LoginDisplay (LoginDisplay.razor) отображается в компоненте MainLayout (MainLayout.razor) и управляет следующими поведениями.

  • Для прошедших проверку подлинности пользователей:
    • Отображает имя текущего пользователя.
    • Отображает ссылку на страницу профиля пользователя в ASP.NET Core Identity.
    • предлагает кнопку для выхода из приложения.
  • Для анонимных пользователей:
    • Предоставляет возможность регистрации.
    • Предоставляет возможность входа в систему.

Из-за различий в платформе между выпусками ASP.NET Core в этом разделе отсутствует разметка Razor для компонента LoginDisplay. Чтобы изучить разметку этого компонента для конкретного выпуска, используйте один из следующих подходов:

  • создайте приложение, подготовленное для проверки подлинности, на основе шаблона проекта по умолчанию Blazor WebAssembly для версии ASP.NET Core, которую предполагается использовать. Изучите компонент LoginDisplay в созданном приложении.

  • Изучите компонент LoginDisplay в справочных материалах. Расположение компонента изменилось с течением времени, поэтому используйте средства поиска GitHub для поиска компонента. В качестве шаблонного содержимого используется Hosted, равно true.

    Примечание.

    Документционные ссылки на справочные материалы по .NET обычно ведут на основную ветвь репозитория, которая отображает текущие разработки для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте выпадающий список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Компонент Authentication

Этот раздел относится к приложению решения Client.

Страница, созданная компонентом Authentication (Pages/Authentication.razor), определяет маршруты, необходимые для обработки различных этапов проверки подлинности.

Компонент RemoteAuthenticatorView:

@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="@Action" />

@code {
    [Parameter]
    public string? Action { get; set; }
}

Примечание.

Типы ссылок, допускающие значение NULL (NRT), и статический анализ состояния NULL компилятором .NET поддерживаются в ASP.NET Core в .NET 6 или более поздних версиях. До выпуска ASP.NET Core в .NET 6 тип string появляется без обозначения типа null (?).

Компонент FetchData

Этот раздел относится к приложению Client решения.

Компонент FetchData показывает, как:

  • выдать токен доступа
  • использовать маркер доступа для вызова API защищенных ресурсов в приложении Server.

Директива @attribute [Authorize] указывает на систему авторизации Blazor WebAssembly, в которой пользователь должен пройти авторизацию для перехода к этому компоненту. Наличие атрибута в приложении Client не мешает вызову API на сервере без соответствующих учетных данных. Приложение Server также должно использовать [Authorize] на соответствующих конечных точках, чтобы правильно защитить их.

IAccessTokenProvider.RequestAccessToken готовит запрос на получение маркера доступа, который можно добавить в запрос для вызова API. Если маркер кэшируется или служба может подготовить новый маркер доступа без вмешательства пользователя, запрос маркера будет выполнен. В противном случае запрос токена завершается ошибкой AccessTokenNotAvailableException, которая перехватывается инструкцией try-catch.

Чтобы получить фактический токен для включения в запрос, приложение должно проверить, что запрос был обработан, вызвав tokenResult.TryGetToken(out var token).

Если запрос был успешным, переменная токена заполняется токеном доступа. В заголовок запроса Authorization включается буквенная строка, предоставляемая свойством AccessToken.Value маркера.

Если маркер не удалось подготовить без взаимодействия с пользователем, в результате чего произошел сбой запроса:

  • ASP.NET Core в .NET 7 или более поздней версии: приложение переходит на AccessTokenResult.InteractiveRequestUrl, используя заданный AccessTokenResult.InteractionOptions, чтобы разрешить обновление маркера доступа.
  • ASP.NET Core в .NET 6 или более ранней версии: результат токена содержит URL-адрес перенаправления. При переходе по этому URL-адресу пользователь переходит на страницу входа и возвращается к текущей странице после успешной проверки подлинности.
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using {APP NAMESPACE}.Shared
@attribute [Authorize]
@inject HttpClient Http

...

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Служба приложений Azure в Linux

При развертывании в Службе приложений Azure в Linux издателя нужно указать явно. Для получения дополнительной информации см. в разделе «Использование Identity для защиты серверной части веб-API для SPA».

Утверждение имени и роли через авторизацию API

Настраиваемая фабрика пользователей

В приложении Client создайте настраиваемую фабрику пользователей. Identity Сервер отправляет несколько ролей в виде массива JSON в одном ключе role. Одна роль отправляется как строковое значение в заявлении. Фабрика создает отдельное утверждение role для каждой из ролей пользователя.

CustomUserFactory.cs:

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

public class CustomUserFactory(IAccessTokenProviderAccessor accessor)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

            if (roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = 
                    account.AdditionalProperties[identity.RoleClaimType];

                if (options.RoleClaim is not null && rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            var roleValue = role.GetString();

                            if (!string.IsNullOrEmpty(roleValue))
                            {
                                identity.AddClaim(
                                  new Claim(options.RoleClaim, roleValue));
                            }
        
                        }
                    }
                    else
                    {
                        var roleValue = roles.GetString();

                        if (!string.IsNullOrEmpty(roleValue))
                        {
                            identity.AddClaim(
                              new Claim(options.RoleClaim, roleValue));
                        }
                    }
                }
            }
        }

        return user;
    }
}

В приложении Client зарегистрируйте фабрику в файле Program.

builder.Services.AddApiAuthorization()
    .AddAccountClaimsPrincipalFactory<CustomUserFactory>();

В приложении Server вызовите AddRoles на построителе Identity, который добавляет службы, связанные с ролями.

В файле Program:

using Microsoft.AspNetCore.Identity;

...

builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

В Startup.cs:

using Microsoft.AspNetCore.Identity;

...

services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Настройка Identity сервера

Воспользуйтесь одним из перечисленных ниже подходов.

Параметры авторизации API

В приложении Server:

  • Настройте Identity Server для размещения утверждений name и role в токене идентификации и токене доступа.
  • Запретить стандартное сопоставление ролей в обработчике токена JWT.

В файле Program:

using System.IdentityModel.Tokens.Jwt;

...

builder.Services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

В Startup.cs:

using System.IdentityModel.Tokens.Jwt;
using System.Linq;

...

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Служба профилей

В приложении Server создайте реализацию ProfileService.

ProfileService.cs:

using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;

public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}

В приложении Server зарегистрируйте службу профилей в файле Program.

using Duende.IdentityServer.Services;

...

builder.Services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

В приложении Server зарегистрируйте службу профилей в Startup.ConfigureServices из Startup.cs:

using IdentityServer4.Services;

...

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Использование механизмов авторизации

На этом этапе подходы с авторизацией компонентов в приложении Client являются функциональными. Любой из механизмов авторизации в компонентах может использовать роль для авторизации пользователя:

Для User.Identity.Name в приложении Client указывается имя пользователя, которое обычно является адресом электронной почты для входа.

UserManager и SignInManager.

Установите тип утверждения идентификатора пользователя, если серверное приложение требует:

  • UserManager<TUser> или SignInManager<TUser> в конечной точке API.
  • Подробные сведения, связанные с IdentityUser, например имя пользователя, адрес электронной почты или время окончания блокировки.

В Program.cs ASP.NET Core в .NET 6 и более поздних версиях:

using System.Security.Claims;

...

builder.Services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

В Startup.ConfigureServices для версий ASP.NET Core, предшествующих версии 6.0:

using System.Security.Claims;

...

services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

Следующий WeatherForecastController записывает UserName при вызове метода Get.

Примечание.

В следующем примере используется:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using BlazorSample.Server.Models;
using BlazorSample.Shared;

namespace BlazorSample.Server.Controllers;

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController(ILogger<WeatherForecastController> logger, 
        UserManager<ApplicationUser> userManager) : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", 
        "Balmy", "Hot", "Sweltering", "Scorching"
    };

    [HttpGet]
    public async Task<IEnumerable<WeatherForecast>> Get()
    {
        var rng = new Random();

        var user = await userManager.GetUserAsync(User);

        if (user != null)
        {
            logger.LogInformation("User.Identity.Name: {UserIdentityName}", user.UserName);
        }

        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

В предыдущем примере:

  • Пространство имен проекта Server — BlazorSample.Server.
  • Пространство имен проекта Shared — BlazorSample.Shared.

Размещение в Службе приложений Azure с использованием личного домена и сертификата

Следующая инструкция объясняет:

  • Как развертывать хостированное приложение Blazor WebAssembly с Identity Server в Службе приложений Azure с использованием пользовательского домена.
  • как создать и использовать TLS-сертификат для обмена данными по протоколу HTTPS с браузерами. Хотя руководство посвящено использованию сертификата с личным доменом, рекомендации также можно применять при работе с доменом приложений Azure по умолчанию, например contoso.azurewebsites.net.

В этом сценарии размещения не используйте тот же сертификат для ключа подписания токенов Duende Identity Server и защищенной связи HTTPS между сайтом и браузерами.

  • Использование разных сертификатов в этих целях рекомендуется из соображений безопасности, так как позволяет изолировать закрытые ключи разного назначения.
  • Управление TLS-сертификатами для обмена с браузерами производится независимо и не влияет на подписывание токенов Identity Server.
  • Когда Azure Key Vault предоставляет сертификат приложению Службы приложений для привязки к личному домену, Identity Server не может получить тот же сертификат из Azure Key Vault для подписывания токена. Хотя можно настроить Identity Server на использование того же TLS-сертификата из физического пути, размещать сертификаты безопасности в системе управления версиями — плохая практика, которой следует избегать в большинстве случаев.

В следующем руководстве в Azure Key Vault создается самозаверенный сертификат исключительно для подписания токена Identity Server. Конфигурация Identity Server использует сертификат из хранилища ключей через хранилище сертификатов приложения CurrentUser>My. Другие сертификаты, используемые для трафика HTTPS пользовательских доменов, создаются и настраиваются отдельно от сертификата подписи Identity сервера.

Чтобы настроить приложение, Службу приложений Azure и Azure Key Vault для размещения в личном домене с HTTPS, выполните указанные ниже действия.

  1. Создайте план Службы приложений уровня Basic B1 или выше. Для использования личных доменов Службе приложений требуется уровень служб Basic B1 или более высокий.

  2. Создайте PFX-сертификат для безопасного обмена данными между сайтом и браузером (по протоколу HTTPS) с полным доменным именем (FQDN) сайта, которое контролирует организация (например, www.contoso.com), в качестве общего имени. Создайте сертификат с указанными ниже параметрами.

    • Основные области применения
      • проверку цифровой подписи (digitalSignature);
      • шифрование ключей (keyEncipherment).
    • Использование улучшенного/расширенного ключа
      • проверка подлинности клиента (1.3.6.1.5.5.7.3.2);
      • проверка подлинности сервера (1.3.6.1.5.5.7.3.1).

    Чтобы создать сертификат, используйте один из следующих подходов или любое другое подходящее средство или веб-службу.

    Запишите пароль, который будет использоваться позже для импорта сертификата в Azure Key Vault.

    Дополнительные сведения о сертификатах Azure Key Vault см. в статье Azure Key Vault: сертификаты.

  3. Создайте хранилище Azure Key Vault или используйте уже имеющееся в подписке Azure.

  4. В области Сертификаты хранилища ключей импортируйте сертификат сайта PFX. Запишите отпечаток сертификата, который будет использоваться позже в конфигурации приложения.

  5. В Azure Key Vault создайте новый самозаверяющий сертификат для подписания токена Identity Server. Укажите имя сертификата и субъект. Субъект указывается в формате CN={COMMON NAME}, где заполнитель {COMMON NAME} — это общее имя сертификата. Общим именем может быть любая буквенно-цифровая строка. Например, CN=IdentityServerSigning — допустимый предмет сертификата. В разделе Политика выдачи>Расширенная настройка политики используйте настройки по умолчанию. Запишите отпечаток сертификата, который будет использоваться позже в конфигурации приложения.

  6. Перейдите к Службе приложений Azure на портале Azure и создайте Службу приложений со следующей конфигурацией:

    • Опубликовать — Code;
    • Стек среды выполнения установлен на среду выполнения приложения.
    • Для номера SKU и размера убедитесь, что уровень Службы приложений составляет Basic B1 или выше. Для использования личных доменов Службе приложений требуется уровень служб Basic B1 или более высокий.
  7. После того как в Azure будет создана Служба приложений, откройте конфигурацию приложения и добавьте новый параметр, указав записанные ранее отпечатки сертификатов. Ключ параметра приложения — WEBSITE_LOAD_CERTIFICATES. ** Отпечатки сертификата в значении параметра приложения разделяйте запятыми, например:

    • Ключ: WEBSITE_LOAD_CERTIFICATES.
    • Значение: 57443A552A46DB...D55E28D412B943565,29F43A772CB6AF...1D04F0C67F85FB0B1

    На портале Azure параметры приложения сохраняются в два шага: сохраните параметр WEBSITE_LOAD_CERTIFICATES, а затем в верхней части колонки нажмите кнопку Сохранить.

  8. Выберите параметры TLS/SSL приложения. Выберите Сертификаты закрытых ключей (PFX). Используйте процесс Импорт сертификата Key Vault. Дважды выполните процесс, чтобы импортировать и сертификат сайта для обеспечения HTTPS-соединения, и самозаверяющий сертификат для подписания токенов сервера Identity.

  9. Перейдите к колонке Личные домены. На веб-сайте регистратора своего домена используйте IP-адрес и идентификатор проверки личного домена для настройки домена. Типичная конфигурация домена включает в себя следующие параметры:

    • запись A с узлом@ и значением IP-адреса с портала Azure;
    • запись TXT с узломasuid и значением идентификатора проверки, созданным Azure и предоставленным на портале Azure.

    Надлежащим образом сохраните изменения на веб-сайте регистратора домена. Некоторые веб-сайты регистратора требуют двухэтапного процесса для сохранения записей домена: одна или несколько записей сохраняются отдельно, за которым следует обновление регистрации домена с помощью отдельной кнопки.

  10. Вернитесь к колонке Личные домены на портале Azure. Нажмите кнопку Добавить личный домен. Выберите параметр Запись A. Укажите домен и выберите команду Проверить. Если записи домена верны и распространены в Интернете, на портале можно будет нажать кнопку Добавить личный домен.

    На распространение изменений регистрации домена на серверах доменных имен (DNS) в Интернете после их обработки регистратором домена может потребоваться несколько дней. Если записи домена не были обновлены в течение трех рабочих дней, убедитесь в том, что они были правильно заданы на сайте регистратора, и обратитесь в его службу поддержки клиентов.

  11. В колонке Личные доменыСОСТОЯНИЕ SSL домена должно иметь значение Not Secure. Щелкните ссылку Добавить привязку. Выберите HTTPS-сертификат сайта из хранилища ключей для привязки личного домена.

  12. В Visual Studio откройте файл параметров приложения для серверного проекта (appsettings.json или appsettings.Production.json). В конфигурации Identity Server добавьте приведенный ниже раздел Key. Укажите предмет самоподписанного сертификата для ключа Name. В следующем примере в хранилище ключей сертификату присвоено общее имя IdentityServerSigning, поэтому субъект — CN=IdentityServerSigning.

    "IdentityServer": {
    
      ...
    
      "Key": {
        "Type": "Store",
        "StoreName": "My",
        "StoreLocation": "CurrentUser",
        "Name": "CN=IdentityServerSigning"
      }
    },
    
  13. В Visual Studio создайте профиль публикации Службы приложений Azure для серверного проекта. В строке меню выберите: Сборка>Опубликовать>Новый>Azure>Служба приложений Azure (Windows или Linux). Когда Visual Studio подключится к подписке Azure, вы сможете настроить представление ресурсов Azure по типу ресурса. В списке Веб-приложение найдите Службу приложений для приложения и выберите ее. Выберите Готово.

  14. Когда в Visual Studio снова отобразится окно Публикация, зависимости хранилища ключей и службы базы данных SQL Server будут обнаружены автоматически.

    Для службы хранилища ключей не требуется изменять настройки по умолчанию.

    Для тестирования локальная база данных SQLite приложения, настроенная Blazor шаблоном, может быть развернута с приложением без дополнительной настройки. Настройка другой базы данных для Identity Server в рабочей среде выходит за рамки этой статьи. Дополнительные сведения см. в ресурсах, посвященных базам данных, в следующих наборах документации.

  15. Щелкните ссылку Изменить под именем профиля развертывания в верхней части окна. Измените URL-адрес назначения на URL-адрес личного домена сайта (например, https://www.contoso.com). Сохранить параметры.

  16. Публикация приложения. В Visual Studio откроется окно браузера и отправит запрос к сайту на пользовательском домене.

В документации по Azure приводятся дополнительные сведения об использовании служб Azure и личных доменов с привязкой TLS в Службе приложений, включая сведения об использовании записей CNAME вместо записей A. Дополнительные сведения см. на следующих ресурсах:

Мы рекомендуем использовать новое окно браузера в частном режиме (например, режим Microsoft Edge InPrivate или режим Google Chrome Incognito) для каждого теста приложения после изменения приложения, конфигурации приложения или служб Azure в портал Azure. Сохранение файлов cookie из предыдущего тестового запуска может привести к сбою проверки подлинности или авторизации при тестировании сайта, даже если конфигурация сайта правильна. Дополнительные сведения о настройке Visual Studio для открытия нового окна частного браузера для каждого тестового запуска см. в разделе "Файлы cookie" и "Данные сайта".

При изменении конфигурации Службы приложений на портале Azure обновления обычно вступают в силу быстро, но не мгновенно. Иногда, чтобы изменение конфигурации вступило в силу, необходимо немного подождать, пока Служба приложений перезапустится.

При устранении проблемы с загрузкой сертификата для подписи ключа Identity Server выполните приведенную ниже команду в командной оболочке PowerShell Kudu на портале Azure. Она выводит список сертификатов, к которым приложение может получить доступ в хранилище сертификатов CurrentUser>My. Выходные данные включают в себя субъекты и отпечатки сертификатов, которые могут быть полезны при отладке приложения.

Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList

Устранение неполадок

Ведение журнала

Чтобы включить ведение журнала отладки или трассировки для Blazor WebAssembly аутентификации, см. раздел Ведение журнала аутентификации на стороне клиента в статье ASP.NET Core Blazor, с установленным селектором версии статьи на ASP.NET Core 7.0 или новее.

Распространенные ошибки

  • Неправильная настройка приложения или поставщика Identity (IP)

    Наиболее частые ошибки вызваны неправильной настройкой. Ниже приводятся несколько примеров.

    • В зависимости от требований сценария, отсутствующие или неправильные полномочия, экземпляр, идентификатор арендатора, домен арендатора, идентификатор клиента или URI перенаправления не позволяют приложению аутентифицировать клиентов.
    • Неверные области запросов не позволяют клиентам получать доступ к конечным точкам веб-API сервера.
    • Неправильные или отсутствующие разрешения API сервера не позволяют клиентам получить доступ к конечным точкам веб-API сервера.
    • Запуск приложения на порту, который отличается от установленного в URI перенаправления при регистрации приложения IP. Обратите внимание, что порт не требуется для Microsoft Entra ID и приложения, работающего на localhost адресе тестирования разработки. Однако конфигурация порта приложения и порт, на котором выполняется приложение, должны соответствовать для не-localhost адресов.

    В разделах конфигурации этой статьи приведены примеры правильной настройки. Внимательно изучите каждый раздел статьи, проверяя, правильно ли настроено приложение и IP-адрес.

    Если конфигурация верна, выполните приведенные ниже действия.

    • Проанализируйте журналы приложений.

    • Изучите сетевой трафик между клиентским приложением и IP-адресом или серверным приложением с помощью инструментов разработчика браузера. Зачастую точное сообщение об ошибке или сообщение с указанием на то, что вызывает проблему, возвращается клиенту с помощью серверного приложения или приложения поставщика удостоверений после выполнения запроса. Руководство по инструментам разработчика можно найти в следующих статьях:

    • Для выпусков Blazor, где используется веб-токен JSON (JWT), декодируйте содержимое токена, используемого для проверки подлинности клиента или доступа к веб-API сервера, в зависимости от места возникновения проблемы. Дополнительные сведения о проверке содержимого JSON Web Token (JWT) см. в этом разделе.

    Команда разработчиков документации реагирует на отзывы о документах и ошибки в статьях (откройте запрос в разделе отзывов на этой странице), но не может предоставить поддержку продукта. Помощь в устранении неполадок в приложении предоставляют несколько общественных форумов поддержки. Мы рекомендуем следующее:

    Указанные выше форумы не принадлежат корпорации Майкрософт и не управляются ею.

    Чтобы сообщить о воспроизводимых ошибках в работе платформы, которые не связаны с безопасностью и конфиденциальностью, создайте запрос на проблему в подразделении продукта ASP.NET Core. Не создавайте запрос к команде продукта, пока вы тщательно не изучите причину проблемы и не сможете её решить самостоятельно или с помощью сообщества на общедоступном форуме поддержки. Единица продукта не способна устранять неполадки отдельных приложений, которые не работают из-за неправильной конфигурации или вариантов использования с участием сторонних служб. Если отчет является конфиденциальным или чувствительным по своему характеру или описывает потенциальный недостаток безопасности в продукте, который могут использовать злоумышленники, см статью "Отчеты о проблемах безопасности и ошибках" (dotnet/aspnetcoreрепозиторий GitHub).

  • Несанкционированный клиент для ME-ID

    info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Авторизация не удалась. Эти требования не выполнены: DenyAnonymousAuthorizationRequirement: требуется прошедший проверку подлинности пользователь.

    Ошибка при входе через обратный вызов из ME-ID:

    • Ошибка: unauthorized_client
    • Описание: AADB2C90058: The provided application is not configured to allow public clients.

    Чтобы устранить эту ошибку, сделайте следующее:

    1. На портале Azure перейдите к манифесту приложения.
    2. Задайте для атрибута allowPublicClient значение null или true.

Файлы cookie и данные сайта

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

  • файлы cookie входа пользователей;
  • файлы cookie приложения;
  • кэшированные и сохраненные данные сайта.

Один из подходов, позволяющих предотвратить влияние устаревших файлов cookie и данных сайта на тестирование и устранение неполадок заключается в следующем:

  • Настройка браузера
    • Для тестирования используйте браузер, в котором можно настроить удаление всех файлов cookie и данных сайта при каждом закрытии браузера.
    • Убедитесь, что при любых изменениях в приложении, в данных тестового пользователя или в конфигурации поставщика закрытие браузера выполняется вручную или интегрированной средой разработки.
  • Используйте пользовательскую команду, чтобы открыть браузер в режиме InPrivate или Incognito в Visual Studio:
    • Откройте диалоговое окно Просмотр с помощью с помощью кнопки Запуск в Visual Studio.
    • Нажмите кнопку Добавить.
    • Укажите путь к браузеру в поле Программа. Следующие пути к исполняемым файлам являются типичными расположениями установки для Windows 10. Если браузер установлен в другом расположении или вы используете операционную систему, отличную от Windows 10, укажите путь к исполняемому файлу браузера.
      • Microsoft Edge: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox: C:\Program Files\Mozilla Firefox\firefox.exe
    • В поле "Аргументы" укажите параметр командной строки, который браузер использует для открытия в режиме InPrivate или Incognito. Для некоторых браузеров требуется URL-адрес приложения.
      • Microsoft Edge: используйте -inprivate.
      • Google Chrome: используйте --incognito --new-window {URL}, где {URL} заполнитель является URL-адресом для открытия (например, https://localhost:5001).
      • Mozilla Firefox: используйте -private -url {URL}, где заполнителем {URL} является URL-адрес для открытия (например, https://localhost:5001).
    • Введите имя в поле Удобное имя. Например, Firefox Auth Testing.
    • Выберите кнопку ОК.
    • Чтобы не выбирать профиль браузера для каждой операции тестирования с помощью приложения, задайте профиль по умолчанию с помощью кнопки По умолчанию.
    • Убедитесь, что при любых изменениях в приложении, в данных тестового пользователя или в конфигурации поставщика закрытие браузера выполняется интегрированной средой разработки.

Обновление приложений

Работающее приложение может перестать функционировать сразу после обновления SDK .NET Core на машине разработки или изменения версий пакетов в самом приложении. В некоторых случаях при выполнении важного обновления несогласованные пакеты могут нарушить работу приложения. Большинство этих проблем можно исправить следующим образом:

  1. Очистите кэши пакетов NuGet локальных систем, выполнив команду dotnet nuget locals all --clear из командной оболочки.
  2. Удалите папки bin и obj проекта.
  3. Восстановите и перестройте проект.
  4. Удалите все файлы из папки развертывания на сервере, прежде чем повторно развернуть приложение.

Примечание.

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

Server Запуск приложения

При тестировании и устранении неполадок в размещенном решении Blazor WebAssembly, убедитесь, что вы используете приложение из проекта Server.

Проверка пользователя

Следующий User компонент можно использовать непосредственно в приложениях или служить основой для дальнейшей настройки.

User.razor:

@page "/user"
@attribute [Authorize]
@using System.Text.Json
@using System.Security.Claims
@inject IAccessTokenProvider AuthorizationService

<h1>@AuthenticatedUser?.Identity?.Name</h1>

<h2>Claims</h2>

@foreach (var claim in AuthenticatedUser?.Claims ?? Array.Empty<Claim>())
{
    <p class="claim">@(claim.Type): @claim.Value</p>
}

<h2>Access token</h2>

<p id="access-token">@AccessToken?.Value</p>

<h2>Access token claims</h2>

@foreach (var claim in GetAccessTokenClaims())
{
    <p>@(claim.Key): @claim.Value.ToString()</p>
}

@if (AccessToken != null)
{
    <h2>Access token expires</h2>

    <p>Current time: <span id="current-time">@DateTimeOffset.Now</span></p>
    <p id="access-token-expires">@AccessToken.Expires</p>

    <h2>Access token granted scopes (as reported by the API)</h2>

    @foreach (var scope in AccessToken.GrantedScopes)
    {
        <p>Scope: @scope</p>
    }
}

@code {
    [CascadingParameter]
    private Task<AuthenticationState> AuthenticationState { get; set; }

    public ClaimsPrincipal AuthenticatedUser { get; set; }
    public AccessToken AccessToken { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        var state = await AuthenticationState;
        var accessTokenResult = await AuthorizationService.RequestAccessToken();

        if (!accessTokenResult.TryGetToken(out var token))
        {
            throw new InvalidOperationException(
                "Failed to provision the access token.");
        }

        AccessToken = token;

        AuthenticatedUser = state.User;
    }

    protected IDictionary<string, object> GetAccessTokenClaims()
    {
        if (AccessToken == null)
        {
            return new Dictionary<string, object>();
        }

        // header.payload.signature
        var payload = AccessToken.Value.Split(".")[1];
        var base64Payload = payload.Replace('-', '+').Replace('_', '/')
            .PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=');

        return JsonSerializer.Deserialize<IDictionary<string, object>>(
            Convert.FromBase64String(base64Payload));
    }
}

Проверка содержимого JSON Web Token (JWT)

Чтобы декодировать JSON Web Token (JWT), используйте средство jwt.ms (Майкрософт). Значения в пользовательском интерфейсе остаются в браузере.

Пример закодированного JWT (сокращено для отображения):

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1j ... bQdHBHGcQQRbW7Wmo6SWYG4V_bU55Ug_PW4pLPr20tTS8Ct7_uwy9DWrzCMzpD-EiwT5IjXwlGX3IXVjHIlX50IVIydBoPQtadvT7saKo1G5Jmutgq41o-dmz6-yBMKV2_nXA25Q

Пример JWT, декодированного инструментом для приложения, которое проходит проверку подлинности в Azure AAD B2C:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk"
}.{
  "exp": 1610059429,
  "nbf": 1610055829,
  "ver": "1.0",
  "iss": "https://mysiteb2c.b2clogin.com/11112222-bbbb-3333-cccc-4444dddd5555/v2.0/",
  "sub": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
  "aud": "00001111-aaaa-2222-bbbb-3333cccc4444",
  "nonce": "bbbb0000-cccc-1111-dddd-2222eeee3333",
  "iat": 1610055829,
  "auth_time": 1610055822,
  "idp": "idp.com",
  "tfp": "B2C_1_signupsignin"
}.[Signature]

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