Partilhar via


Proteja um aplicativo ASP.NET Core Blazor WebAssembly hospedado com o Identity Server

Important

O modelo de projeto Hosted Blazor WebAssembly foi removido da estrutura com o lançamento do .NET 8 (novembro de 2023). As orientações neste artigo só são suportadas para o .NET 7 ou anterior. Os aplicativos de Blazor WebAssembly hospedados que são atualizados a cada versão continuam a receber suporte ao produto. Como alternativa, reorganize a aplicação numa aplicação autónoma Blazor WebAssembly ou num Blazor Web App.

Este artigo explica como criar uma solução hospedada que utiliza Duende Server para autenticar utilizadores e chamadas de API.

Important

Duende Software pode exigir que você pague uma taxa de licença para uso de produção do Duende Identity Server. Para obter mais informações, consulte Migrar do ASP.NET Core no .NET 5 para o .NET 6.

Note

Para configurar uma aplicação Blazor WebAssembly independente ou hospedada para utilizar uma instância externa existente do Identity Server, siga as orientações em como proteger uma aplicação independente do ASP.NET Core Blazor WebAssembly com a biblioteca de autenticação.

Para obter cobertura adicional de cenários de segurança depois de ler este artigo, consulte ASP.NET Core Blazor WebAssembly cenários de segurança adicionais.

Walkthrough

As subseções do passo a passo explicam como:

  • Criar a aplicação Blazor
  • Executar o aplicativo

Criar um aplicativo Blazor

Para criar um novo projeto Blazor WebAssembly com um mecanismo de autenticação:

  1. Crie um novo projeto.

  2. Escolha o modelo Blazor WebAssembly App. Selecione Avançar.

  3. Forneça um nome de projeto sem usar traços. Confirme se a Localização está correta. Selecione Avançar.

    Evite usar traços (-) no nome do projeto que interrompam a formação do identificador do aplicativo OIDC. A lógica no modelo de projeto Blazor WebAssembly utiliza o nome do projeto para um identificador de aplicativo OIDC na configuração da solução, e não se permitem traços em um identificador de aplicativo OIDC. PascalCase (BlazorSample) ou underscores (Blazor_Sample) são alternativas aceitáveis.

  4. Na caixa de diálogo Informações adicionais, selecione Contas Individuais como o tipo de Autenticação para armazenar os usuários no aplicativo usando o sistema do ASP.NET Core Identity.

  5. Marque a caixa de seleção ASP.NET Core Hosted.

  6. Selecione o botão Criar para criar o aplicativo.

Executar o aplicativo

Execute o aplicativo a partir do projeto Server. Ao usar o Visual Studio, faça o seguinte:

  • Selecione a seta para baixo junto ao botão Executar. Abra Configurar Projetos de Inicialização na lista suspensa. Selecione a opção projeto de inicialização único. Confirme ou altere o projeto de arranque para o projeto Server.

  • Confirme se o projeto Server está realçado no Gerenciador de Soluções antes de iniciar o aplicativo com qualquer uma das seguintes abordagens:

    • Selecione o botão Executar.
    • Use Depurar>Iniciar a Depuração no menu.
    • Pressione F5.
  • Em um shell de comando, navegue até a pasta de projeto Server da solução. Execute o comando dotnet watch (ou dotnet run).

Partes da solução

Esta seção descreve as partes de uma solução geradas a partir do modelo de projeto Blazor WebAssembly e descreve como os projetos Client e Server da solução são configurados para referência. Não há orientações específicas a seguir nesta secção para um aplicativo funcional básico se criou a aplicação usando o guia passo a passo na secção de demonstração . As orientações nesta seção são úteis para atualizar um aplicativo para autenticar e autorizar usuários. No entanto, uma abordagem alternativa para atualizar uma aplicação é criar uma nova aplicação a partir do guia na seção Walkthrough e mover os componentes, classes e recursos da aplicação para a nova aplicação.

Server serviços de aplicativos

Esta seção diz respeito ao aplicativo Server da solução.

Registam-se os seguintes serviços:

  • No ficheiro Program:

    • Entity Framework Core e 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 com um método auxiliar adicional de AddApiAuthorization que configura convenções padrão do ASP.NET Core em cima de Identity Server:

      builder.Services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Autenticação com um método auxiliar de AddIdentityServerJwt adicional que configura o aplicativo para validar tokens JWT produzidos pelo Identity Server:

      builder.Services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • Em Startup.ConfigureServices de Startup.cs:

    • Entity Framework Core e ASP.NET Core Identity:

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

      Warning

      Não armazene segredos de aplicativos, cadeias de conexão, credenciais, senhas, números de identificação pessoal (PINs), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente, que é sempre inseguro. Em ambientes de teste/preparação e produção, o código Blazor do lado do servidor e as APIs da Web devem usar fluxos de autenticação seguros que evitem a manutenção de credenciais no código do projeto ou nos arquivos de configuração. Fora dos testes de desenvolvimento local, recomendamos evitar o uso de variáveis de ambiente para armazenar dados confidenciais, pois as variáveis de ambiente não são a abordagem mais segura. Para testes de desenvolvimento local, a ferramenta Secret Manager é recomendada para proteger dados confidenciais. Para obter mais informações, consulte Manter com segurança dados confidenciais e credenciais.

    • Identity Server com um método auxiliar adicional de AddApiAuthorization que configura convenções padrão do ASP.NET Core em cima de Identity Server:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Autenticação com um método auxiliar de AddIdentityServerJwt adicional que configura o aplicativo para validar tokens JWT produzidos pelo Identity Server:

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Note

