Blazor WebAssemblyProteger um aplicativo ASP.NET Core hospedado com o Servidor Identity

Este artigo explica como criar uma solução hospedada Blazor WebAssembly que usa o Servidor Duende Identity para autenticar usuários e chamadas à API.

Importante

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

Observação

A fim de configurar um aplicativo autônomo ou hospedado Blazor WebAssembly para usar uma instância existente e externa do Servidor Identity, siga as diretrizes em Proteger um aplicativo autônomo ASP.NET Core Blazor WebAssembly com a biblioteca de Autenticação.

Para obter cobertura adicional do cenário de segurança depois de ler este artigo, confira Cenários de segurança adicional do Blazor WebAssembly ASP.NET Core.

Passo a passo

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

  • Criar o aplicativo 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 doaplicativoBlazor WebAssembly. Selecione Avançar.

  3. Forneça um Nome de projeto sem usar traços. Confirme se o Local está correto. Selecione Avançar.

    Evite usar traços (-) no nome do projeto que interrompem a formação do identificador de aplicativo OIDC. A lógica no modelo de projeto Blazor WebAssembly usa o nome do projeto para um identificador de aplicativo OIDC na configuração da solução, e traços não são permitidos em um identificador de aplicativo OIDC. Caso Pascal (BlazorSample) ou sublinhados (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 usuários no aplicativo usando o sistema do Identity ASP.NET Core.

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

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

Executar o aplicativo

Execute o aplicativo no projeto Server. Ao usar também o Visual Studio:

  • Selecione a seta suspensa ao lado do botão Executar . Abra Configurar Projetos de Inicialização na lista suspensa. Selecione a opção Projeto de inicialização única. Confirme ou altere o projeto do projeto de inicialização para o projeto Server.

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

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

Partes da solução

Esta seção descreve as partes de uma solução gerada 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á diretrizes específicas a serem seguidas nesta seção para um aplicativo de trabalho básico se você criou o aplicativo usando as diretrizes na seção Passo a passo. As diretrizes nesta seção são úteis para atualizar um aplicativo para autenticar e autorizar usuários. No entanto, uma abordagem alternativa para atualizar um aplicativo é criar um novo aplicativo a partir das diretrizes na seção Passo a passo e mover os componentes, classes e recursos do aplicativo para o novo aplicativo.

ServerServiços de aplicativos

Esta seção refere-se ao aplicativo da Server solução.

Os serviços a seguir são registrados.

  • No arquivo 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 Servidor com um método auxiliar adicional AddApiAuthorization que configura as convenções padrão do ASP.NET Core na parte superior do Servidor Identity:

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

      builder.Services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • No Startup.ConfigureServices do 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>();
      
    • Identity Servidor com um método auxiliar adicional AddApiAuthorization que configura as convenções padrão do ASP.NET Core na parte superior do Servidor Identity:

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

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Observação

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 por meio de AuthenticationOptions. Para obter mais informações, consulte Visão geral da Autenticação ASP.NET Core e o comunicado de ASP.NET Core (aspnet/Announcements #490).

  • No arquivo Program:
  • No Startup.Configure do 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 de solicitação e definir o usuário no contexto da solicitação:

    app.UseAuthentication();
    
  • O Middleware de Autorização permite recursos de autorização:

    app.UseAuthorization();
    

Autorização de API

Esta seção pertence ao aplicativo Server da solução.

O método auxiliar AddApiAuthorization configura o Identity Servidor para cenários do ASP.NET Core. O Servidor Identity é uma estrutura poderosa e extensível para lidar com questões de segurança do aplicativo. O Servidor Identity expõe a complexidade desnecessária para os cenários mais comuns. Consequentemente, um conjunto de convenções e opções de configuração é fornecido e consideramos um bom ponto de partida. Depois que a autenticação precisar ser alterada, o poder total do Servidor Identityestará disponível para personalizar a autenticação para atender aos requisitos de um aplicativo.

Adicionar um manipulador de autenticação para uma API que coexista com o Servidor Identity

Esta seção pertence ao aplicativo Server da solução.

O método auxiliar AddIdentityServerJwt configura uma política para o aplicativo como o manipulador de autenticação padrão. A política é configurada para permitir a manipulação por parte do Identity de todas as solicitações roteadas para qualquer subcaminho no espaço URL do Identity segundo o /Identity. Ele JwtBearerHandler manipula todas as outras solicitações. Além disso, este método:

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

Controlador de previsão do tempo

Esta seção pertence ao aplicativo Server da solução.

No WeatherForecastController (Controllers/WeatherForecastController.cs), o [Authorize]atributo é 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 por 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 pertence ao aplicativo Server da solução.

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

Para obter controle total do esquema de banco de dados, herde de uma das classes disponíveis IdentityDbContext 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 pertence ao aplicativo Server da solução.

No OidcConfigurationController (Controllers/OidcConfigurationController.cs), o ponto de extremidade do cliente é provisionado para atender aos parâmetros OIDC.

Configurações de aplicativo

Esta seção pertence ao aplicativo Server da solução.

No arquivo de configurações do aplicativo (appsettings.json) na raiz do projeto, a IdentityServer seção 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 espaço reservado {ASSEMBLY NAME} é o Client nome do assembly do aplicativo (por exemplo, BlazorSample.Client).

Pacote de autenticação

Esta seção pertence ao aplicativo Client da solução.

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

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

Observação

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

Configuração de HttpClient

Esta seção pertence ao aplicativo Client da solução.

No arquivo Program, um serviço denominado HttpClient é configurado para fornecer instâncias HttpClient que incluem tokens de acesso ao fazer solicitações à API do servidor. Por padrão, na criação da solução, o HttpClient nomeado é {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 espaço reservado {PROJECT NAME} é o nome do projeto na criação da solução. Fornecer um nome de projeto BlazorSample, por exemplo, produz um HttpClient nomeado de BlazorSample.ServerAPI.

Observação

Se você estiver configurando um aplicativo Blazor WebAssembly para usar uma instância do Servidor Identity existente que não faz parte de uma solução Blazor hospedada, altere o registro HttpClient de endereço base a partir de IWebAssemblyHostEnvironment.BaseAddress (builder.HostEnvironment.BaseAddress) para a URL do ponto de extremidade de autorização de API do aplicativo de servidor.

Suporte à autorização de API

Esta seção pertence 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();

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

Arquivo Imports

Esta seção pertence ao aplicativo Client da solução.

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

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

Index página

Esta seção pertence ao aplicativo Client da solução.

A página Índice (wwwroot/index.html) inclui um script que define o AuthenticationService em JavaScript. O AuthenticationService manipula 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 pertence ao aplicativo Client da solução.

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

  • O componente CascadingAuthenticationState gerencia a exposição do AuthenticationState no restante 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 logon.

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

  • Crie um aplicativo provisionado para autenticação do modelo de projeto padrão do Blazor WebAssembly 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 do seletor de branch e pesquise o componente na pasta ProjectTemplates do repositório porque ele se moveu ao longo dos anos.

    Observação

    Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

componente RedirectToLogin

Esta seção pertence 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 logon.
  • A URL atual que o usuário está tentando acessar é mantida para que ele possa ser retornado a essa página se a autenticação for bem-sucedida usando:

Inspecione o componente RedirectToLogin na fonte de referência. O local do componente mudou com o tempo, portanto, portanto, use as ferramentas de pesquisa do GitHub para localizar o componente.

Observação

Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

componente LoginDisplay

Esta seção pertence ao aplicativo Client da solução.

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

  • Para usuários autenticados:
    • Exibe o nome de usuário atual.
    • Oferece um link para a página de perfil do usuário na Identity do ASP.NET Core.
    • Oferece um botão para sair do aplicativo.
  • Para usuários anônimos:
    • Oferece a opção de se registrar.
    • Oferece a opção de fazer logon.

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

  • Crie um aplicativo provisionado para autenticação do modelo de projeto padrão do Blazor WebAssembly 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. O local do componente mudou com o tempo, portanto, portanto, use as ferramentas de pesquisa do GitHub para localizar o componente. O conteúdo com modelo para Hosted igual a true é usado.

    Observação

    Os links de documentação para a fonte de referência do .NET geralmente carregam o branch padrão do repositório, que representa o desenvolvimento atual da próxima versão do .NET. Para selecionar uma marca para uma versão específica, use a lista suspensa para Alternar branches ou marcas. Para saber mais, confira Como selecionar uma marca de versão do código-fonte do ASP.NET Core (dotnet/AspNetCore.Docs #26205).

componente Authentication

Esta seção pertence 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; }
}

Observação

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

componente FetchData

Esta seção pertence ao aplicativo Client da solução.

O componente FetchData mostra como:

  • Provisão de um token de acesso.
  • Use o token de acesso para chamar uma API de recurso protegido no aplicativo Servidor .

A diretiva @attribute [Authorize] indica ao sistema de autorização Blazor WebAssembly que o usuário deve ser autorizado para visitar esse componente. A presença do atributo no aplicativo Client não impede que a API no servidor seja chamada sem as credenciais adequadas. O aplicativo Server também deve usar [Authorize] nos pontos de extremidade apropriados para protegê-los corretamente.

IAccessTokenProvider.RequestAccessToken cuida da solicitação de 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 puder 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 é capturado em uma 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 tiver sido bem-sucedida, a variável de token será 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 a solicitação falhou porque o token não pôde ser provisionado sem interação do usuário:

  • ASP.NET Core em .NET 7 ou posterior: O aplicativo navega para 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 até essa URL leva o usuário para a página de logon e 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 Aplicativo do Azure no Linux

Especifique o emissor explicitamente ao implantar no Serviço de Aplicativo do Azure no Linux. Para obter mais informações, confira Usar o Identity para proteger um back-end da API Web para SPAs.

Declaração de nome e função com autorização de API

Fábrica de usuários personalizada

No aplicativo Client, crie uma fábrica de usuários personalizada. O Servidor Identity envia várias funções como uma matriz ON JS 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 individual role 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
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(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;
    }
}
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomUserFactory
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (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 (rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
                        }
                    }
                    else
                    {
                        identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
                    }
                }
            }
        }

        return user;
    }
}

