Обеспечение безопасности размещенного приложения ASP.NET Core Blazor WebAssembly с использованием сервера Identity.

Important

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

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

Important

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

Note

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

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

Walkthrough

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

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

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

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

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

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

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

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

  4. В диалоговом окне Additional information Выберите Individual Accounts в качестве типа Authentication для хранения пользователей в приложении ASP.NET Core с помощью системы Identity.

  5. Установите флажок ASP.NET Core Hosted.

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

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

Запустите приложение из Server проекта. При использовании Visual Studio либо:

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

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

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

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

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

Пути удаленной проверки подлинности настраиваются с помощью свойства RemoteAuthenticationOptions<TRemoteAuthenticationProviderOptions>.AuthenticationPaths на RemoteAuthenticationApplicationPathsOptions в файле Program приложения. Значения путей по умолчанию для фреймворка см. в справочном источникеdotnet/aspnetcore.

Note

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

Если приложение настраивает путь удаленной проверки подлинности, выполните любой из следующих подходов:

  • Сопоставлять путь в жёстко закодированных строках внутри приложения.

  • Вставить Майкрософт.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationOptions<TRemoteAuthenticationProviderOptions>, чтобы получить настроенное значение по всему приложению. В следующем примере демонстрируется подход к компонентуRedirectToLogin.

    Добавьте следующие Razor директивы в начало файла компонента Razor :

    @using Microsoft.Extensions.Options
    @inject IOptionsSnapshot<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>> RemoteOptions
    

    Измените перенаправление компонента в методе OnInitialized :

    - Navigation.NavigateToLogin("authentication/login");
    + Navigation.NavigateToLogin(RemoteOptions.Get(Options.DefaultName)
    +     .AuthenticationPaths.LogInPath);
    

    Note

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

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

В этом разделе описываются части решения, созданного с использованием шаблона проекта 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 Server с дополнительным вспомогательным методом AddApiAuthorization, который настраивает соглашения ASP.NET Core по умолчанию поверх Identity Server:

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

      Warning

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

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

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

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Note

При регистрации одной схемы проверки подлинности, эта схема автоматически используется в качестве схемы по умолчанию приложения, и нет необходимости указывать схему в 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), оно автоматически получает ссылку на Майкрософт.AspNetCore.Components.WebAssembly.Authentication пакет. В пакете содержится набор примитивов, которые помогают приложению проверять подлинность пользователей и получать маркеры для вызова защищенных API.

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

Note

Рекомендации по добавлению пакетов в приложения .NET можно найти в статьях раздела Установка и управление пакетами на странице Потребление пакетов (документация 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.

Note

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

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

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

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

builder.Services.AddApiAuthorization();

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

Файл Imports

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

Пространство имен Майкрософт.AspNetCore.Components.Authorization доступно в приложении через файл _Imports.razor:

...
@using Microsoft.AspNetCore.Components.Authorization
...

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 расположение компонента изменилось в течение многих лет.

    Note

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

Компонент RedirectToLogin

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

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

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

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

Компонент 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.

    Note

    Ссылки на справочную документацию .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; }
}

Note

Nullable типы ссылок (NRTs) и статический анализ состояния 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).

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

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

  • 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 для .NET 5 или более ранних версий:

using System.Security.Claims;

...

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

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

Note

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

  • Пространство имен с областью действия файла (file-scoped) — это функция C# версии 10 или более поздней (.NET 6 или более поздней версии).
  • Конструктор primary — это компонент C# 12 или более поздней версии (.NET 8 или более поздней версии).
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 предоставляет сертификат приложению службы приложений для привязки пользовательского домена, сервер не может получить тот же сертификат из 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 откройте Configuration и добавьте новый параметр приложения, указывающий отпечатки сертификата, записанные ранее. Ключ параметра приложения — WEBSITE_LOAD_CERTIFICATES. ** Отпечатки сертификата в значении параметра приложения разделяйте запятыми, например:

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

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

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

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

    • Запись A Record с Host@ и значением IP-адреса на портале Azure.
    • Запись TXT Record с Hostasuid и значением идентификатора проверки, созданного Azure и предоставленным порталом Azure.

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

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

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

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

  12. В Visual Studio откройте файл параметров приложения проекта Server проекта (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 publish для проекта Server. В строке меню выберите: Build>Publish>New>Azure>Служба приложений Azure (Windows или Linux). При подключении Visual Studio к подписке Azure можно задать View ресурсов 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 для открытия нового закрытого окна браузера для каждого тестового запуска см. в разделе Cookies и данных сайта.

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

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

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

Troubleshoot

Logging

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

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

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

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

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

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

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

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

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

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

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

    Предыдущие форумы не принадлежат или управляются Майкрософт.

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

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

    сведения: Майкрософт.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 диалоговое окно Browse With с кнопки Run.
    • Нажмите кнопку Добавить.
    • Укажите путь к браузеру в поле Программа. Следующие пути исполняемого файла являются типичными расположениями установки для Windows 10. Если браузер установлен в другом расположении или вы не используете Windows 10, укажите путь к исполняемому файлу браузера.
      • Microsoft Edge: C:\Program Files (x86)\Майкрософт\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.
    • Выберите кнопку ОК.
    • Чтобы не выбирать профиль браузера для каждой операции тестирования с помощью приложения, задайте профиль по умолчанию с помощью кнопки По умолчанию.
    • Убедитесь, что при любых изменениях в приложении, в данных тестового пользователя или в конфигурации поставщика закрытие браузера выполняется интегрированной средой разработки.

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

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

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

Note

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

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 (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]

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