Quando um único esquema de autenticação é registrado, o esquema de autenticação é usado automaticamente como o esquema padrão do aplicativo, e não é necessário declarar o esquema para AddAuthentication ou via AuthenticationOptions. Para obter mais informações, consulte Visão geral da autenticação do ASP.NET Core e o anúncio do ASP.NET Core (aspnet/Announcements #490).

  • No ficheiro Program:
  • Em Startup.Configure de Startup.cs:
  • O middleware do servidor Identity expõe os pontos de extremidade do OpenID Connect (OIDC):

    app.UseIdentityServer();
    
  • O Middleware de Autenticação é responsável por validar as credenciais da solicitação e definir o usuário no contexto da solicitação:

    app.UseAuthentication();
    
  • O middleware de autorização permite capacidades de autorização.

    app.UseAuthorization();
    

Autorização API

Esta seção diz respeito ao aplicativo Server da solução.

O método auxiliar AddApiAuthorization configura Identity Server para cenários ASP.NET Core. O Identity Server é uma estrutura poderosa e extensível para lidar com questões de segurança de aplicativos. Identity Server expõe complexidade desnecessária para os cenários mais comuns. Consequentemente, é fornecido um conjunto de convenções e opções de configuração que consideramos um bom ponto de partida. Quando suas necessidades de autenticação mudam, todo o poder do Identity Server está disponível para personalizar a autenticação de acordo com os requisitos de um aplicativo.

Adicionar um manipulador de autenticação para uma API que coexiste com o Identity Server

Esta seção diz respeito ao aplicativo Server da solução.

O método auxiliar AddIdentityServerJwt configura um esquema de política para o aplicativo como o manipulador de autenticação padrão. A política está configurada para permitir que Identity manipule todas as solicitações encaminhadas para qualquer subcaminho no espaço de URL Identity sob /Identity. O JwtBearerHandler lida com todas as outras solicitações. Além disso, este método:

  • Registra um recurso de API no Identity Server com um escopo padrão de {PROJECT NAME}API, onde o espaço reservado {PROJECT NAME} é o nome do projeto na criação do aplicativo.
  • Configura o middleware de token portador JWT para validar tokens emitidos pelo servidor Identity para a aplicação.

Controlador de previsão meteorológica

Esta seção diz respeito ao aplicativo Server da solução.

No WeatherForecastController (Controllers/WeatherForecastController.cs), o atributo [Authorize] é aplicado à classe. O atributo indica que o usuário deve ser autorizado com base na política padrão para acessar o recurso. A política de autorização padrão é configurada para usar o esquema de autenticação padrão, que é configurado pelo AddIdentityServerJwt. O método auxiliar configura JwtBearerHandler como o manipulador padrão para solicitações ao aplicativo.

Contexto do banco de dados do aplicativo

Esta seção diz respeito ao aplicativo Server da solução.

No ApplicationDbContext (Data/ApplicationDbContext.cs), DbContext estende ApiAuthorizationDbContext<TUser> para incluir o esquema para o Servidor Identity. ApiAuthorizationDbContext<TUser> deriva de IdentityDbContext.

Para obter controle total do esquema de banco de dados, herde de uma das classes de IdentityDbContext disponíveis e configure o contexto para incluir o esquema Identity chamando builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) no método OnModelCreating.

Controlador de configuração OIDC

Esta seção diz respeito ao aplicativo Server da solução.

No OidcConfigurationController (Controllers/OidcConfigurationController.cs), o endpoint do cliente é configurado para fornecer parâmetros OIDC.

Configurações do aplicativo

Esta seção diz respeito ao aplicativo Server da solução.

No arquivo de configurações do aplicativo (appsettings.json) na raiz do projeto, a seção IdentityServer descreve a lista de clientes configurados. No exemplo a seguir, há um único cliente. O nome do cliente corresponde ao nome do assembly do aplicativo Client e é mapeado por convenção para o parâmetro OAuth ClientId. O perfil indica o tipo de aplicativo que está sendo configurado. O perfil é usado internamente para conduzir convenções que simplificam o processo de configuração para o servidor.

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

O marcador de posição {ASSEMBLY NAME} é o nome do assembly do aplicativo Client (por exemplo, BlazorSample.Client).

Pacote de autenticação

Esta seção diz respeito ao aplicativo Client da solução.

Quando um aplicativo é criado para usar Contas Individuais (Individual), o aplicativo recebe automaticamente uma referência de pacote para o Microsoft.AspNetCore.Components.WebAssembly.Authentication pacote. O pacote fornece um conjunto de primitivos que ajudam o aplicativo a autenticar usuários e obter tokens para chamar APIs protegidas.

Se adicionar autenticação a um aplicativo, adicione manualmente o pacote Microsoft.AspNetCore.Components.WebAssembly.Authentication ao aplicativo.

Note

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes em Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