No aplicativo Client, registre o alocador no arquivo Program:

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

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

No arquivo Program:

using Microsoft.AspNetCore.Identity;

...

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

No Startup.cs:

using Microsoft.AspNetCore.Identity;

...

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

Configurar Servidor Identity

Use uma das abordagens a seguir:

Opções de autorização de 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 arquivo 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");

No 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;
    }
}
using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using System.Threading.Tasks;

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

No aplicativo Server, registre o Serviço de Perfil em Startup.ConfigureServices de Startup.cs:

using IdentityServer4.Services;

...

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Usar mecanismos de autorização

No aplicativo Client, as abordagens de autorização de componentes estã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 entrada.

UserManager e SignInManager

Defina o tipo de declaração do identificador de usuário quando um aplicativo de servidor exigir:

Em 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 para versões do ASP.NET Core anteriores à 6.0:

using System.Security.Claims;

...

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

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

Observação

O exemplo a seguir usa um namespace com escopo de arquivo, que é um recurso C# 10 ou posterior (.NET 6 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 : ControllerBase
{
    private readonly UserManager<ApplicationUser> userManager;

    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", 
        "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger, 
        UserManager<ApplicationUser> userManager)
    {
        this.logger = logger;
        this.userManager = userManager;
    }

    [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 Server namespace do projeto é BlazorSample.Server.
  • O Shared namespace do projeto é BlazorSample.Shared.

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

As diretrizes a seguir explicam:

  • Como implantar um aplicativo hospedado Blazor WebAssembly com o Servidor Identity no Serviço de Aplicativo do Azure com um domínio personalizado.
  • Como criar e usar um certificado TLS para comunicação de protocolo HTTPS com navegadores. Embora as diretrizes se concentrem em usar o certificado com um domínio personalizado, as diretrizes são igualmente aplicáveis ao uso de um domínio padrão dos Aplicativos do Azure como, por exemplo, contoso.azurewebsites.net.

Para esse cenário de hospedagem, não use o mesmo certificado para a chave de assinatura do Servidor Identity e para a comunicação segura HTTPS do site com navegadores:

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

Nas diretrizes a seguir, um certificado autoassinado é criado no Azure Key Vault apenas para assinatura de token do Servidor Identity. A configuração do Servidor Identity usa o certificado do cofre de chaves por meio do repositório de certificados CurrentUser>My do aplicativo. Outros certificados usados para o tráfego HTTPS com domínios personalizados são criados e configurados separadamente do certificado de assinatura do Servidor Identity.

Realize o seguinte para configurar um aplicativo, Serviço de Aplicativo do Azure e um Azure Key Vault a fim de hospedar com um domínio personalizado e HTTPS:

  1. Crie um plano do Serviço de Aplicativo com um nível de plano igual Basic B1 ou superior. Serviço de Aplicativo requer uma Basic B1 ou camada de serviço 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 FQDN (nome de domínio totalmente qualificado) do site que sua organização controla (por exemplo, www.contoso.com). Crie o certificado com:

    • Usos de chave
      • Validação de assinatura digital (digitalSignature)
      • Codificação de chave (keyEncipherment)
    • Usos avançados/estendidos de chave
      • Autenticação de cliente (1.3.6.1.5.5.7.3.2)
      • Autenticação de 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 Azure Key Vault.

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

  3. Crie um novo Azure Key Vault 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 mais tarde.

  5. No Azure Key Vault, gere um novo certificado autoassinado para a assinatura de token do Servidor Identity. Dê ao certificado um Nome de Certificado e umaEntidade. A Entidade é especificada como CN={COMMON NAME}, em que o espaço reservado{COMMON NAME} é o nome comum do certificado. O nome comum pode ser qualquer cadeia de caracteres alfanumérica. Por exemplo, CN=IdentityServerSigning é uma Entidade de certificado válida. Em Política de Emissão>Configuração de Política Avançada, use as configurações padrão. Registre a impressão digital do certificado, que é usada na configuração do aplicativo mais tarde.

  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.
    • Pilha de runtime definida como o runtime do aplicativo.
    • Para Sku e tamanho, confirme se a camada do Serviço de Aplicativo é Basic B1 ou superior. Serviço de Aplicativo requer uma Basic B1 ou camada de serviço 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 registradas anteriormente. A chave de configuração do aplicativo é WEBSITE_LOAD_CERTIFICATES. Separe as impressões digitais do certificado no valor de 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 WEBSITE_LOAD_CERTIFICATES e, em seguida, selecione o botão Salvar na parte superior da folha.

  8. Selecione as Configurações de TLS/SSL do aplicativo. Selecione osCertificados de Chave Privada (.pfx). Use o processo de Importar Certificado do Key Vault. Use o processo duas vezes para importar o certificado do site para comunicação HTTPS e o certificado de assinatura de token do Servidor Identity autoassinado do site.

  9. Navegue até a folha Domínios personalizados. No site do registrador de domínios, use o endereço IP e oID de verificação do domínio personalizado para configurar o domínio. Uma configuração de domínio típica inclui o seguinte:

    • Um Registro A com um Host de @ e um valor do endereço IP do portal do Azure.
    • Um Registro TXT com um Host de asuid e o valor da ID de verificação gerado pelo Azure e fornecido pelo portal do Azure.

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

  10. Retorne à janela Domínios personalizados no portal do Azure. Selecione Adicionar domínio personalizado. Selecione a opção Registro A. Forneça o domínio e selecione Validar. Se os registros de domínio estiverem corretos e propagados pela Internet, o portal permitirá que você selecione o botão Adicionar domínio personalizado.

    Pode levar alguns dias para que as alterações de registro de domínio se propaguem entre os DNS (servidores de nomes de domínio) 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 definidos corretamente com o registrador de domínios e entre em contato com o atendimento ao cliente.

  11. Na folha Domínios personalizados , o SSL STATE para o domínio está marcado como Not Secure. Selecione o link Adicionar associaçã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 de aplicativo do projeto do Servidor (appsettings.json ou appsettings.Production.json). Na configuração do Servidor Identity, adicione a seção Key a seguir. Especifique a Entidade de certificado autoassinada para a chave Name. No exemplo a seguir, o nome comum do certificado atribuído no cofre de chaves é IdentityServerSigning, o que gera uma Entidade de CN=IdentityServerSigning:

    "IdentityServer": {
    
      ...
    
      "Key": {
        "Type": "Store",
        "StoreName": "My",
        "StoreLocation": "CurrentUser",
        "Name": "CN=IdentityServerSigning"
      }
    },
    
  13. No Visual Studio, crie um perfil de publicação Serviço de Aplicativo do Azure para o projeto do Servidor. Na barra de menus, selecione: Compilar>Publicar>Novo>Azure>Serviço de Aplicativo do Azure (Windows ou Linux). Quando o Visual Studio está conectado a uma assinatura do Azure, você pode definir a Exibição de recursos do Azure por Tipo de recurso. Navegue no Aplicativo Web para localizar o Serviço de Aplicativo para o aplicativo em questão 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 banco de dados do SQL Server são detectados automaticamente.

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

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

  15. Selecione o link Editar no nome do perfil de implantação na parte superior da janela. Altere a URL de destino para a URL de domínio personalizada 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 serviços do Azure e domínios personalizados com associação TLS em Serviço de Aplicativo, incluindo informações sobre como usar registros CNAME em vez de registros A. Para saber mais, consulte os recursos a seguir:

É recomendável usar uma nova janela do navegador de modo privado (por exemplo, modo InPrivate do Microsoft Edge ou modo Anônimo do Google Chrome) para cada execução de teste de aplicativo após uma alteração no aplicativo, na configuração do aplicativo ou nos serviços do Azure no portal do Azure. cookieOs persistentes de uma execução de teste anterior podem resultar em falha na 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 do navegador privado em cada execução de teste, consulte a seção de dados do site e de Cookies.

Quando a configuração do Serviço de Aplicativo é alterada no portal do Azure, as atualizações geralmente têm efeito 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 solucionando um problema de carregamento de certificado de assinatura de chave do Servidor Identity, execute o comando a seguir em um shell de comando do PowerShell do portal do Azure Kudu. O comando fornece uma lista de certificados que o aplicativo pode acessar no repositório de certificados CurrentUser>My. A saída inclui entidades de certificado e impressões digitais úteis ao depurar um aplicativo:

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

Solucionar problemas

Logging

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

Erros comuns

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

    Os erros mais comuns são causados pela configuração incorreta. A seguir, estão 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 um aplicativo autenticar clientes.
    • Escopos de solicitação incorretos impedem que os clientes acessem pontos de extremidade da API Web do servidor.
    • Permissões incorretas ou ausentes da API do servidor impedem os clientes de acessar pontos de extremidade da API Web do servidor.
    • Executar o aplicativo em uma porta diferente da configurada no URI de Redirecionamento do registro de aplicativo 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 da porta do aplicativo e a porta em que o aplicativo está sendo executado devem corresponder a endereços que não sejam localhost.

    As seções de configuração das diretrizes deste artigo mostram exemplos da configuração correta. Verifique cuidadosamente cada seção do artigo em busca de configurações incorretas de aplicativo e IP.

    Se a configuração aparecer correta:

    • Analisar logs de aplicativos.

    • 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 aplicativo IP ou servidor depois de fazer uma solicitação. As diretrizes das ferramentas de desenvolvedor são encontradas nos seguintes artigos:

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

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

    Os fóruns anteriores não são de propriedade ou controlados pela Microsoft.

    Para relatórios de bugs de estrutura reproduzível não confidenciais e não confidenciais, abra um problema com a unidade do produto do ASP.NET Core. Não abra um problema com a unidade do produto até que você investigue completamente a causa de um problema e não possa resolvê-lo por conta própria e com a ajuda da comunidade em um fórum de suporte público. A unidade do produto não é capaz de solucionar problemas de aplicativos individuais que estão não estão funcionando devido a uma simples configuração incorreta ou casos de uso envolvendo serviços de terceiros. Se um relatório for confidencial ou de natureza confidencial ou descrever uma potencial falha de segurança no produto que possa ser explorada por invasores, confira Relatar problemas e bugs de segurança (dotnet/aspnetcore repositório GitHub).

  • Cliente não autorizado para o ME-ID

    informação: falha na autorização do Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]. Esses requisitos não foram atendidos: DenyAnonymousAuthorizationRequirement: requer um usuário autenticado.

    Erro de retorno de chamada de logon do 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, acesse o manifesto do aplicativo.
    2. Defina o atributo allowPublicClient como null ou true.

