Compartilhar via


Proteger um Blazor Web App ASP.NET Core com o OIDC (OpenID Connect)

Este artigo descreve como proteger um Blazor Web App com o OIDC (OpenID Connect) usando um aplicativo de exemplo no repositório GitHub do dotnet/blazor-samples (.NET 8 ou posterior) (como baixar).

Esta versão do artigo aborda a implementação do OIDC sem a adoção do padrão Back-end for Front-end (BFF). O padrão BFF é útil para fazer solicitações autenticadas a serviços externos. Altere o seletor de versão do artigo para OIDC com o padrão BFF se a especificação do aplicativo exigir a adoção do padrão BFF.

A seguinte especificação é abordada:

  • O Blazor Web App usa o modo de renderização automática com interatividade global.
  • Os serviços personalizados de provedor de estado de autenticação são usados pelo servidor e pelos aplicativos cliente para capturar o estado de autenticação do usuário e transferi-lo entre o servidor e o cliente.
  • Este aplicativo é um ponto de partida para qualquer fluxo de autenticação OIDC. O OIDC é configurado manualmente no aplicativo e não depende do Microsoft Entra ID ou dos pacotes do Microsoft Identity Web, nem o aplicativo de exemplo requer a hospedagem do Microsoft Azure. No entanto, o aplicativo de exemplo pode ser usado com o Entra, Microsoft Identity Web e hospedado no Azure.
  • Atualização automática de token não interativo.
  • Chama com segurança uma API (Web) no projeto do servidor para obter dados.

Aplicativo de exemplo

Este aplicativo de exemplo é composto por dois projetos:

  • BlazorWebAppOidc: projeto do lado do servidor do Blazor Web App, que contém um exemplo de ponto de extremidade de API mínima de dados meteorológicos.
  • BlazorWebAppOidc.Client: projeto do lado do cliente do Blazor Web App.

Acesse os aplicativos de exemplo por meio da pasta da versão mais recente na raiz do repositório com o link a seguir. Os projetos estão na pasta BlazorWebAppOidc no .NET 8 ou posterior.

Exibir ou baixar código de exemplo (como baixar)

Projeto Blazor Web App do lado do servidor (BlazorWebAppOidc)

O projeto BlazorWebAppOidc é o projeto do lado do servidor do Blazor Web App.

O arquivo BlazorWebAppOidc.http pode ser usado para testar a solicitação de dados meteorológicos. Observe que o projeto BlazorWebAppOidc deve estar em execução para testar o ponto de extremidade e o ponto de extremidade está codificado no arquivo. Para obter mais informações, consulte Usar arquivos .http no Visual Studio 2022.

Observação

O projeto do servidor usa IHttpContextAccessor/HttpContext, mas nunca para componentes renderizados interativamente. Para obter mais informações, confira Diretrizes de mitigação de ameaças para renderização interativa do lado do servidor do ASP.NET Blazor.

Configuração

Esta seção explica como configurar o aplicativo de exemplo.

Observação

Para a ID do Microsoft Entra ou o Azure AD B2C, você pode usar AddMicrosoftIdentityWebApp a partir da Web da Microsoft Identity (Microsoft.Identity.Webpacote NuGet, documentação da API), que adiciona o OIDC e Cookie os manipuladores de autenticação com os padrões apropriados. O aplicativo de exemplo e as diretrizes nesta seção não usam o Microsoft Identity Web. As diretrizes demonstram como configurar o manipulador do OIDC manualmente para qualquer provedor OIDC. Para obter mais informações sobre como implementar o Microsoft Identity Web, consulte os recursos vinculados.

Estabelecer o segredo do cliente

Aviso

Não armazene segredos de aplicativo, cadeias de conexão, credenciais, senhas, PINs (números de identificação pessoal), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente, que é sempre inseguro. Em ambientes de teste/preparo e produção, o código do lado do Blazor servidor e as APIs Web devem usar fluxos de autenticação seguros que evitam 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 Gerenciador de segredos é recomendada para proteger dados confidenciais. Para obter mais informações, consulte Manter dados e credenciais confidenciais com segurança.

Para testes de desenvolvimento local, use a ferramenta Gerenciador de segredos para armazenar o segredo do cliente do aplicativo de servidor na chave Authentication:Schemes:MicrosoftOidc:ClientSecretde configuração.

Observação

Se o aplicativo usar a ID do Microsoft Entra ou o Azure AD B2C, crie um segredo do cliente no registro do aplicativo no portal do Entra ou do Azure (Gerenciar>certificados e segredos>Novo segredo do cliente). Use o valor do novo segredo nas diretrizes a seguir.

O aplicativo de exemplo não foi inicializado para a ferramenta Gerenciador de Segredos. Use um shell de comando, como o shell de comando do PowerShell do Desenvolvedor no Visual Studio, para executar o comando a seguir. Antes de executar o comando, altere o diretório com o cd comando para o diretório do projeto do servidor. O comando estabelece um identificador de segredos do usuário (<UserSecretsId> no arquivo de projeto do aplicativo do servidor):

dotnet user-secrets init

Execute o comando a seguir para definir o segredo do cliente. O {SECRET} espaço reservado é o segredo do cliente obtido do registro do aplicativo:

dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"

Se estiver usando o Visual Studio, você poderá confirmar se o segredo está definido clicando com o botão direito do mouse no projeto do servidor no Gerenciador de Soluções e selecionando Gerenciar Segredos do Usuário.

Configurar o aplicativo

