Share via


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

Este artigo descreve como proteger um Aplicativo Web Blazor com o OpenID Connect (OIDC) usando um aplicativo de amostra no dotnet/blazor-samples repositório GitHub (.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 aplicativo Web Blazor 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, com o 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 aplicativo Web Blazor, que contém um exemplo de ponto de extremidade de API mínima para dados meteorológicos.
  • BlazorWebAppOidc.Client: projeto do lado do cliente do aplicativo Web Blazor.

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 de aplicativo Web Blazor do lado do servidor (BlazorWebAppOidc)

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

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 o Microsoft Entra ID e o Azure AD B2C, você pode usar AddMicrosoftIdentityWebApp do Microsoft Identity Web (Microsoft.Identity.Web pacote NuGet, documentação da API), que adiciona os manipuladores de OIDC e autenticação Cookie 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.

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 persistir a identidade 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 por padrão 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/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/ (usa a ID do Locatário a3942615-d115-4eb7-bc84-9974abcf5064)
    • ID de Cliente ({CLIENT ID}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/";
    oidcOptions.ClientId = "4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    

    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/";
    
  • ClientSecret: o segredo do cliente OIDC.

    O exemplo a seguir é apenas para fins de teste e demonstração. Não armazene o segredo do cliente no assembly do aplicativo ou o adicione ao controle do código-fonte. Armazene o segredo do cliente em Segredos do Usuário, no Azure Key Vault ou em uma variável de ambiente.

    A configuração do esquema de autenticação é lida automaticamente de builder.Configuration["Authentication:Schemes:{SCHEME NAME}:{PropertyName}"], no qual o espaço reservado {SCHEME NAME} é o esquema, que é MicrosoftOidc por padrão. Como a configuração é pré-definida, um segredo do cliente pode ser lido automaticamente por meio da chave de configuração Authentication:Schemes:MicrosoftOidc:ClientSecret. No servidor que usa variáveis de ambiente, nomeie a variável de ambiente Authentication__Schemes__MicrosoftOidc__ClientSecret:

    set Authentication__Schemes__MicrosoftOidc__ClientSecret={CLIENT SECRET}
    

    Somente para demonstração e teste, é possível definir ClientSecret diretamente. Não defina o valor diretamente para aplicativos de produção implantados. Para uma segurança ligeiramente aprimorada, compile condicionalmente a linha com o símbolo DEBUG:

    #if DEBUG
    oidcOptions.ClientSecret = "{CLIENT SECRET}";
    #endif
    

    Exemplo:

    Segredo do cliente ({CLIENT SECRET}): 463471c8c4...f90d674bc9 (abreviado para exibição)

    #if DEBUG
    oidcOptions.ClientSecret = "463471c8c4...137f90d674bc9";
    #endif
    
  • 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 para Concessões implícitas e fluxos híbridos do portal do Azure ou no Entra, *não marque a caixa de seleção para o ponto de extremidade de autorização retornar 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 mapeia manualmente as declarações de nome e função:

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 = JwtRegisteredClaimNames.Name;
oidcOptions.TokenValidationParameters.RoleClaimType = "role";
  • 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 identidade.

      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).
  • 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 aplicativo Web Blazor para 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 obter mais informações sobre chamadas à API (Web) usando abstrações de serviço em aplicativos Web Blazor, confira Chamar uma API Web de um aplicativo do ASP.NET CoreBlazor.

Projeto de aplicativo Web Blazor do lado do cliente (BlazorWebAppOidc.Client)

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

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 aplicativo Web Blazor 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, com o Microsoft Identity Web e hospedado no Azure.
  • Atualização automática de token não interativo.
  • O padrão Back-end for Front-end (BFF) é 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 aplicativo Web Blazor 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.

Aviso de versão prévia do pacote

Aviso

As tecnologias e os pacotes usados pelo aplicativo de exemplo BlazorWebAppOidcBff e descritos neste artigo estão em versão prévia no momento. O conteúdo do artigo, a API e o aplicativo de exemplo não têm suporte no momento e atualmente não são recomendados para uso em produção. O aplicativo de exemplo e as diretrizes estão sujeitos a alterações sem aviso prévio.

Pré-requisito

O .NET Aspire requer o Visual Studio 17.10 ou versão 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 aplicativo Web Blazor.
  • BlazorWebAppOidc.Client: projeto do lado do cliente do aplicativo Web Blazor.

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)

Projetos do .NET Aspire