HttpClient configuração

Esta seção diz respeito ao aplicativo Client da solução.

No arquivo Program, um HttpClient nomeado é configurado para fornecer instâncias de HttpClient que incluem tokens de acesso ao fazer solicitações à API do servidor. Na criação da solução, o HttpClient é nomeado como {PROJECT NAME}.ServerAPI, onde o espaço reservado {PROJECT NAME} é o nome do projeto.

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

O placeholder {PROJECT NAME} é o nome do projeto na criação da solução. Por exemplo, fornecer um nome de projeto como BlazorSample gera um HttpClient denominado BlazorSample.ServerAPI.

Note

Se estiver a configurar uma aplicação Blazor WebAssembly para usar uma instância existente do Identity Server que não faça parte de uma solução Blazor alojada, altere o registo do endereço de base HttpClient de IWebAssemblyHostEnvironment.BaseAddress (builder.HostEnvironment.BaseAddress) para a URL de autorização da API da aplicação do servidor.

Suporte de autorização de API

Esta seção diz respeito ao aplicativo Client da solução.

O suporte para autenticação de usuários é conectado ao contêiner de serviço pelo método de extensão fornecido dentro do pacote Microsoft.AspNetCore.Components.WebAssembly.Authentication. Esse método configura os serviços exigidos pelo aplicativo para interagir com o sistema de autorização existente.

builder.Services.AddApiAuthorization();

A configuração da aplicação é carregada convencionalmente a partir de _configuration/{client-id}. Por convenção, a ID do cliente é definida como o nome do assembly do aplicativo. Esse URL pode ser alterado para apontar para um ponto de extremidade separado chamando a sobrecarga com opções.

Imports arquivo

Esta seção diz respeito ao aplicativo Client da solução.

O namespace Microsoft.AspNetCore.Components.Authorization é disponibilizado em todo o aplicativo por meio do arquivo _Imports.razor:

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

Index página

Esta seção diz respeito ao aplicativo Client da solução.

A página Índice (wwwroot/index.html) inclui um script que define o AuthenticationService em JavaScript. AuthenticationService lida com os detalhes de baixo nível do protocolo OIDC. O aplicativo chama internamente métodos definidos no script para executar as operações de autenticação.

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

Componente App

Esta seção diz respeito ao aplicativo Client da solução.

O componente App (App.razor) é semelhante ao componente App encontrado em Blazor Server aplicativos:

  • O componente CascadingAuthenticationState gerencia a exposição do AuthenticationState ao resto do aplicativo.
  • O componente AuthorizeRouteView garante que o usuário atual esteja autorizado a acessar uma determinada página ou renderize o componente RedirectToLogin.
  • O componente RedirectToLogin gerencia o redirecionamento de usuários não autorizados para a página de login.

Devido a alterações na estrutura entre as versões do ASP.NET Core, a marcação Razor para o componente App (App.razor) não é mostrada nesta secção. Para inspecionar a marcação do componente para uma determinada versão, use uma ou outra das seguintes abordagens:

  • Crie um aplicativo provisionado para autenticação a partir do modelo de projeto Blazor WebAssembly padrão para a versão do ASP.NET Core que você pretende usar. Inspecione o componente App (App.razor) no aplicativo gerado.

  • Inspecione o componente App (App.razor) na fonte de referência . Selecione a versão no seletor de ramificação e procure o componente na pasta ProjectTemplates do repositório porque a localização do componente App mudou ao longo dos anos.

    Note

    Os links de documentação para a fonte de referência do .NET geralmente carregam a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma versão específica, use a lista suspensa para ramificações ou alternar para tags. Para obter mais informações, consulte Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Componente RedirectToLogin

Esta seção diz respeito ao aplicativo Client da solução.

O componente RedirectToLogin (RedirectToLogin.razor):

  • Gerencia o redirecionamento de usuários não autorizados para a página de login.
  • A URL atual que o utilizador está tentando acessar é mantida para que o utilizador possa voltar a essa página se a autenticação for bem-sucedida usando:

Inspecione o componente RedirectToLogin na fonte de referência . A localização do componente mudou ao longo do tempo, portanto, use as ferramentas de pesquisa do GitHub para localizar o componente.

O caminho de login pode ser personalizado pelo aplicativo (RemoteAuthenticationApplicationPathsOptions.LogInPath, padrões da estrutura (dotnet/aspnetcore fonte de referência)). O componente do RedirectToLogin modelo de projeto usa o caminho de login padrão do authentication/login.

Note

Os links de documentação para a fonte de referência do .NET geralmente carregam a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma versão específica, use a lista suspensa para ramificações ou alternar para tags. Para obter mais informações, consulte Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Se um aplicativo personalizar o caminho de login, siga uma das seguintes abordagens:

  • Corresponder o caminho na cadeia de caracteres codificada RedirectToLogin no componente.

  • Injete RemoteAuthenticationOptions para obter o valor configurado. Por exemplo, adote essa abordagem ao personalizar o caminho com AddApiAuthorizationo . Adicione as seguintes diretivas na parte superior do RedirectToLogin componente:

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

    Modifique o redirecionamento do componente no OnInitialized método:

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

    Note

    Se outros caminhos forem diferentes dos caminhos padrão do modelo de projeto ou da estrutura, eles deverão ser gerenciados da mesma maneira.