A configuração OpenIdConnectOptions a seguir é encontrada no arquivo Program do projeto na chamada para AddOpenIdConnect:

  • SignInScheme: define o esquema de autenticação correspondente ao middleware responsável por manter a identity do usuário após uma autenticação bem-sucedida. O manipulador do OIDC precisa usar um esquema de entrada capaz de persistir as credenciais do usuário entre solicitações. A linha a seguir está presente apenas para fins de demonstração. Se omitido, DefaultSignInScheme será usado como um valor de fallback.

    oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    
  • Escopos para openid e profile (Scope) (Opcional): os escopos profile e openid também são configurados por padrão porque são necessários para que o manipulador do OIDC funcione, mas eles podem precisar ser adicionados novamente se os escopos forem incluídos na configuração Authentication:Schemes:MicrosoftOidc:Scope. Para obter diretrizes gerais de configuração, consulte Configuração no ASP.NET Core e Configuração do no ASP.NET CoreBlazor.

    oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
    
  • SaveTokens: define se os tokens de acesso e atualização devem ser armazenados no AuthenticationProperties após uma autorização bem-sucedida. Essa propriedade é definida como false para reduzir o tamanho do cookie da autenticação final.

    oidcOptions.SaveTokens = false;
    
  • Escopo para acesso offline (Scope): o escopo offline_access é necessário para o token de atualização.

    oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
    
  • Authority e ClientId: define a autoridade e a ID do cliente para chamadas ao OIDC.

    oidcOptions.Authority = "{AUTHORITY}";
    oidcOptions.ClientId = "{CLIENT ID}";
    

    Exemplo:

    • Autoridade ({AUTHORITY}): https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (usa a ID do Locatário aaaabbbb-0000-cccc-1111-dddd2222eeee)
    • ID de Cliente ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
    

    Exemplo de autoridade "comum" do Microsoft Azure:

    A autoridade "comum" deve ser usada para aplicativos multilocatários. Você também pode usar a autoridade "comum" para aplicativos de locatário único, mas um IssuerValidator personalizado é necessário, conforme será mostrado posteriormente nesta seção.

    oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
    
  • ResponseType: configura o manipulador do OIDC para executar apenas o fluxo do código de autorização. Concessões implícitas e fluxos híbridos são desnecessários nesse modo.

    Na configuração de registro do aplicativo Concessão implícita e fluxos híbridos do portal do Entra ou do Azure, não marque nenhuma das caixas de seleção para que o ponto de extremidade de autorização retorne Tokens de acesso ou Tokens de ID. O manipulador do OIDC solicita automaticamente os tokens apropriados usando o código retornado do ponto de extremidade da autorização.

    oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
    
  • MapInboundClaims e a configuração de NameClaimType e RoleClaimType: muitos servidores OIDC usam " name" e "role" em vez dos padrões SOAP/WS-Fed em ClaimTypes. Quando MapInboundClaims está definido como false, o manipulador não executa mapeamentos de declarações e os nomes das declarações do JWT são usados diretamente pelo aplicativo. O exemplo a seguir define o tipo de declaração de função como "roles", que é apropriado para o Microsoft Entra ID (ME-ID). Consulte a documentação do provedor de identity para mais informações.

    Observação

    MapInboundClaims deve ser definido como false para a maioria dos provedores OIDC, o que impede a renomeação de declarações.

    oidcOptions.MapInboundClaims = false;
    oidcOptions.TokenValidationParameters.NameClaimType = "name";
    oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
    
  • Configuração do caminho: os caminhos devem corresponder ao URI de redirecionamento (caminho de retorno de chamada do logon) e aos caminhos de redirecionamento de logoff (caminho de retorno de chamada de saída) configurados ao registrar o aplicativo com o provedor OIDC. No portal do Azure, os caminhos são configurados na folha Autenticação do registro do aplicativo. Os caminhos de entrada e saída devem ser registrados como URIs de redirecionamento. Os valores padrão são /signin-oidc e /signout-callback-oidc.

    • CallbackPath: o caminho da solicitação dentro do caminho base do aplicativo no qual o agente do usuário é retornado.

      No portal do Azure ou no Entra, defina o caminho na configuração da plataforma Web em URI de Redirecionamento:

      https://localhost/signin-oidc

      Observação

      Uma porta não é necessária para endereços localhost ao usar o Microsoft Entra ID. A maioria dos outros provedores OIDC exige uma porta correta.

    • SignedOutCallbackPath: o caminho da solicitação dentro do caminho base do aplicativo no qual o agente do usuário é retornado após sair do provedor de identity.

      No portal do Azure ou no Entra, defina o caminho na configuração da plataforma Web em URI de Redirecionamento:

      https://localhost/signout-callback-oidc

      Observação

      Uma porta não é necessária para endereços localhost ao usar o Microsoft Entra ID. A maioria dos outros provedores OIDC exige uma porta correta.

      Observação

      Se estiver usando o Microsoft Identity Web, o provedor atualmente só redirecionará de volta para o SignedOutCallbackPath caso a Autoridade microsoftonline.com (https://login.microsoftonline.com/{TENANT ID}/v2.0/) seja usada. Essa limitação não existirá se você puder usar a Autoridade "comum" com o Microsoft Identity Web. Para obter mais informações, consulte O postLogoutRedirectUri não está funcionando quando a URL da autoridade contém uma ID de locatário (AzureAD/microsoft-authentication-library-for-js nº 5783).

    • RemoteSignOutPath: as solicitações recebidas nesse caminho fazem com que o manipulador invoque a saída usando o esquema de saída.

      No portal do Azure ou do Entra, defina a URL de logoff de front-channel:

      https://localhost/signout-oidc

      Observação

      Uma porta não é necessária para endereços localhost ao usar o Microsoft Entra ID. A maioria dos outros provedores OIDC exige uma porta correta.

      oidcOptions.CallbackPath = new PathString("{PATH}");
      oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
      oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
      

      Exemplos (valores padrão):

      oidcOptions.CallbackPath = new PathString("/signin-oidc");
      oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
      oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
      
  • (Microsoft Azure somente com o ponto de extremidade "comum") TokenValidationParameters.IssuerValidator: muitos provedores OIDC funcionam com o validador de emissor padrão, mas precisamos considerar o emissor parametrizado com a ID do Locatário ({TENANT ID}) retornada por https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Para obter mais informações, consulte SecurityTokenInvalidIssuerException com OpenID Connect e o ponto de extremidade "comum" do Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet nº 1731).

    Somente para aplicativos que usam o Microsoft Entra ID ou o Azure AD B2C com o ponto de extremidade "comum":

    var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
    oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
    

Código de aplicativo de exemplo