Para obter 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 obter mais informações, consulte a seção Pré-requisitos do Início Rápido: como criar seu primeiro aplicativo .NET Aspire.

Projeto de aplicativo Web Blazor do lado do servidor (BlazorWebAppOidc)

O projeto BlazorWebAppOidc é o projeto do lado do servidor do aplicativo Web Blazor. 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 o Microsoft Entra ID e o Azure AD B2C, você pode usar AddMicrosoftIdentityWebApp do Microsoft Identity Web (Microsoft.Identity.Web pacote NuGet, documentação da API), que adiciona os manipuladores de OIDC e autenticação Cookie 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.

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 persistir a identidade 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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    • Escopo configurado para dados meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f/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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    • Escopo configurado para dados meteorológicos de MinimalApiJwt ({API NAME}): Weather.Get
    oidcOptions.Scope.Add("api://4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f/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/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/ (usa a ID do Locatário a3942615-d115-4eb7-bc84-9974abcf5064)
    • ID de Cliente ({CLIENT ID}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/";
    oidcOptions.ClientId = "4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    

    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/";
    
  • ClientSecret: o segredo do cliente OIDC.

    O exemplo a seguir é apenas para fins de teste e demonstração. Não armazene o segredo do cliente no assembly do aplicativo ou o adicione ao controle do código-fonte. Armazene o segredo do cliente em Segredos do Usuário, no Azure Key Vault ou em uma variável de ambiente.

    A configuração do esquema de autenticação é lida automaticamente de builder.Configuration["Authentication:Schemes:{SCHEME NAME}:{PropertyName}"], no qual o espaço reservado {SCHEME NAME} é o esquema, que é MicrosoftOidc por padrão. Como a configuração é pré-definida, um segredo do cliente pode ser lido automaticamente por meio da chave de configuração Authentication:Schemes:MicrosoftOidc:ClientSecret. No servidor que usa variáveis de ambiente, nomeie a variável de ambiente Authentication__Schemes__MicrosoftOidc__ClientSecret:

    set Authentication__Schemes__MicrosoftOidc__ClientSecret={CLIENT SECRET}
    

    Somente para demonstração e teste, é possível definir ClientSecret diretamente. Não defina o valor diretamente para aplicativos de produção implantados. Para uma segurança ligeiramente aprimorada, compile condicionalmente a linha com o símbolo DEBUG:

    #if DEBUG
    oidcOptions.ClientSecret = "{CLIENT SECRET}";
    #endif
    

    Exemplo:

    Segredo do cliente ({CLIENT SECRET}): 463471c8c4...f90d674bc9 (abreviado para exibição)

    #if DEBUG
    oidcOptions.ClientSecret = "463471c8c4...137f90d674bc9";
    #endif
    
  • 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 para Concessões implícitas e fluxos híbridos do portal do Azure ou no Entra, *não marque a caixa de seleção para o ponto de extremidade de autorização retornar 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 mapeia manualmente as declarações de nome e função:

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 = JwtRegisteredClaimNames.Name;
oidcOptions.TokenValidationParameters.RoleClaimType = "role";
  • 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 identidade.

      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).
  • 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 aplicativo Web Blazor são encaminhadas por proxy para o projeto de 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 obter mais informações sobre chamadas à API (Web) usando abstrações de serviço em aplicativos Web Blazor, confira Chamar uma API Web de um aplicativo do ASP.NET CoreBlazor.

Projeto de aplicativo Web Blazor do lado do cliente (BlazorWebAppOidc.Client)

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

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 Aplicativo Web Blazor (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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f
    jwtOptions.Audience = "https://contoso.onmicrosoft.com/4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    

    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}): 4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f

    jwtOptions.Audience = "api://4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f";
    
  • 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/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/ (usa a ID do Locatário a3942615-d115-4eb7-bc84-9974abcf5064)

    jwtOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/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 ME-ID, a autoridade deverá corresponder ao emissor (iss) do JWT retornado pelo provedor de identidade:

    jwtOptions.Authority = "https://sts.windows.net/a3942615-d115-4eb7-bc84-9974abcf5064/";
    

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 identidade 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. O value do campo oculto para o ReturnUrl é definido como a home page em /. 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 />
                <input type="hidden" name="ReturnUrl" value="/" />
                <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 obter 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.

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, 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.

Executar o aplicativo de servidor

Ao testar e solucionar problemas de um aplicativo Web Blazor, verifique se você está executando o aplicativo do 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.Count() > 0)
{
    <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