Componente LoginDisplay

Esta seção diz respeito ao aplicativo Client da solução.

O componente LoginDisplay (LoginDisplay.razor) é processado no componente MainLayout (MainLayout.razor) e gerencia os seguintes comportamentos:

  • Para utilizadores autenticados:
    • Exibe o nome de usuário atual.
    • Oferece um link para a página de perfil do usuário no ASP.NET Core Identity.
    • Oferece um botão para sair do aplicativo.
  • Para utilizadores anónimos:
    • Oferece a opção de registo.
    • Oferece a opção de iniciar sessão.

Devido a alterações na estrutura nas versões do ASP.NET Core, Razor a marcação do componente LoginDisplay não é apresentada nesta secção. Para inspecionar a marcação do componente para uma determinada versão, use uma ou outra das seguintes abordagens:

  • Crie um aplicativo provisionado para autenticação a partir do modelo de projeto Blazor WebAssembly padrão para a versão do ASP.NET Core que você pretende usar. Inspecione o componente LoginDisplay no aplicativo gerado.

  • Inspecione o componente LoginDisplay na fonte de referência . A localização do componente mudou ao longo do tempo, portanto, use as ferramentas de pesquisa do GitHub para localizar o componente. O conteúdo modelado para Hosted igual a true é utilizado.

    Note

    Os links de documentação para a fonte de referência do .NET geralmente carregam a ramificação padrão do repositório, que representa o desenvolvimento atual para a próxima versão do .NET. Para selecionar uma tag para uma versão específica, use a lista suspensa para ramificações ou alternar para tags. Para obter mais informações, consulte Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Componente Authentication

Esta seção diz respeito ao aplicativo Client da solução.

A página produzida pelo componente Authentication (Pages/Authentication.razor) define as rotas necessárias para lidar com diferentes estágios de autenticação.

O componente RemoteAuthenticatorView:

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

<RemoteAuthenticatorView Action="@Action" />

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

Note

Tipos de referência anuláveis (NRTs) e análise estática de estado nulo do compilador .NET são suportados no ASP.NET Core no .NET 6 ou posterior. Antes do lançamento do ASP.NET Core no .NET 6, o tipo string aparece sem a designação de tipo nulo (?).

Componente FetchData

Esta seção diz respeito ao aplicativo Client da solução.

O componente FetchData mostra como:

  • Provisionar um token de acesso.
  • Use o token de acesso para invocar uma API de recurso protegida na aplicação Server.

A diretiva @attribute [Authorize] indica ao sistema de autorização Blazor WebAssembly que o usuário deve ser autorizado para visitar este componente. A presença do atributo no aplicativo Client não impede que a API no servidor seja chamada sem as credenciais adequadas. A aplicação Server também deve utilizar [Authorize] nos respetivos pontos de extremidade para os proteger corretamente.

IAccessTokenProvider.RequestAccessToken se encarrega de solicitar um token de acesso que pode ser adicionado à solicitação para chamar a API. Se o token for armazenado em cache ou se o serviço for capaz de provisionar um novo token de acesso sem interação do usuário, a solicitação de token será bem-sucedida. Caso contrário, a solicitação de token falhará com um AccessTokenNotAvailableException, que é interceptado numa instrução try-catch.

Para obter o token real a ser incluído na solicitação, o aplicativo deve verificar se a solicitação foi bem-sucedida chamando tokenResult.TryGetToken(out var token).

Se a solicitação foi bem-sucedida, a variável de token é preenchida com o token de acesso. A propriedade AccessToken.Value do token expõe a cadeia de caracteres literal a ser incluída no cabeçalho da solicitação Authorization.

Se o token não puder ser provisionado sem a interação do usuário, resultando em uma solicitação com falha:

  • ASP.NET Core no .NET 7 ou posterior: o aplicativo navega até AccessTokenResult.InteractiveRequestUrl usando o AccessTokenResult.InteractionOptions fornecido para permitir a atualização do token de acesso.
  • ASP.NET Core no .NET 6 ou anterior: o resultado do token contém uma URL de redirecionamento. Navegar para este URL leva o usuário para a página de login e de volta para a página atual após uma autenticação bem-sucedida.
@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();
        }
    }
}

Serviço de Aplicações do Azure no Linux

Especifique o emissor explicitamente ao implantar no Azure App Service no Linux. Para mais informações, veja Use Identity para proteger um back-end de API da Web para SPAs.

Reivindicação de nome e atribuição de função com autorização de API

Fábrica personalizada de utilizadores

No aplicativo Client, crie uma fábrica de usuário personalizada. Identity Server envia várias funções como uma matriz JSON em uma única declaração role. Uma única função é enviada como um valor de cadeia de caracteres na declaração. A fábrica cria uma declaração de role individual para cada uma das funções do usuário.

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

No aplicativo Client, registre a fábrica no arquivo Program:

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

No aplicativo Server, chame AddRoles no construtor de Identity, que adiciona serviços relacionados à função.

No ficheiro Program:

using Microsoft.AspNetCore.Identity;

...

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

Em Startup.cs:

using Microsoft.AspNetCore.Identity;

...

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