Inspecione o aplicativo de exemplo para obter os seguintes recursos:

  • Atualização automática de token não interativo com a ajuda de um atualizador cookie personalizado (CookieOidcRefresher.cs).
  • O projeto do servidor chama AddAuthenticationStateSerialization para adicionar um provedor de estado de autenticação do lado do servidor que usa PersistentComponentState para fluir o estado de autenticação para o cliente. O cliente chama AddAuthenticationStateDeserialization para desserializar e usar o estado de autenticação passado pelo servidor. O estado de autenticação é corrigido para o tempo de vida do aplicativo WebAssembly.
  • Um exemplo de solicitações para o Blazor Web App de dados meteorológicos é tratado por um ponto de extremidade de API mínima (/weather-forecast) no arquivo Program (Program.cs). O ponto de extremidade requer autorização chamando RequireAuthorization. Para todos os controladores que você adicionar ao projeto, adicione o atributo [Authorize] ao controlador ou à ação.
  • O aplicativo chama com segurança uma API (Web) no projeto do servidor para obter dados meteorológicos:
    • Ao renderizar o componente Weather no servidor, o componente usa o servidor ServerWeatherForecaster para obter dados meteorológicos diretamente (não por meio de uma chamada à API Web).
    • Quando o componente é renderizado no cliente, o componente usa a implementação do serviço ClientWeatherForecaster, que usa um HttpClient pré-configurado (no arquivo do projeto Program do cliente) para fazer uma chamada à API Web para o projeto do servidor. Um ponto de extremidade de API mínimo (/weather-forecast) definido no arquivo do projeto Program do servidor obtém os dados meteorológicos do ServerWeatherForecaster e retorna os dados para o cliente.
  • Atualização automática de token não interativo com a ajuda de um atualizador cookie personalizado (CookieOidcRefresher.cs).
  • A classe PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) é um AuthenticationStateProvider do lado do servidor que usa PersistentComponentState para transmitir o estado de autenticação para o cliente, que é então corrigido para o tempo de vida do aplicativo WebAssembly.
  • Um exemplo de solicitações para o Blazor Web App de dados meteorológicos é tratado por um ponto de extremidade de API mínima (/weather-forecast) no arquivo Program (Program.cs). O ponto de extremidade requer autorização chamando RequireAuthorization. Para todos os controladores que você adicionar ao projeto, adicione o atributo [Authorize] ao controlador ou à ação.
  • O aplicativo chama com segurança uma API (Web) no projeto do servidor para obter dados meteorológicos:
    • Ao renderizar o componente Weather no servidor, o componente usa o servidor ServerWeatherForecaster para obter dados meteorológicos diretamente (não por meio de uma chamada à API Web).
    • Quando o componente é renderizado no cliente, o componente usa a implementação do serviço ClientWeatherForecaster, que usa um HttpClient pré-configurado (no arquivo do projeto Program do cliente) para fazer uma chamada à API Web para o projeto do servidor. Um ponto de extremidade de API mínimo (/weather-forecast) definido no arquivo do projeto Program do servidor obtém os dados meteorológicos do ServerWeatherForecaster e retorna os dados para o cliente.

Para mais informações sobre chamadas à API (Web) usando abstrações de serviço em Blazor Web Apps, confira Chamar uma API Web de um Blazor do ASP.NET Core.

Projeto Blazor Web App do lado do cliente (BlazorWebAppOidc.Client)

O projeto BlazorWebAppOidc.Client é o projeto do lado do cliente do Blazor Web App.

O cliente chama AddAuthenticationStateDeserialization para desserializar e usar o estado de autenticação passado pelo servidor. O estado de autenticação é corrigido para o tempo de vida do aplicativo WebAssembly.

A classe PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) é uma AuthenticationStateProvider do lado do cliente que determina o estado de autenticação do usuário procurando dados persistidos na página quando ele foi renderizado no servidor. O estado de autenticação é corrigido para o tempo de vida do aplicativo WebAssembly.

Se o usuário precisar fazer logon ou logoff, será necessário recarregar a página inteira.

O aplicativo de exemplo fornece apenas um nome de usuário e email para fins de exibição. Ele não inclui tokens que se autenticam no servidor ao fazer solicitações subsequentes, que funciona separadamente usando um cookie que é incluído nas solicitações HttpClient ao servidor.

Esta versão do artigo aborda a implementação do OIDC com o padrão Back-end for Front-end (BFF). Altere o seletor de versão do artigo para OIDC sem o padrão BFF se a especificação do aplicativo não exigir a adoção do padrão BFF.

A seguinte especificação é abordada:

  • O Blazor Web App usa o modo de renderização automática com interatividade global.
  • Os serviços personalizados de provedor de estado de autenticação são usados pelo servidor e pelos aplicativos cliente para capturar o estado de autenticação do usuário e transferi-lo entre o servidor e o cliente.
  • Este aplicativo é um ponto de partida para qualquer fluxo de autenticação OIDC. O OIDC é configurado manualmente no aplicativo e não depende do Microsoft Entra ID ou dos pacotes do Microsoft Identity Web, nem o aplicativo de exemplo requer a hospedagem do Microsoft Azure. No entanto, o aplicativo de exemplo pode ser usado com o Entra, Microsoft Identity Web e hospedado no Azure.
  • Atualização automática de token não interativo.
  • O padrão BFF (Back-end for Front-end) é adotado usando o .NET Aspire para descoberta de serviço e o YARP para solicitações de proxy a um ponto de extremidade de previsão do tempo no aplicativo de back-end.
    • Uma API Web de back-end usa a autenticação de portador JWT para validar tokens JWT salvos pelo Blazor Web App no cookie de entrada.
    • O Aspire melhora a experiência de criação de aplicativos nativos de nuvem do .NET. Ele fornece um conjunto consistente e opinativo de ferramentas e padrões para criar e executar aplicativos distribuídos.
    • O YARP (Outro Proxy Reverso) é uma biblioteca usada para criar um servidor proxy reverso.

Para mais informações sobre o .NET Aspire, consulte Disponibilidade geral do .NET Aspire: Desenvolvimento Nativo de Nuvem do .NET simplificado (maio de 2024).

Pré-requisito

O .NET Aspire requer o Visual Studio versão 17.10 ou posterior.

Aplicativo de exemplo

O aplicativo de exemplo consiste em cinco projetos:

  • .NET Aspire:
    • Aspire.AppHost: usado para gerenciar as preocupações de orquestração de alto nível do aplicativo.
    • Aspire.ServiceDefaults: contém configurações padrão do aplicativo .NET Aspire que podem ser estendidas e personalizadas conforme necessário.
  • MinimalApiJwt: API Web de back-end, que contém um exemplo de ponto de extremidade de API mínima para dados meteorológicos.
  • BlazorWebAppOidc: projeto do lado do servidor do Blazor Web App.
  • BlazorWebAppOidc.Client: projeto do lado do cliente do Blazor Web App.