Cookies e dados do site

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

  • cookies de entrada do usuário
  • cookies de aplicativo
  • Dados do site armazenados e em cache

Uma abordagem para impedir que os dados persistentes de cookies e de sites interfiram no teste e na solução de problemas é:

  • Configurar um navegador
    • Use um navegador para testar se você consegue configurar a exclusão de todos os dados de cookies e sites sempre que o navegador é fechado.
    • Verifique se o navegador está fechado manualmente ou pelo IDE para qualquer alteração no aplicativo, usuário de teste ou configuração do provedor.
  • Use um comando personalizado para abrir um navegador no modo InPrivate ou Incógnito no Visual Studio:
    • Abra a caixa de diálogo Procurar com no botão Executar do Visual Studio.
    • Selecione o botão Adicionar.
    • Forneça o caminho para o navegador no campo Programa. Os seguintes caminhos executáveis são locais de instalação típicos para Windows 10. Se o navegador estiver instalado em um local diferente ou você não estiver usando 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 a URL do aplicativo.
      • Microsoft Edge: use -inprivate.
      • Google Chrome: use --incognito --new-window {URL}, em que o espaço reservado {URL} é a URL a ser aberta (por exemplo, https://localhost:5001).
      • Mozilla Firefox: use -private -url {URL}, em que o espaço reservado {URL} é a URL a ser aberta (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 a necessidade de selecionar o perfil do navegador para cada iteração de teste com um aplicativo, defina o perfil como o padrão com o botão Definir como Padrão.
    • Verifique se o navegador está fechado pelo IDE para qualquer alteração no aplicativo, usuário de teste ou configuração do provedor.

Atualizações de aplicativos

Um aplicativo em funcionamento pode falhar imediatamente depois de atualizar o SDK do .NET Core no computador de desenvolvimento ou alterar as versões do pacote dentro do aplicativo. Em alguns casos, pacotes incoerentes podem interromper um aplicativo ao executar atualizações principais. A maioria desses problemas pode ser corrigida seguindo estas instruções:

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

Observação

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 a Galeria do NuGet ou a Gerenciador de Pacotes FuGet.

Execute o aplicativo Server

Ao testar e solucionar problemas de uma solução Blazor WebAssemblyhospedada, verifique se você está executando o aplicativo no 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 Token Web JSON (JWT)

Para decodificar um Token Web JSON (JWT), use a ferramenta jwt.ms da 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/5cc15ea8-a296-4aa3-97e4-226dcc9ad298/v2.0/",
  "sub": "5ee963fb-24d6-4d72-a1b6-889c6e2c7438",
  "aud": "70bde375-fce3-4b82-984a-b247d823a03f",
  "nonce": "b2641f54-8dc4-42ca-97ea-7f12ff4af871",
  "iat": 1610055829,
  "auth_time": 1610055822,
  "idp": "idp.com",
  "tfp": "B2C_1_signupsignin"
}.[Signature]

Recursos adicionais