Configurar o Identity Servidor

Utilize uma das seguintes abordagens:

Opções de autorização da API

No aplicativo Server:

  • Configure o servidor Identity para colocar as declarações name e role no token de ID e no token de acesso.
  • Impeça o mapeamento padrão para funções no manipulador de token JWT.

No ficheiro 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");

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

Serviço de Perfil

No aplicativo Server, crie uma implementação 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;
    }
}

No aplicativo Server, registre o Serviço de Perfil no arquivo Program:

using Duende.IdentityServer.Services;

...

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

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Na aplicação Server, registe o Serviço de Perfil em Startup.ConfigureServices de Startup.cs:

using IdentityServer4.Services;

...

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Mecanismos de autorização de utilização

No aplicativo Client, as abordagens de autorização de componentes são funcionais neste momento. Qualquer um dos mecanismos de autorização em componentes pode usar uma função para autorizar o usuário:

User.Identity.Name é preenchido no aplicativo Client com o nome de usuário do usuário, que geralmente é seu endereço de e-mail de login.

UserManager e SignInManager

Defina o tipo de declaração do identificador de usuário quando for necessário para uma aplicação de Servidor.

No Program.cs para ASP.NET Core em .NET 6 ou posterior:

using System.Security.Claims;

...

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

No Startup.ConfigureServices .NET 5 ou anterior:

using System.Security.Claims;

...

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

O WeatherForecastController a seguir registra o UserName quando o método Get é chamado.

Note

O exemplo a seguir usa:

  • Um namespace definido por um ficheiro, que é uma funcionalidade do C# 10 ou posterior (.NET 6 ou posterior).
  • Um construtor primário , que é um recurso C# 12 ou posterior (.NET 8 ou posterior).
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();
    }
}

No exemplo anterior:

  • O namespace do projeto Server é BlazorSample.Server.
  • O namespace do projeto Shared é BlazorSample.Shared.

Hospedar no Serviço de Aplicativo do Azure com um domínio e certificado personalizados

As orientações a seguir explicam:

  • Como implantar uma aplicação Blazor WebAssembly alojada com o servidor Identity no Serviço de Aplicações do Azure com um domínio personalizado.
  • Como criar e usar um certificado TLS para comunicação do protocolo HTTPS com navegadores. Embora as diretrizes se concentrem no uso do certificado com um domínio personalizado, elas são igualmente aplicáveis ao uso de um domínio padrão do Azure Apps, por exemplo, contoso.azurewebsites.net.

Neste cenário de hospedagem, não use o mesmo certificado para a chave de assinatura de token do Duende Server e a comunicação segura HTTPS do site com os navegadores:

  • Usar certificados diferentes para esses dois requisitos é uma boa prática de segurança porque isola chaves privadas para cada finalidade.
  • Os certificados TLS para comunicação com navegadores são gerenciados de forma independente, sem afetar a assinatura de token do Identity Server.
  • Quando o Azure Key Vault fornece um certificado a uma app do App Service para ligação de domínio personalizado, o Identity Server não pode obter o mesmo certificado do Azure Key Vault para a assinatura de token. Embora seja possível configurar o Identity Server para usar o mesmo certificado TLS de um caminho físico, colocar certificados de segurança no controle do código-fonte é uma prática ruim e deve ser evitada na maioria dos cenários.

Nas diretrizes a seguir, um certificado autoassinado é criado no Cofre da Chave do Azure somente para assinatura de token do Identity Server. A configuração do Identity Server usa o certificado do cofre de chaves por meio do armazenamento de certificados CurrentUser>My do aplicativo. Outros certificados usados para tráfego HTTPS com domínios personalizados são criados e configurados separadamente do certificado de assinatura do Identity Server.