Acesse os aplicativos de exemplo por meio da pasta da versão mais recente na raiz do repositório com o link a seguir. Os projetos estão na pasta BlazorWebAppOidcBff no .NET 8 ou posterior.

Exibir ou baixar código de exemplo (como baixar)

.NET Aspire projetos

Para mais informações sobre como usar o ..NET Aspire e detalhes sobre os projetos .AppHost e .ServiceDefaults do aplicativo de exemplo, consulte a Documentação do .NET Aspire.

Confirme se você atendeu aos pré-requisitos do .NET Aspire. Para mais informações, consulte a seção Pré-requisitos do Guia de Início Rápido: Criar seu primeiro aplicativo .NET Aspire.

O aplicativo de amostra configura apenas um perfil de inicialização HTTP inseguro (http) para uso durante o teste de desenvolvimento. Para mais informações, incluindo um exemplo de perfis de configurações de inicialização inseguros e seguros, consulte Permitir o transporte não seguro no .NET Aspire (documentação do .NET Aspire).

Projeto Blazor Web App do lado do servidor (BlazorWebAppOidc)

O projeto BlazorWebAppOidc é o projeto do lado do servidor do Blazor Web App. O projeto usa o YARP para fazer proxy de solicitações para um ponto de extremidade de previsão do tempo no projeto da API Web de back-end (MinimalApiJwt) com o access_token armazenado na autenticação cookie.

O arquivo BlazorWebAppOidc.http pode ser usado para testar a solicitação de dados meteorológicos. Observe que o projeto BlazorWebAppOidc deve estar em execução para testar o ponto de extremidade e o ponto de extremidade está codificado no arquivo. Para obter mais informações, consulte Usar arquivos .http no Visual Studio 2022.

Observação

O projeto do servidor usa IHttpContextAccessor/HttpContext, mas nunca para componentes renderizados interativamente. Para obter mais informações, confira Diretrizes de mitigação de ameaças para renderização interativa do lado do servidor do ASP.NET Blazor.

Configuração

Esta seção explica como configurar o aplicativo de exemplo.

Observação

Para a ID do Microsoft Entra ou o Azure AD B2C, você pode usar AddMicrosoftIdentityWebApp a partir da Web da Microsoft Identity (Microsoft.Identity.Webpacote NuGet, documentação da API), que adiciona o OIDC e Cookie os manipuladores de autenticação com os padrões apropriados. O aplicativo de exemplo e as diretrizes nesta seção não usam o Microsoft Identity Web. As diretrizes demonstram como configurar o manipulador do OIDC manualmente para qualquer provedor OIDC. Para obter mais informações sobre como implementar o Microsoft Identity Web, consulte os recursos vinculados.

Estabelecer o segredo do cliente

Aviso

Não armazene segredos de aplicativo, cadeias de conexão, credenciais, senhas, PINs (números de identificação pessoal), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente, que é sempre inseguro. Em ambientes de teste/preparo e produção, o código do lado do Blazor servidor e as APIs Web devem usar fluxos de autenticação seguros que evitam 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 Gerenciador de segredos é recomendada para proteger dados confidenciais. Para obter mais informações, consulte Manter dados e credenciais confidenciais com segurança.

Para testes de desenvolvimento local, use a ferramenta Gerenciador de segredos para armazenar o segredo do cliente do aplicativo de servidor na chave Authentication:Schemes:MicrosoftOidc:ClientSecretde configuração.

Observação

Se o aplicativo usar a ID do Microsoft Entra ou o Azure AD B2C, crie um segredo do cliente no registro do aplicativo no portal do Entra ou do Azure (Gerenciar>certificados e segredos>Novo segredo do cliente). Use o valor do novo segredo nas diretrizes a seguir.

O aplicativo de exemplo não foi inicializado para a ferramenta Gerenciador de Segredos. Use um shell de comando, como o shell de comando do PowerShell do Desenvolvedor no Visual Studio, para executar o comando a seguir. Antes de executar o comando, altere o diretório com o cd comando para o diretório do projeto do servidor. O comando estabelece um identificador de segredos do usuário (<UserSecretsId> no arquivo de projeto do aplicativo do servidor):

dotnet user-secrets init

Execute o comando a seguir para definir o segredo do cliente. O {SECRET} espaço reservado é o segredo do cliente obtido do registro do aplicativo:

dotnet user-secrets set "Authentication:Schemes:MicrosoftOidc:ClientSecret" "{SECRET}"

Se estiver usando o Visual Studio, você poderá confirmar se o segredo está definido clicando com o botão direito do mouse no projeto do servidor no Gerenciador de Soluções e selecionando Gerenciar Segredos do Usuário.

Configurar o aplicativo

A configuração OpenIdConnectOptions a seguir é encontrada no arquivo Program do projeto na chamada para AddOpenIdConnect:

  • SignInScheme: define o esquema de autenticação correspondente ao middleware responsável por manter a identity do usuário após uma autenticação bem-sucedida. O manipulador do OIDC precisa usar um esquema de entrada capaz de persistir as credenciais do usuário entre solicitações. A linha a seguir está presente apenas para fins de demonstração. Se omitido, DefaultSignInScheme será usado como um valor de fallback.

    oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    
  • Escopos para openid e profile (Scope) (Opcional): os escopos profile e openid também são configurados por padrão porque são necessários para que o manipulador do OIDC funcione, mas eles podem precisar ser adicionados novamente se os escopos forem incluídos na configuração Authentication:Schemes:MicrosoftOidc:Scope. Para obter diretrizes gerais de configuração, consulte Configuração no ASP.NET Core e Configuração do no ASP.NET CoreBlazor.

    oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
    
  • SaveTokens: define se os tokens de acesso e atualização devem ser armazenados no AuthenticationProperties após uma autorização bem-sucedida. O valor é definido como true para autenticar solicitações de dados meteorológicos do projeto de API Web de back-end (MinimalApiJwt).

    oidcOptions.SaveTokens = true;
    
  • Escopo para acesso offline (Scope): o escopo offline_access é necessário para o token de atualização.

    oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);
    
  • Escopos para obter dados meteorológicos da API Web (Scope): o escopo Weather.Get está configurado no portal do Azure ou do Entra em Expor uma API. Isso é necessário para que o projeto de API Web de back-end (MinimalApiJwt) valide o token de acesso com o portador JWT.

    oidcOptions.Scope.Add("{APP ID URI}/{API NAME}");
    

    Exemplo:

    • URI da ID do aplicativo ({APP ID URI}): https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}
      • Nome do diretório ({DIRECTORY NAME}): contoso
      • ID do aplicativo (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    • Escopo configurado para dados meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
    

    O exemplo anterior pertence a um aplicativo registrado em um locatário com um tipo de locatário AAD B2C. Se o aplicativo estiver registrado em um locatário ME-ID, o URI da ID do aplicativo será diferente; portanto, o escopo será diferente.

    Exemplo:

    • URI da ID do aplicativo ({APP ID URI}): api://{CLIENT ID} com a ID do aplicativo (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    • Escopo configurado para dados meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("api://00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
    
  • Authority e ClientId: define a autoridade e a ID do cliente para chamadas ao OIDC.

    oidcOptions.Authority = "{AUTHORITY}";
    oidcOptions.ClientId = "{CLIENT ID}";
    

    Exemplo:

    • Autoridade ({AUTHORITY}): https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (usa a ID do Locatário aaaabbbb-0000-cccc-1111-dddd2222eeee)
    • ID de Cliente ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
    

    Exemplo de autoridade "comum" do Microsoft Azure:

    A autoridade "comum" deve ser usada para aplicativos multilocatários. Você também pode usar a autoridade "comum" para aplicativos de locatário único, mas um IssuerValidator personalizado é necessário, conforme será mostrado posteriormente nesta seção.

    oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";
    
  • ResponseType: configura o manipulador do OIDC para executar apenas o fluxo do código de autorização. Concessões implícitas e fluxos híbridos são desnecessários nesse modo.

    Na configuração de registro do aplicativo Concessão implícita e fluxos híbridos do portal do Entra ou do Azure, não marque nenhuma das caixas de seleção para que o ponto de extremidade de autorização retorne Tokens de acesso ou Tokens de ID. O manipulador do OIDC solicita automaticamente os tokens apropriados usando o código retornado do ponto de extremidade da autorização.

    oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
    
  • MapInboundClaims e a configuração de NameClaimType e RoleClaimType: muitos servidores OIDC usam " name" e "role" em vez dos padrões SOAP/WS-Fed em ClaimTypes. Quando MapInboundClaims está definido como false, o manipulador não executa mapeamentos de declarações e os nomes das declarações do JWT são usados diretamente pelo aplicativo. O exemplo a seguir define o tipo de declaração de função como "roles", que é apropriado para o Microsoft Entra ID (ME-ID). Consulte a documentação do provedor de identity para mais informações.

    Observação

    MapInboundClaims deve ser definido como false para a maioria dos provedores OIDC, o que impede a renomeação de declarações.

    oidcOptions.MapInboundClaims = false;
    oidcOptions.TokenValidationParameters.NameClaimType = "name";
    oidcOptions.TokenValidationParameters.RoleClaimType = "roles";
    
  • Configuração do caminho: os caminhos devem corresponder ao URI de redirecionamento (caminho de retorno de chamada do logon) e aos caminhos de redirecionamento de logoff (caminho de retorno de chamada de saída) configurados ao registrar o aplicativo com o provedor OIDC. No portal do Azure, os caminhos são configurados na folha Autenticação do registro do aplicativo. Os caminhos de entrada e saída devem ser registrados como URIs de redirecionamento. Os valores padrão são /signin-oidc e /signout-callback-oidc.

    • CallbackPath: o caminho da solicitação dentro do caminho base do aplicativo no qual o agente do usuário é retornado.

      No portal do Azure ou no Entra, defina o caminho na configuração da plataforma Web em URI de Redirecionamento:

      https://localhost/signin-oidc

      Observação

      Uma porta não é necessária para endereços localhost.

    • SignedOutCallbackPath: o caminho da solicitação dentro do caminho base do aplicativo no qual o agente do usuário é retornado após sair do provedor de identity.

      No portal do Azure ou no Entra, defina o caminho na configuração da plataforma Web em URI de Redirecionamento:

      https://localhost/signout-callback-oidc

      Observação

      Uma porta não é necessária para endereços localhost.

      Observação

      Se estiver usando o Microsoft Identity Web, o provedor atualmente só redirecionará de volta para o SignedOutCallbackPath caso a Autoridade microsoftonline.com (https://login.microsoftonline.com/{TENANT ID}/v2.0/) seja usada. Essa limitação não existirá se você puder usar a Autoridade "comum" com o Microsoft Identity Web. Para obter mais informações, consulte O postLogoutRedirectUri não está funcionando quando a URL da autoridade contém uma ID de locatário (AzureAD/microsoft-authentication-library-for-js nº 5783).

    • RemoteSignOutPath: as solicitações recebidas nesse caminho fazem com que o manipulador invoque a saída usando o esquema de saída.

      No portal do Azure ou do Entra, defina a URL de logoff de front-channel:

      https://localhost/signout-oidc

      Observação

      Uma porta não é necessária para endereços localhost.

      oidcOptions.CallbackPath = new PathString("{PATH}");
      oidcOptions.SignedOutCallbackPath = new PathString("{PATH}");
      oidcOptions.RemoteSignOutPath = new PathString("{PATH}");
      

      Exemplos (valores padrão):

      oidcOptions.CallbackPath = new PathString("/signin-oidc");
      oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
      oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc");
      
  • (Microsoft Azure somente com o ponto de extremidade "comum") TokenValidationParameters.IssuerValidator: muitos provedores OIDC funcionam com o validador de emissor padrão, mas precisamos considerar o emissor parametrizado com a ID do Locatário ({TENANT ID}) retornada por https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Para obter mais informações, consulte SecurityTokenInvalidIssuerException com OpenID Connect e o ponto de extremidade "comum" do Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet nº 1731).

    Somente para aplicativos que usam o Microsoft Entra ID ou o Azure AD B2C com o ponto de extremidade "comum":

    var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority);
    oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate;
    

Código de aplicativo de exemplo