Para configurar um aplicativo, o Serviço de Aplicativo do Azure e o Cofre da Chave do Azure para hospedar com um domínio personalizado e HTTPS:

  1. Crie um Plano de Serviço de Aplicações com um nível de plano de Basic B1 ou superior. O Serviço de Aplicativo requer uma camada de serviço Basic B1 ou superior para usar domínios personalizados.

  2. Crie um certificado PFX para a comunicação segura do navegador do site (protocolo HTTPS) com um nome comum do nome de domínio totalmente qualificado (FQDN) do site que sua organização controla (por exemplo, www.contoso.com). Crie o certificado com:

    • Principais utilizações
      • Validação de assinatura digital (digitalSignature)
      • Codificação de chaves (keyEncipherment)
    • Utilizações de chaves melhoradas/alargadas
      • Autenticação de cliente (1.3.6.1.5.5.7.3.2)
      • Autenticação do servidor (1.3.6.1.5.5.7.3.1)

    Para criar o certificado, use uma das seguintes abordagens ou qualquer outra ferramenta ou serviço online adequado:

    Anote a senha, que é usada posteriormente para importar o certificado para o Cofre de Chaves do Azure.

    Para obter mais informações sobre certificados do Azure Key Vault, consulte Azure Key Vault: Certificates.

  3. Crie um novo Cofre de Chaves do Azure ou use um cofre de chaves existente em sua assinatura do Azure.

  4. Na área Certificados do cofre de chaves, importe o certificado do site PFX. Registre a impressão digital do certificado, que é usada na configuração do aplicativo posteriormente.

  5. No Cofre da Chave do Azure, gere um novo certificado autoassinado para assinatura de token do Identity Server. Dê ao certificado um Nome do Certificado e um Assunto do Certificado. O Assunto é especificado como CN={COMMON NAME}, onde o marcador de posição {COMMON NAME} é o nome comum do certificado. O nome comum pode ser qualquer cadeia alfanumérica. Por exemplo, CN=IdentityServerSigning é um certificado válido Assunto. Em Política de Emissão>Configuração Avançada de Política, use as configurações padrão. Registre a impressão digital do certificado, que é usada na configuração do aplicativo posteriormente.

  6. Navegue até o Serviço de Aplicativo do Azure no portal do Azure e crie um novo Serviço de Aplicativo com a seguinte configuração:

    • Publicar definido como Code.
    • stack de runtime definido para o runtime da aplicação.
    • Para Sku e tamanho, confirme se a camada do Serviço de Aplicativo é Basic B1 ou superior. O Serviço de Aplicativo requer uma camada de serviço Basic B1 ou superior para usar domínios personalizados.
  7. Depois que o Azure criar o Serviço de Aplicativo, abra a Configuração do aplicativo e adicione uma nova configuração de aplicativo especificando as impressões digitais do certificado registadas anteriormente. A chave de configuração do aplicativo é WEBSITE_LOAD_CERTIFICATES. Separe as impressões digitais do certificado no valor da configuração do aplicativo com uma vírgula, como mostra o exemplo a seguir:

    • Chave: WEBSITE_LOAD_CERTIFICATES
    • Valor: 57443A552A46DB...D55E28D412B943565,29F43A772CB6AF...1D04F0C67F85FB0B1

    No portal do Azure, salvar as configurações do aplicativo é um processo de duas etapas: salve a configuração chave-valor do WEBSITE_LOAD_CERTIFICATES e selecione o botão Salvar na parte superior da folha.

  8. Selecione as configurações TLS/SSL do aplicativo. Selecione Certificados de Chave Privada (.pfx). Use o processo Importar Certificado de Key Vault.

  9. Navegue até o painel Domínios personalizados. No site do fornecedor de domínios, use o endereço IP e o ID de Verificação de Domínio Personalizado para configurar o domínio. Uma configuração de domínio típica inclui:

    • Um Registo A com um Host de @ e um valor do endereço IP do portal do Azure.
    • Um registo TXT com um host de asuid e o valor da ID de verificação gerada pelo Azure e fornecida pelo portal do Azure.

    Certifique-se de salvar as alterações no site do registrador de domínios corretamente. Alguns sites de registro exigem um processo de duas etapas para salvar registros de domínio: um ou mais registros são salvos individualmente, seguidos da atualização do registro do domínio com um botão separado.

  10. Retorne à área Domínios personalizados no portal do Azure. Selecione Adicionar domínio personalizado. Selecione a opção Registo A. Forneça o domínio e selecione Validar. Se os registos de domínio estiverem corretos e propagados pela Internet, o portal permite-lhe selecionar o botão Adicionar domínio personalizado.

    Pode levar alguns dias para que as alterações de registro de domínio se propaguem pelos servidores de nomes de domínio (DNS) da Internet depois de serem processadas pelo registrador de domínios. Se os registros de domínio não forem atualizados dentro de três dias úteis, confirme se os registros estão corretamente definidos com o registrador de domínios e entre em contato com o suporte ao cliente.

  11. No painel Domínios personalizados, o ESTADO SSL para o domínio é marcado Not Secure. Selecione a ligação Adicionar vinculação. Selecione o certificado HTTPS do site no cofre de chaves para a associação de domínio personalizada.

  12. No Visual Studio, abra o arquivo de configurações do aplicativo do Server do projeto (appsettings.json ou appsettings.Production.json). Na configuração do Identity Server, adicione a seguinte seção Key. Especifique o certificado autoassinado Assunto para a chave Name. No exemplo a seguir, o nome comum do certificado atribuído no cofre de chaves é IdentityServerSigning, o que resulta num sujeito de :

    "IdentityServer": {
    
      ...
    
      "Key": {
        "Type": "Store",
        "StoreName": "My",
        "StoreLocation": "CurrentUser",
        "Name": "CN=IdentityServerSigning"
      }
    },
    
  13. No Visual Studio, crie um perfil de publicação do Serviço de Aplicativo do Azure para o projeto do Server. Na barra de menus, selecione: Criar>Publicar>Novo>Azure>Serviço de Aplicativo do Azure (Windows ou Linux). Quando o Visual Studio está conectado a uma subscrição do Azure, pode-se definir a Vista dos recursos do Azure por tipo de recurso. Navegue dentro da lista Web App para encontrar o App Service do aplicativo e selecione-o. Selecione Concluir.

  14. Quando o Visual Studio retorna à janela Publicar, o cofre de chaves e as dependências do serviço de base de dados do SQL Server são detetadas automaticamente.

    Nenhuma alteração de configuração nas configurações padrão é necessária para o serviço de cofre de chaves.

    Para fins de teste, o banco de dados SQLite local de um aplicativo, que é configurado pelo modelo Blazor, pode ser implantado com o aplicativo sem configuração adicional. Configurar um banco de dados diferente para Identity Server em produção está além do escopo deste artigo. Para obter mais informações, consulte os recursos de banco de dados nos seguintes conjuntos de documentação:

  15. Selecione o link Editar sob o nome do perfil de implantação na parte superior da janela. Altere o URL de destino para o URL de domínio personalizado do site (por exemplo, https://www.contoso.com). Salve as configurações.

  16. Publique o aplicativo. O Visual Studio abre uma janela do navegador e solicita o site em seu domínio personalizado.

A documentação do Azure contém detalhes adicionais sobre como usar os serviços do Azure e domínios personalizados com associação TLS no Serviço de Aplicativo, incluindo informações sobre como usar registros CNAME em vez de registros A. Para obter mais informações, consulte os seguintes recursos:

Recomendamos o uso de uma nova janela do navegador no modo privado (por exemplo, o modo InPrivate do Microsoft Edge ou o modo de navegação anônima do Google Chrome) para cada teste de aplicativo executado após uma alteração no aplicativo, na configuração do aplicativo ou nos serviços do Azure no portal do Azure. Cookies persistentes de uma execução de teste anterior podem resultar em falha de autenticação ou autorização ao testar o site, mesmo quando a configuração do site está correta. Para obter mais informações sobre como configurar o Visual Studio para abrir uma nova janela privada do navegador para cada execução de teste, consulte a seção Cookies e de dados do site.

Quando a configuração do Serviço de Aplicativo é alterada no portal do Azure, as atualizações geralmente entram em vigor rapidamente, mas não são instantâneas. Às vezes, você deve aguardar um curto período para que um Serviço de Aplicativo seja reiniciado para que uma alteração de configuração entre em vigor.

Se estiver a solucionar um problema de carregamento de certificado de assinatura de chave do Identity Server, execute o seguinte comando em um portal do Azure PowerShell do Kudu. O comando fornece uma lista de certificados que a aplicação pode acessar a partir da loja de certificados CurrentUser>My. A saída inclui assuntos de certificado e impressões digitais úteis ao depurar um aplicativo:

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

Troubleshoot

Logging

Para habilitar o log de depuração ou rastreamento para Blazor WebAssembly autenticação, consulte a seção Log de autenticação do lado do cliente do log do ASP.NET Core Blazor com o seletor de versão do artigo definido como ASP.NET Core no .NET 7 ou posterior.

Erros comuns

  • Configuração incorreta do aplicativo ou do provedor de Identity (IP)

    Os erros mais comuns são causados por configuração incorreta. Seguem-se alguns exemplos:

    • Dependendo dos requisitos do cenário, uma autoridade, instância, ID do locatário, domínio do locatário, ID do cliente ou URI de redirecionamento ausente ou incorreto impede que um aplicativo autentique clientes.
    • Escopos de solicitação incorretos impedem que os clientes acessem os endpoints da API web do servidor.
    • Permissões incorretas ou ausentes da API do servidor impedem que os clientes possam aceder aos pontos de extremidade da API web do servidor.
    • Executar a aplicação numa porta diferente da configurada no URI de redirecionamento no registo da aplicação do IP. Observe que uma porta não é necessária para o Microsoft Entra ID e um aplicativo em execução em um endereço de teste de desenvolvimento localhost, mas a configuração de porta do aplicativo e a porta onde o aplicativo está sendo executado devem corresponder para endereços nãolocalhost.

    As seções de configuração das diretrizes deste artigo mostram exemplos da configuração correta. Verifique cuidadosamente cada seção do artigo procurando por configuração incorreta de aplicativos e IP.

    Se a configuração parecer correta:

    • Analise os logs do aplicativo.

    • Examine o tráfego de rede entre o aplicativo cliente e o aplicativo IP ou servidor com as ferramentas de desenvolvedor do navegador. Muitas vezes, uma mensagem de erro exata ou uma mensagem com uma pista do que está causando o problema é retornada ao cliente pelo IP ou aplicativo de servidor depois de fazer uma solicitação. A orientação das ferramentas de desenvolvedor pode ser encontrada nos seguintes artigos:

    • Para versões de Blazor em que um JSON Web Token (JWT) é usado, decodifice o conteúdo do token usado para autenticar um cliente ou acessar uma API da Web do servidor, dependendo de onde o problema está ocorrendo. Para obter mais informações, consulte Inspecionar o conteúdo de um token da Web JSON (JWT).

    A equipa de documentação responde a feedback sobre documentos e a bugs em artigos (abra um problema na seção de feedback Esta página), mas não consegue fornecer suporte ao produto. Vários fóruns públicos de suporte estão disponíveis para ajudar na solução de problemas de um aplicativo. Recomendamos o seguinte:

    Os fóruns anteriores não pertencem nem são controlados pela Microsoft.

    Para relatórios de bugs de estrutura reproduzíveis não relacionados à segurança, não sensíveis e não confidenciais, abrir um ticket de problema com a unidade de produto ASP.NET Core. Não abra um problema com a unidade de produto até que você tenha investigado completamente a causa de um problema e não possa resolvê-lo sozinho e com a ajuda da comunidade em um fórum de suporte público. A unidade de produto não é capaz de solucionar problemas de aplicativos individuais que estão quebrados devido a simples erros de configuração ou casos de uso envolvendo serviços de terceiros. Se um relatório for de natureza sensível ou confidencial ou descrever uma possível falha de segurança no produto que os ciberatacantes podem explorar, consulte Relatando problemas de segurança e bugs (dotnet/aspnetcore repositório GitHub).

  • Cliente não autorizado para ME-ID

    info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Falha na autorização. Estes requisitos não foram atendidos: DenyAnonymousAuthorizationRequirement: Requer um usuário autenticado.

    Erro de retorno de chamada ao fazer login com o ME-ID:

    • Erro: unauthorized_client
    • Descrição: AADB2C90058: The provided application is not configured to allow public clients.

    Para resolver o erro:

    1. No portal do Azure, aceda ao manifesto da app .
    2. Defina o atributo allowPublicClient como null ou true.

Cookies e dados do site

Os cookies e os dados do site podem persistir nas atualizações do aplicativo e interferir nos testes e na solução de problemas. Desmarque o seguinte ao fazer alterações no código do aplicativo, alterações na conta do usuário com o provedor ou alterações na configuração do aplicativo do provedor:

  • Cookies de sessão do utilizador
  • Cookies de aplicações
  • Dados do site em cache e armazenados

Uma abordagem para evitar que cookies persistentes e dados do site interfiram com testes e solução de problemas é:

  • Configurar um navegador
    • Utilize um navegador para testar, que possa ser configurado para eliminar todos os cookie e dados do site sempre que o navegador for fechado.
    • Verifique se o navegador está fechado manualmente ou pelo IDE para qualquer alteração na configuração do aplicativo, do usuário de teste ou do provedor.
  • Use um comando personalizado para abrir um navegador no modo InPrivate ou de navegação anônima no Visual Studio:
    • Abra caixa de diálogo Procurar com no botão Executar do Visual Studio.
    • Selecione o botão Adicionar.
    • Forneça o caminho para o seu navegador no campo Programa. Os seguintes caminhos executáveis são locais de instalação típicos para o Windows 10. Se o seu navegador estiver instalado em um local diferente ou você não estiver usando o Windows 10, forneça o caminho para o executável do navegador.
      • 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
    • No campo Argumentos, forneça a opção de linha de comando que o navegador usa para abrir no modo InPrivate ou Incógnito. Alguns navegadores exigem o URL do aplicativo.
      • Microsoft Edge: Utilize -inprivate.
      • Google Chrome: Use o --incognito --new-window {URL}, onde o marcador de posição {URL} é o URL a ser aberto (por exemplo, https://localhost:5001).
      • Mozilla Firefox: Use -private -url {URL}, onde o marcador de posição {URL} é o URL que deve ser aberto (por exemplo, https://localhost:5001).
    • Forneça um nome no campo Nome amigável. Por exemplo, Firefox Auth Testing.
    • Selecione o botão OK.
    • Para evitar ter de selecionar o perfil do navegador para cada iteração de teste com uma aplicação, defina o perfil como padrão com o botão Definir padrão como.
    • Certifique-se de que o navegador está fechado pelo IDE para qualquer alteração na configuração do aplicativo, usuário de teste ou provedor.

Atualizações de aplicativos

Um aplicativo em funcionamento pode falhar imediatamente após atualizar o SDK do .NET na máquina de desenvolvimento ou alterar as versões do pacote dentro do aplicativo. Em alguns casos, pacotes incoerentes podem quebrar um aplicativo ao executar grandes atualizações. A maioria desses problemas pode ser corrigida seguindo estas instruções:

  1. Limpe os caches de pacotes NuGet do sistema local executando dotnet nuget locals all --clear a partir de um shell de comando.
  2. Exclua as pastas bin e obj do projeto.
  3. Restaure e reconstrua o projeto.
  4. Exclua todos os arquivos na pasta de implantação no servidor antes de reimplantar o aplicativo.

Note

Não há suporte para o uso de versões de pacote incompatíveis com a estrutura de destino do aplicativo. Para obter informações sobre um pacote, use o Galeria NuGet.

Executar o aplicativo Server

Ao testar e resolver problemas de uma solução hospedada Blazor WebAssembly, certifique-se de estar a executar a aplicação a partir do projeto Server.

Inspecionar o usuário

O componente User a seguir pode ser usado diretamente em aplicativos ou servir como base para personalização adicional.

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

Inspecionar o conteúdo de um JSON Web Token (JWT)

Para decodificar um JSON Web Token (JWT), utilize a ferramenta jwt.ms disponibilizada pela Microsoft. Os valores na interface do usuário nunca saem do navegador.

Exemplo de JWT codificado (abreviado para exibição):

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1j ... bQdHBHGcQQRbW7Wmo6SWYG4V_bU55Ug_PW4pLPr20tTS8Ct7_uwy9DWrzCMzpD-EiwT5IjXwlGX3IXVjHIlX50IVIydBoPQtadvT7saKo1G5Jmutgq41o-dmz6-yBMKV2_nXA25Q

Exemplo de JWT decodificado pela ferramenta para um aplicativo que se autentica no 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]

Recursos adicionais