Inspecione o aplicativo de exemplo para obter os seguintes recursos:

  • Atualização automática de token não interativo com a ajuda de um atualizador cookie personalizado (CookieOidcRefresher.cs).
  • O projeto do servidor chama AddAuthenticationStateSerialization para adicionar um provedor de estado de autenticação do lado do servidor que usa PersistentComponentState para fluir o estado de autenticação para o cliente. O cliente chama AddAuthenticationStateDeserialization para desserializar e usar o estado de autenticação passado pelo servidor. O estado de autenticação é corrigido para o tempo de vida do aplicativo WebAssembly.
  • As solicitações para o Blazor Web App são encaminhadas por proxy para o projeto da API Web de back-end (MinimalApiJwt). MapForwarder no arquivo Program adiciona o encaminhamento direto de solicitações HTTP que correspondem ao padrão especificado a um destino específico usando a configuração padrão para a solicitação de saída, transformações personalizadas e cliente HTTP padrão:
    • Ao renderizar o componente Weather no servidor, o componente usa o proxy ServerWeatherForecaster da solicitação de dados meteorológicos com o token de acesso do usuário.
    • Quando o componente é renderizado no cliente, o componente usa a implementação do serviço ClientWeatherForecaster, que usa um HttpClient pré-configurado (no arquivo do projeto Program do cliente) para fazer uma chamada à API Web para o projeto do servidor. Um ponto de extremidade de API mínimo (/weather-forecast) definido no arquivo do projeto Program do servidor transforma a solicitação com o token de acesso do usuário para obter os dados meteorológicos.
  • Atualização automática de token não interativo com a ajuda de um atualizador cookie personalizado (CookieOidcRefresher.cs).
  • A classe PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) é um AuthenticationStateProvider do lado do servidor que usa PersistentComponentState para transmitir o estado de autenticação para o cliente, que é então corrigido para o tempo de vida do aplicativo WebAssembly.
  • As solicitações para o Blazor Web App são encaminhadas por proxy para o projeto da API Web de back-end (MinimalApiJwt). MapForwarder no arquivo Program adiciona o encaminhamento direto de solicitações HTTP que correspondem ao padrão especificado a um destino específico usando a configuração padrão para a solicitação de saída, transformações personalizadas e cliente HTTP padrão:
    • Ao renderizar o componente Weather no servidor, o componente usa o proxy ServerWeatherForecaster da solicitação de dados meteorológicos com o token de acesso do usuário.
    • Quando o componente é renderizado no cliente, o componente usa a implementação do serviço ClientWeatherForecaster, que usa um HttpClient pré-configurado (no arquivo do projeto Program do cliente) para fazer uma chamada à API Web para o projeto do servidor. Um ponto de extremidade de API mínimo (/weather-forecast) definido no arquivo do projeto Program do servidor transforma a solicitação com o token de acesso do usuário para obter os dados meteorológicos.

Para mais informações sobre chamadas à API (Web) usando abstrações de serviço em Blazor Web Apps, confira Chamar uma API Web de um Blazor do ASP.NET Core.

Projeto Blazor Web App do lado do cliente (BlazorWebAppOidc.Client)

O projeto BlazorWebAppOidc.Client é o projeto do lado do cliente do Blazor Web App.

O cliente chama AddAuthenticationStateDeserialization para desserializar e usar o estado de autenticação passado pelo servidor. O estado de autenticação é corrigido para o tempo de vida do aplicativo WebAssembly.

A classe PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) é uma AuthenticationStateProvider do lado do cliente que determina o estado de autenticação do usuário procurando dados persistidos na página quando ele foi renderizado no servidor. O estado de autenticação é corrigido para o tempo de vida do aplicativo WebAssembly.

Se o usuário precisar fazer logon ou logoff, será necessário recarregar a página inteira.

O aplicativo de exemplo fornece apenas um nome de usuário e email para fins de exibição. Ele não inclui tokens que se autenticam no servidor ao fazer solicitações subsequentes, que funciona separadamente usando um cookie que é incluído nas solicitações HttpClient ao servidor.

Projeto de API Web de back-end (MinimalApiJwt)

O projeto MinimalApiJwt é uma API Web de back-end para vários projetos de front-end. O projeto configura um ponto de extremidade de API mínima para dados meteorológicos. As solicitações do projeto do lado do servidor do Blazor Web App (BlazorWebAppOidc) são encaminhados por proxy para o projeto MinimalApiJwt.

Configuração

Configure o projeto na JwtBearerOptions da chamada AddJwtBearer no arquivo Program do projeto:

  • Audience: define o Público-alvo para qualquer token do OpenID Connect recebido.

    No portal do Azure ou do Entra: combine o valor apenas com o caminho do URI da ID do Aplicativo configurado ao adicionar o escopo Weather.Get em Expor uma API:

    jwtOptions.Audience = "{APP ID URI}";
    

    Exemplo:

    URI da ID do aplicativo ({APP ID URI}): https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}:

    • Nome do diretório ({DIRECTORY NAME}): contoso
    • ID do aplicativo (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444
    jwtOptions.Audience = "https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444";
    

    O exemplo anterior pertence a um aplicativo registrado em um locatário com um tipo de locatário AAD B2C. Se o aplicativo estiver registrado em um locatário ME-ID, o URI da ID do aplicativo será diferente, portanto, o público-alvo será diferente.

    Exemplo:

    URI da ID do aplicativo ({APP ID URI}): api://{CLIENT ID} com a ID do aplicativo (cliente) ({CLIENT ID}): 00001111-aaaa-2222-bbbb-3333cccc4444

    jwtOptions.Audience = "api://00001111-aaaa-2222-bbbb-3333cccc4444";
    
  • Authority: define a autoridade para fazer chamadas do OpenID Connect. Combine o valor com a Autoridade configurada para o manipulador OIDC em BlazorWebAppOidc/Program.cs:

    jwtOptions.Authority = "{AUTHORITY}";
    

    Exemplo:

    Autoridade ({AUTHORITY}): https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/ (usa a ID do Locatário aaaabbbb-0000-cccc-1111-dddd2222eeee)

    jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
    

    O exemplo anterior pertence a um aplicativo registrado em um locatário com um tipo de locatário AAD B2C. Se o aplicativo estiver registrado em um locatário do ME-ID, a autoridade deverá corresponder ao emissor (iss) do JWT retornado pelo provedor de identity:

    jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
    

API mínima para dados meteorológicos

Proteja o ponto de extremidade de dados de previsão do tempo no arquivo Program do projeto:

app.MapGet("/weather-forecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
}).RequireAuthorization();

O método de extensão RequireAuthorization requer autorização para a definição de rota. Para todos os controladores que você adicionar ao projeto, adicione o atributo [Authorize] ao controlador ou à ação.

Redirecionar para a home page na saída

Quando um usuário navega pelo aplicativo, o componente LogInOrOut (Layout/LogInOrOut.razor) define um campo oculto para a URL de retorno (ReturnUrl) para o valor da URL atual (currentURL). Quando o usuário sai do aplicativo, o provedor de identity o retorna para a página da qual se desconectou.

Se o usuário sair de uma página segura, ele será retornado para a mesma página segura depois de sair apenas para ser enviado de volta por meio do processo de autenticação. Esse comportamento é bom quando os usuários precisam alternar contas com frequência. No entanto, uma especificação de aplicativo alternativa pode solicitar que o usuário retorne à home page do aplicativo ou a alguma outra página após a saída. O exemplo a seguir mostra como definir a home page do aplicativo como a URL de retorno para operações de saída.

As alterações importantes no componente LogInOrOut são demonstradas no exemplo a seguir. Não há necessidade de fornecer um campo oculto para o ReturnUrl conjunto da home página em / porque esse é o caminho padrão. IDisposable não é mais implementado. O NavigationManager não é mais injetado. O bloco inteiro @code é removido.

Layout/LogInOrOut.razor:

@using Microsoft.AspNetCore.Authorization

<div class="nav-item px-3">
    <AuthorizeView>
        <Authorized>
            <form action="authentication/logout" method="post">
                <AntiforgeryToken />
                <button type="submit" class="nav-link">
                    <span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
                    </span> Logout @context.User.Identity?.Name
                </button>
            </form>
        </Authorized>
        <NotAuthorized>
            <a class="nav-link" href="authentication/login">
                <span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span> 
                Login
            </a>
        </NotAuthorized>
    </AuthorizeView>
</div>

Nonce criptográfico

Um nonce é um valor de cadeia de caracteres que associa a sessão de um cliente a um token de ID para atenuar ataques de reprodução.

Se você receber um erro de nonce durante o desenvolvimento e teste de autenticação, use uma nova sessão do navegador InPrivate/incógnito para cada execução de teste, não importa o tamanho da alteração feita no aplicativo ou no usuário de teste, pois os dados obsoletos de cookie podem resultar em um erro de nonce. Para mais informações, confira a seção Cookies e dados do site.

Um nonce não é necessário ou usado quando um token de atualização é trocado por um novo token de acesso. No aplicativo de exemplo, o CookieOidcRefresher (CookieOidcRefresher.cs) define deliberadamente OpenIdConnectProtocolValidator.RequireNonce como false.

Funções de aplicativo para aplicativos não registrados com o Microsoft Entra (ME-ID)

Esta seção se refere a aplicativos que não usam o ME-ID (Microsoft Entra ID) como o provedor de identity. Para aplicativos registrados com ME-ID, consulte a seção Funções de aplicativo para aplicativos registrados com o Microsoft Entra (ME-ID).

Configure o tipo de declaração de função (TokenValidationParameters.RoleClaimType) no OpenIdConnectOptions de Program.cs:

oidcOptions.TokenValidationParameters.RoleClaimType = "{ROLE CLAIM TYPE}";

Para muitos provedores de identity do OIDC, o tipo de declaração de função é role. Verifique a documentação do seu provedor de identity para obter o valor correto.

Substitua a classe UserInfo no projeto BlazorWebAppOidc.Client pela classe a seguir.

UserInfo.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;

namespace BlazorWebAppOidc.Client;

// Add properties to this class and update the server and client 
// AuthenticationStateProviders to expose more information about 
// the authenticated user to the client.
public sealed class UserInfo
{
    public required string UserId { get; init; }
    public required string Name { get; init; }
    public required string[] Roles { get; init; }

    public const string UserIdClaimType = "sub";
    public const string NameClaimType = "name";
    private const string RoleClaimType = "role";

    public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
        new()
        {
            UserId = GetRequiredClaim(principal, UserIdClaimType),
            Name = GetRequiredClaim(principal, NameClaimType),
            Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
                .ToArray(),
        };

    public ClaimsPrincipal ToClaimsPrincipal() =>
        new(new ClaimsIdentity(
            Roles.Select(role => new Claim(RoleClaimType, role))
                .Concat([
                    new Claim(UserIdClaimType, UserId),
                    new Claim(NameClaimType, Name),
                ]),
            authenticationType: nameof(UserInfo),
            nameType: NameClaimType,
            roleType: RoleClaimType));

    private static string GetRequiredClaim(ClaimsPrincipal principal,
        string claimType) =>
            principal.FindFirst(claimType)?.Value ??
            throw new InvalidOperationException(
                $"Could not find required '{claimType}' claim.");
}

Neste ponto, os componentes Razor podem adotar autorização baseada em função e baseada em política. As funções de aplicativo aparecem em declarações role, uma declaração por função.

Funções de aplicativo para aplicativos registrados com o Microsoft Entra (ME-ID)

Use as diretrizes nesta seção para implementar funções de aplicativo, grupos de segurança ME-ID e funções da conta de administrador interno do ME-ID para aplicativos que usam o ME-ID (Microsoft Entra ID).

A abordagem descrita nesta seção configura o ME-ID para enviar grupos e funções no cabeçalho de autenticação cookie. Quando os usuários são apenas membros de alguns grupos de segurança e funções, a abordagem a seguir deve funcionar para a maioria das plataformas de hospedagem sem encontrar um problema em que os cabeçalhos são muito longos, por exemplo, com a hospedagem do IIS que tem um limite de comprimento de cabeçalho padrão de 16 KB (MaxRequestBytes). Se o comprimento do cabeçalho for um problema devido à alta associação de grupo ou função, recomendamos não seguir as diretrizes nesta seção em favor da implementação do Microsoft Graph para obter os grupos e funções de um usuário do ME-ID separadamente, uma abordagem que não aumenta o tamanho da autenticação cookie. Para obter mais informações, consulte Solicitação incorreta – Solicitação muito longa – Servidor IIS (dotnet/aspnetcore #57545).

Configure o tipo de declaração de função (TokenValidationParameters.RoleClaimType) em OpenIdConnectOptions de Program.cs. Defina o valor como roles:

oidcOptions.TokenValidationParameters.RoleClaimType = "roles";

Embora não seja possível atribuir funções a grupos sem uma conta ME-ID Premium, você pode atribuir funções aos usuários e receber declarações de funções para usuários com uma conta padrão do Azure. As diretrizes nesta seção não exigem uma conta Premium do ME-ID.

Ao trabalhar com o diretório padrão, siga as diretrizes em Adicionar funções de aplicativo a seu aplicativo e recebê-las no token (documentação do ME-ID) para configurar e atribuir funções. Se você não estiver trabalhando com o diretório padrão, edite o manifesto do aplicativo no portal do Azure para estabelecer as funções do aplicativo manualmente na entrada appRoles do arquivo de manifesto. Para obter mais informações, consulte Configurar a declaração de função (documentação do ME-ID).

Os grupos de segurança do Azure de um usuário chegam em declarações groups e as atribuições de função de administrador do ME-ID internas de um usuário chegam em declarações de IDs (wids) conhecidas. Os valores para ambos os tipos de declaração são GUIDs. Quando recebidas pelo aplicativo, essas declarações podem ser usadas para estabelecer a autorização de função e política nos componentes Razor.

No manifesto do aplicativo no portal do Azure, defina o atributo groupMembershipClaims como All. Um valor de All resulta no envio pelo ME-ID de todos os grupos de segurança/distribuição (declarações groups) e funções (declarações wids) do usuário conectado. Para definir o atributo groupMembershipClaims, faça o seguinte:

  1. Abra o registro do aplicativo no portal do Azure.
  2. Selecione Gerenciar>Manifesto na barra lateral.
  3. Localize o atributo groupMembershipClaims.
  4. Defina o valor como All ("groupMembershipClaims": "All").
  5. Selecione o botão Salvar.

Substitua a classe UserInfo no projeto BlazorWebAppOidc.Client pela classe a seguir.

UserInfo.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using System.Security.Claims;

namespace BlazorWebAppOidc.Client;

// Add properties to this class and update the server and client 
// AuthenticationStateProviders to expose more information about 
// the authenticated user to the client.
public sealed class UserInfo
{
    public required string UserId { get; init; }
    public required string Name { get; init; }
    public required string[] Roles { get; init; }
    public required string[] Groups { get; init; }
    public required string[] Wids { get; init; }

    public const string UserIdClaimType = "sub";
    public const string NameClaimType = "name";
    private const string RoleClaimType = "roles";
    private const string GroupsClaimType = "groups";
    private const string WidsClaimType = "wids";

    public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
        new()
        {
            UserId = GetRequiredClaim(principal, UserIdClaimType),
            Name = GetRequiredClaim(principal, NameClaimType),
            Roles = principal.FindAll(RoleClaimType).Select(c => c.Value)
                .ToArray(),
            Groups = principal.FindAll(GroupsClaimType).Select(c => c.Value)
                .ToArray(),
            Wids = principal.FindAll(WidsClaimType).Select(c => c.Value)
                .ToArray(),
        };

    public ClaimsPrincipal ToClaimsPrincipal() =>
        new(new ClaimsIdentity(
            Roles.Select(role => new Claim(RoleClaimType, role))
                .Concat(Groups.Select(role => new Claim(GroupsClaimType, role)))
                .Concat(Wids.Select(role => new Claim(WidsClaimType, role)))
                .Concat([
                    new Claim(UserIdClaimType, UserId),
                    new Claim(NameClaimType, Name),
                ]),
            authenticationType: nameof(UserInfo),
            nameType: NameClaimType,
            roleType: RoleClaimType));

    private static string GetRequiredClaim(ClaimsPrincipal principal,
        string claimType) =>
            principal.FindFirst(claimType)?.Value ??
            throw new InvalidOperationException(
                $"Could not find required '{claimType}' claim.");
}

Neste ponto, os componentes Razor podem adotar autorização baseada em função e baseada em política:

  • As funções de aplicativo aparecem em declarações roles, uma declaração por função.
  • Os grupos de segurança aparecem em declarações groups, uma declaração por grupo. Os GUIDs do grupo de segurança aparecem no portal do Azure quando você cria um grupo de segurança e são listados ao selecionar Identity>Visão geral>Grupos>Exibir.
  • As funções de administrador de ME-ID internas aparecem em declarações wids, uma declaração por função. A declaração wids com um valor de b79fbf4d-3ef9-4689-8143-76b194e85509 é sempre enviada pelo ME-ID para contas não convidadas do locatário e não se refere a uma função de administrador. Os GUIDs da função de administrador (IDs de modelo de função) aparecem no portal do Azure ao selecionar Funções e administradores, seguido por reticências () >Descrição para a função listada. As IDs do modelo de função também estão listadas em Funções internas do Microsoft Entra (documentação do Entra).

Solucionar problemas

Logging

O aplicativo de servidor é um aplicativo ASP.NET Core padrão. Consulte as diretrizes de registro em log do ASP.NET Core para habilitar um nível de log mais baixo no aplicativo servidor.

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.

    A cobertura de configuração neste artigo mostra exemplos da configuração correta. Verifique cuidadosamente a configuração em busca de alguma configuração incorreta 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:

    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 cibernéticos, confira Relatar problemas e bugs de segurança (Repositório GitHub dotnet/aspnetcore).

  • 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 nas 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 login do usuário
  • Cookies de aplicativos
  • Dados do site armazenados e em cache

Uma abordagem para impedir que cookies e dados do site persistentes 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}, onde o espaço reservado {URL} é a URL a ser aberta (por exemplo, https://localhost:5001).
      • Mozilla Firefox: Use -private -url {URL}, onde o {URL} espaço reservado é a URL para abrir (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.

Executar o aplicativo de servidor

Ao testar e solucionar problemas de um Blazor Web App, verifique se você está executando o aplicativo no projeto do servidor.

Inspecionar o usuário

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

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]

<PageTitle>User Claims</PageTitle>

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li><b>@claim.Type:</b> @claim.Value</li>
        }
    </ul>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    [CascadingParameter]
    private Task<AuthenticationState>? AuthState { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (AuthState == null)
        {
            return;
        }

        var authState = await AuthState;
        claims = authState.User.Claims;
    }
}

Recursos adicionais