Partilhar via


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

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Importante

Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.

Para a versão atual, consulte a versão .NET 9 deste artigo.

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

Para o Microsoft Entra ID ou Azure AD B2C, pode usar AddMicrosoftIdentityWebApp de Microsoft Identity Web, (Microsoft.Identity.Web pacote NuGet, documentação da API), que adiciona tanto os handlers de autenticação OIDC como Cookie com as definições padrão apropriadas. O aplicativo de exemplo e as diretrizes neste artigo não usam o Microsoft Identity Web. As instruções demonstram como configurar o manipulador OIDC manualmente para qualquer provedor OIDC. Para obter mais informações sobre como implementar o Microsoft Identity Web, consulte Proteger um ASP.NET Core Blazor Web App com o Microsoft Entra ID.

Esta versão do artigo aborda a implementação do OIDC sem adotar o padrão Backend for Frontend (BFF) com um aplicativo que adota renderização automática interativa global (projetos de servidor e .Client). O padrão BFF é útil para fazer solicitações autenticadas para serviços externos. Altere o seletor de versão do artigo para padrão BFF se a especificação do aplicativo exigir a adoção do padrão BFF.

Adota-se a seguinte especificação:

  • O Blazor Web App usa modo de renderização automática com interatividade global.
  • Os serviços personalizados do provedor de estado de autenticação são usados pelos aplicativos de servidor e cliente para capturar o estado de autenticação do usuário e fluí-lo entre o servidor e o cliente.
  • Este aplicativo é um ponto de partida para qualquer fluxo de autenticação OIDC. OIDC é configurado manualmente na app e não depende do Microsoft Entra ID ou dos pacotes Microsoft Identity Web, nem a app de exemplo requer hospedagem no Microsoft Azure. No entanto, o aplicativo de exemplo pode ser usado com o Entra, o Microsoft Identity Web e hospedado no Azure.
  • Atualização automática de token que não requer interação.
  • Um projeto de API da Web separado demonstra uma chamada de API da Web segura para dados meteorológicos.

Para obter uma experiência alternativa usando Biblioteca de Autenticação da Microsoft para .NET, Microsoft Identity Web e Microsoft Entra ID, consulte Proteger um Blazor Web App ASP.NET Core com o Microsoft Entra ID.

Solução de amostra

O aplicativo de exemplo consiste nos seguintes projetos:

  • BlazorWebAppOidc: Projeto do lado do servidor do Blazor Web App, contendo um exemplo de endpoint de Minimal API para dados meteorológicos.
  • BlazorWebAppOidc.Client: Projeto cliente do Blazor Web App.
  • MinimalApiJwt: API da Web de back-end com um ponto de extremidade de API mínima para dados meteorológicos.

Acesse o exemplo através da pasta da versão mais recente no repositório de exemplos Blazor com o link a seguir. O exemplo está na BlazorWebAppOidc pasta para .NET 8 ou posterior.

Inicie a solução a partir do Aspire/Aspire.AppHost projeto.

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

Exemplos de recursos da solução:

  • Atualização automática de token não interativo com a ajuda de um atualizador cookie personalizado (CookieOidcRefresher.cs).

  • Os dados meteorológicos são tratados por um ponto de extremidade de API mínima (/weather-forecast) no Program arquivo (Program.cs) do MinimalApiJwt projeto. O endpoint requer autorização ao chamar RequireAuthorization. Para todos os controladores que você adicionar ao projeto, adicione o atributo [Authorize] ao controlador ou ação. Para obter mais informações sobre como exigir autorização em todo o aplicativo por meio de uma política de autorização de e não exigir autorização em um subconjunto de pontos públicos finais, consulte a orientação OIDC das páginas Razor.

  • O aplicativo chama com segurança uma API da Web para dados meteorológicos:

    • Ao renderizar o componente Weather no servidor, o componente usa o ServerWeatherForecaster no servidor para obter dados meteorológicos da API da Web no projeto MinimalApiJwt usando um DelegatingHandler (TokenHandler) que anexa o token de acesso do HttpContext à solicitação.
    • Quando o componente é renderizado no cliente, o componente usa a implementação do serviço de ClientWeatherForecaster, que utiliza um HttpClient preconfigurado (no ficheiro de Program do projeto cliente) para fazer a chamada de API a partir do ServerWeatherForecaster do projeto do servidor.
  • A classe PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) é uma AuthenticationStateProvider do lado do servidor que usa PersistentComponentState para transmitir o estado de autenticação ao cliente, o qual é então mantido durante o tempo de vida da aplicação WebAssembly.

Para obter mais informações sobre chamadas de API da Web usando abstrações de serviço em Blazor Web Apps, consulte Chamar uma API da Web de um aplicativo ASP.NET Core Blazor.

Registos da aplicação Microsoft Entra ID

Recomendamos o uso de registros separados para aplicativos e APIs da Web, mesmo quando os aplicativos e as APIs da Web estiverem na mesma solução. A orientação a seguir é para o aplicativo BlazorWebAppOidc e a API da web MinimalApiJwt da solução modelo, mas a mesma orientação se aplica, em geral, a quaisquer registos baseados em Entra para aplicativos e APIs da web.

Registre a API da Web (MinimalApiJwt) primeiro para que você possa conceder acesso à API da Web ao registrar o aplicativo. O ID do tenant e o ID do cliente da Web API são utilizados para configurar a Web API no seu ficheiro Program. Depois de registar a API da Web, exponha a API da Web em Registos de Aplicações>Expor uma API com o nome de escopo de Weather.Get. Registre o URI da ID do aplicativo para uso na configuração do aplicativo.

Em seguida, registre o aplicativo (BlazorWebAppOidc/BlazorWebApOidc.Client) com uma configuração de plataforma Web e um URI de redirecionamento de https://localhost/signin-oidc (uma porta não é necessária). Os IDs do locatário e do cliente do aplicativo, juntamente com o endereço base da API web, o URI do ID do aplicativo e o nome do escopo meteorológico, são usados para configurar o aplicativo no seu ficheiro Program. Conceda permissão à API para aceder à API da web em Registros de aplicativos>Permissões da API. Se a especificação de segurança do aplicativo exigir, você poderá conceder consentimento de administrador para que a organização acesse a API da Web. Os utilizadores e grupos autorizados são atribuídos ao registo da aplicação em Registos de> aplicaçõesAplicações empresariais.

Na configuração de registo do portal do Entra ou do Azure para "concessão implícita e fluxos híbridos", não marque nenhuma das caixas de seleção do ponto de extremidade de autorização para que retornem tokens de acesso ou tokens de ID. O manipulador OpenID Connect solicita automaticamente os tokens apropriados usando o código retornado do endpoint de autorização.

Crie um segredo do cliente no registro do aplicativo no portal do Entra ou do Azure (Gerenciar>certificados & segredos> do cliente). Mantenha o valor secreto do cliente para uso na próxima seção.

Orientações adicionais de configuração do Entra para configurações específicas são fornecidas mais adiante neste artigo.

Defina o segredo do cliente

Esta seção só se aplica ao projeto de servidor do Blazor Web App (BlazorWebAppOidc projeto).

Advertência

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

Para testes de desenvolvimento local, use a ferramenta Secret Manager para armazenar o segredo do cliente do projeto de servidor sob a chave de configuração Blazor.

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

dotnet user-secrets init

Execute o seguinte comando para definir o segredo do cliente. O marcador {SECRET} é o segredo do cliente obtido a partir do registo da aplicação:

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

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

MinimalApiJwt projeto

O projeto MinimalApiJwt é uma API web de back-end para vários projetos de frontend. O projeto configura um endpoint de API Minimal para dados meteorológicos.

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

O projeto inclui pacotes e configuração para produzir documentos OpenAPI e a UI do Swagger no ambiente de desenvolvimento. Para obter mais informações, consulte Usar os documentos OpenAPI gerados.

O projeto cria um endpoint Minimal API para dados meteorológicos:

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

Configure o projeto em JwtBearerOptions da chamada AddJwtBearer no ficheiro Program do projeto.

O Authority define a autoridade para fazer chamadas OIDC. Recomendamos o uso de um registro de aplicativo separado para o MinimalApiJwt projeto. A autoridade confirma o emissor (iss) do JWT retornado pelo provedor de identidade.

jwtOptions.Authority = "{AUTHORITY}";

O formato da Autoridade depende do tipo de inquilino utilizado. Os exemplos a seguir para o Microsoft Entra ID usam uma ID de locatário de aaaabbbb-0000-cccc-1111-dddd2222eeee.

ME-ID exemplo de autoridade do inquilino:

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

Exemplo de Autoridade de locatário B2C do AAD:

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

O Audience define a audiência para qualquer token OIDC recebido.

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

Observação

Ao utilizar o Microsoft Entra ID, corresponda o valor apenas ao caminho do URI da ID da Aplicação configurado ao adicionar o Weather.Get escopo em Expor uma API no portal do Entra ou do Azure. Não inclua o nome do escopo, "Weather.Get," no valor.

O formato do Público depende do tipo de locatário em uso. Os exemplos a seguir para o Microsoft Entra ID usam uma ID de Locatário de contoso e uma ID de Cliente de 11112222-bbbb-3333-cccc-4444dddd5555.

ME-ID exemplo de URI de ID de aplicativo de inquilino:

jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";

Exemplo de URI de ID de aplicação do inquilino B2C do Azure Active Directory:

jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";

Blazor Web App projeto de servidor (BlazorWebAppOidc)

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

A DelegatingHandler (TokenHandler) gerencia a anexação do token de acesso de um usuário às solicitações de saída. O manipulador de token só é executado durante a renderização estática do lado do servidor (SSR estático), portanto, o uso HttpContext é seguro neste cenário. Para obter mais informações, consulte IHttpContextAccessor/HttpContext em aplicativos ASP.NET Core Blazor e ASP.NET Core do lado do servidor e Blazor Web App cenários adicionais de segurança.

TokenHandler.cs:

public class TokenHandler(IHttpContextAccessor httpContextAccessor) : 
    DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (httpContextAccessor.HttpContext is null)
        {
            throw new Exception("HttpContext not available");
        }

        var accessToken = await httpContextAccessor.HttpContext
            .GetTokenAsync("access_token");

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}

No Program arquivo do projeto, o manipulador de tokens (TokenHandler) é registado como um serviço e definido como o manipulador de mensagens com AddHttpMessageHandler para realizar pedidos seguros para a MinimalApiJwt API web de back-end, utilizando um cliente HTTP nomeado ("ExternalApi").

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("ExternalApi",
      client => client.BaseAddress = new Uri(builder.Configuration["ExternalApiUri"] ?? 
          throw new Exception("Missing base address!")))
      .AddHttpMessageHandler<TokenHandler>();

No arquivo do projeto appsettings.json, configure o URL da API externa:

"ExternalApiUri": "{BASE ADDRESS}"

Exemplo:

"ExternalApiUri": "https://localhost:7277"

A seguinte configuração OpenIdConnectOptions é encontrada no ficheiro Program do projeto durante a chamada para AddOpenIdConnect:

PushedAuthorizationBehavior: Controla o suporte a solicitações de autorização push (PAR). Por padrão, a configuração é usar PAR se o documento de descoberta do provedor de identidade (geralmente encontrado em .well-known/openid-configuration) anunciar suporte para PAR. Se desejar exigir suporte PAR para o aplicativo, você pode atribuir um valor de PushedAuthorizationBehavior.Require. O PAR não é suportado pelo Microsoft Entra, e não há planos para que o Entra o suporte no futuro.

oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.UseIfAvailable;

SignInScheme: Define o esquema de autenticação correspondente ao middleware responsável pela persistência da identidade do usuário após uma autenticação bem-sucedida. O manipulador OIDC precisa usar um esquema de entrada que seja capaz de persistir as credenciais do usuário nas solicitações. A linha seguinte está presente apenas para fins de demonstração. Se omitido, DefaultSignInScheme é usado como um valor de fallback.

oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

Escopos para openid e profile (Scope) (Opcional): Os escopos openid e profile também são configurados por padrão porque são necessários para que o manipulador OIDC funcione, mas podem precisar ser adicionados novamente se os escopos forem incluídos na configuração Authentication:Schemes:MicrosoftOidc:Scope. Para obter orientações gerais de configuração, consulte Configuração no ASP.NET Core e configuração no ASP.NET Core Blazor.

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 é configurada como true para que o token de atualização seja armazenado para uma atualização não interativa.

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

Authority e ClientId: Define a Autoridade e a ID do Cliente para chamadas OIDC.

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

O exemplo a seguir usa uma ID de locatário de aaaabbbb-0000-cccc-1111-dddd2222eeee e uma ID de cliente de 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";

Para aplicações multicliente, a autoridade "common" deve ser usada. Você também pode usar a autoridade "comum" para aplicativos de locatário único, mas é necessário um IssuerValidator personalizado, conforme mostrado mais adiante nesta seção.

oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";

ResponseType: Configura o manipulador OIDC para executar apenas o fluxo de código de autorização. As subvenções implícitas e os fluxos híbridos são desnecessários neste modo. O manipulador OIDC solicita automaticamente os tokens apropriados usando o código retornado do endpoint de autorização.

oidcOptions.ResponseType = OpenIdConnectResponseType.Code;

MapInboundClaims e 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 de declaração 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 Microsoft Entra ID (ME-ID). Consulte a documentação do seu provedor de identidade para obter 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 de caminhos: os caminhos devem corresponder aos caminhos de URI de redirecionamento (caminho de retorno de chamada de login) e redirecionamento após término de sessão (caminho de retorno de chamada após término de sessão) configurados ao registrar a aplicação com o provedor OIDC. No portal do Azure, os caminhos são configurados no bloco Autenticação do registo 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 da aplicação a partir do qual o user-agent é retornado.

Configure o caminho de callback para terminar a sessão no registo do fornecedor OIDC da aplicação. No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost:{PORT}/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 requer a porta correta.

SignedOutCallbackPath (chave de configuração: "SignedOutCallbackPath"): O caminho da solicitação dentro do caminho base da aplicação interceptado pelo manipulador OIDC para onde o agente do utilizador é redirecionado pela primeira vez após terminar sessão no fornecedor de identidade. O aplicativo de exemplo não define um valor para o caminho porque o valor padrão de "/signout-callback-oidc" é usado. Depois de intercetar a solicitação, o manipulador OIDC redireciona para o SignedOutRedirectUri ou RedirectUri, se especificado.

Configure o caminho de callback para terminar a sessão no registo do fornecedor OIDC da aplicação. No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost:{PORT}/signout-callback-oidc

Observação

Ao usar o Microsoft Entra ID, defina o caminho nas entradas de URI de Redirecionamento da configuração da plataforma Web no portal do Entra ou do Azure. Uma porta não é necessária para endereços localhost ao usar o Entra. A maioria dos outros provedores OIDC requer a porta correta. Se não adicionar o URI do caminho de retorno de chamada com a sessão terminada ao registo da aplicação no Entra, o Entra recusa-se a redirecionar o utilizador de volta para a aplicação e simplesmente pede ao utilizador para fechar a janela do navegador.

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

No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost/signout-oidc

Observação

Ao usar o Microsoft Entra ID, defina a URL de logout do Canal Frontal no portal do Entra ou do Azure. Uma porta não é necessária para endereços localhost ao usar o Entra. A maioria dos outros provedores OIDC requer a 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 apenas 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 o ID do inquilino ({TENANT ID}) retornado por https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Para obter mais informações, consulte SecurityTokenInvalidIssuerException com OpenID Connect e o endpoint "comum" do Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

Apenas para aplicações que utilizem o Microsoft Entra ID ou o Azure AD B2C com o endpoint "comum":

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

Blazor Web App projeto cliente (BlazorWebAppOidc.Client)

O projeto BlazorWebAppOidc.Client é o projeto do lado 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 é fixo para o tempo de vida do aplicativo WebAssembly.

A classe PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) é um AuthenticationStateProvider do lado do cliente que determina o estado de autenticação do utilizador procurando dados persistentes na página quando é renderizada no servidor. O estado de autenticação é fixo para o tempo de vida do aplicativo WebAssembly.

Se o usuário precisar fazer login ou logout, será necessário um recarregamento de página inteira.

O aplicativo de exemplo fornece apenas um nome de usuário e e-mail para fins de exibição.

Para o Microsoft Entra ID ou Azure AD B2C, pode usar AddMicrosoftIdentityWebApp de Microsoft Identity Web, (Microsoft.Identity.Web pacote NuGet, documentação da API), que adiciona tanto os handlers de autenticação OIDC como Cookie com as definições padrão apropriadas. O aplicativo de exemplo e as diretrizes neste artigo não usam o Microsoft Identity Web. As instruções demonstram como configurar o manipulador OIDC manualmente para qualquer provedor OIDC. Para obter mais informações sobre como implementar o Microsoft Identity Web, consulte Proteger um ASP.NET Core Blazor Web App com o Microsoft Entra ID.

Esta versão do artigo aborda a implementação do OIDC sem adotar o padrão Backend for Frontend (BFF) com um aplicativo que adota a renderização global do Servidor Interativo (projeto único). O padrão BFF é útil para fazer solicitações autenticadas para serviços externos. Altere o seletor de versão do artigo para o padrão BFF se a especificação do aplicativo exigir a adoção do padrão BFF com renderização automática interativa global.

Adota-se a seguinte especificação:

  • O Blazor Web App usa o modo de renderização no servidor com interatividade global.
  • Este aplicativo é um ponto de partida para qualquer fluxo de autenticação OIDC. OIDC é configurado manualmente na app e não depende do Microsoft Entra ID ou dos pacotes Microsoft Identity Web, nem a app de exemplo requer hospedagem no Microsoft Azure. No entanto, o aplicativo de exemplo pode ser usado com o Entra, o Microsoft Identity Web e hospedado no Azure.
  • Atualização automática de token que não requer interação.
  • Um projeto de API da Web separado demonstra uma chamada de API da Web segura para dados meteorológicos.

Para obter uma experiência alternativa usando Biblioteca de Autenticação da Microsoft para .NET, Microsoft Identity Web e Microsoft Entra ID, consulte Proteger um Blazor Web App ASP.NET Core com o Microsoft Entra ID.

Solução de amostra

O aplicativo de exemplo consiste nos seguintes projetos:

  • BlazorWebAppOidcServer: Blazor Web App projeto do lado do servidor (renderização global do servidor interativo).
  • MinimalApiJwt: API da Web de back-end com um ponto de extremidade de API mínima para dados meteorológicos.

Acesse o exemplo através da pasta da versão mais recente no repositório de exemplos Blazor com o link a seguir. O exemplo está na BlazorWebAppOidcServer pasta para .NET 8 ou posterior.

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

Registos da aplicação Microsoft Entra ID

Recomendamos o uso de registros separados para aplicativos e APIs da Web, mesmo quando os aplicativos e as APIs da Web estiverem na mesma solução. A orientação a seguir é para o aplicativo BlazorWebAppOidcServer e a API da web MinimalApiJwt da solução modelo, mas a mesma orientação se aplica, em geral, a quaisquer registos baseados em Entra para aplicativos e APIs da web.

Registre a API da Web (MinimalApiJwt) primeiro para que você possa conceder acesso à API da Web ao registrar o aplicativo. O ID do tenant e o ID do cliente da Web API são utilizados para configurar a Web API no seu ficheiro Program. Depois de registar a API da Web, exponha a API da Web em Registos de Aplicações>Expor uma API com o nome de escopo de Weather.Get. Registre o URI da ID do aplicativo para uso na configuração do aplicativo.

Em seguida, registre o aplicativo (BlazorWebAppOidcServer) com uma configuração de plataforma Web e um URI de redirecionamento de https://localhost/signin-oidc (uma porta não é necessária). Os IDs do locatário e do cliente do aplicativo, juntamente com o endereço base da API web, o URI do ID do aplicativo e o nome do escopo meteorológico, são usados para configurar o aplicativo no seu ficheiro Program. Conceda permissão à API para aceder à API da web em Registros de aplicativos>Permissões da API. Se a especificação de segurança do aplicativo exigir, você poderá conceder consentimento de administrador para que a organização acesse a API da Web. Os utilizadores e grupos autorizados são atribuídos ao registo da aplicação em Registos de> aplicaçõesAplicações empresariais.

Na configuração de registo do portal do Entra ou do Azure para "concessão implícita e fluxos híbridos", não marque nenhuma das caixas de seleção do ponto de extremidade de autorização para que retornem tokens de acesso ou tokens de ID. O manipulador OpenID Connect solicita automaticamente os tokens apropriados usando o código retornado do endpoint de autorização.

Crie um segredo do cliente no registro do aplicativo no portal do Entra ou do Azure (Gerenciar>certificados & segredos> do cliente). Mantenha o valor secreto do cliente para uso na próxima seção.

Orientações adicionais de configuração do Entra para configurações específicas são fornecidas mais adiante neste artigo.

Defina o segredo do cliente

Esta seção só se aplica ao projeto de servidor do Blazor Web App (BlazorWebAppOidcServer projeto).

Advertência

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

Para testes de desenvolvimento local, use a ferramenta Secret Manager para armazenar o segredo do cliente do projeto de servidor sob a chave de configuração Blazor.

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

dotnet user-secrets init

Execute o seguinte comando para definir o segredo do cliente. O marcador {SECRET} é o segredo do cliente obtido a partir do registo da aplicação:

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

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

MinimalApiJwt projeto

O projeto MinimalApiJwt é uma API web de back-end para vários projetos de frontend. O projeto configura um endpoint de API Minimal para dados meteorológicos.

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

O projeto inclui pacotes e configuração para produzir documentos OpenAPI e a UI do Swagger no ambiente de desenvolvimento. Para obter mais informações, consulte Usar os documentos OpenAPI gerados.

O projeto cria um endpoint Minimal API para dados meteorológicos:

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

Configure o projeto em JwtBearerOptions da chamada AddJwtBearer no ficheiro Program do projeto.

O Authority define a autoridade para fazer chamadas OIDC. Recomendamos o uso de um registro de aplicativo separado para o MinimalApiJwt projeto. A autoridade confirma o emissor (iss) do JWT retornado pelo provedor de identidade.

jwtOptions.Authority = "{AUTHORITY}";

O formato da Autoridade depende do tipo de inquilino utilizado. Os exemplos a seguir para o Microsoft Entra ID usam uma ID de locatário de aaaabbbb-0000-cccc-1111-dddd2222eeee.

ME-ID exemplo de autoridade do inquilino:

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

Exemplo de Autoridade de locatário B2C do AAD:

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

O Audience define a audiência para qualquer token OIDC recebido.

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

Observação

Ao utilizar o Microsoft Entra ID, corresponda o valor apenas ao caminho do URI da ID da Aplicação configurado ao adicionar o Weather.Get escopo em Expor uma API no portal do Entra ou do Azure. Não inclua o nome do escopo, "Weather.Get," no valor.

O formato do Público depende do tipo de locatário em uso. Os exemplos a seguir para o Microsoft Entra ID usam uma ID de Locatário de contoso e uma ID de Cliente de 11112222-bbbb-3333-cccc-4444dddd5555.

ME-ID exemplo de URI de ID de aplicativo de inquilino:

jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";

Exemplo de URI de ID de aplicação do inquilino B2C do Azure Active Directory:

jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";

BlazorWebAppOidcServer projeto

A atualização automática de tokens não interativos é feita por meio de um atualizador personalizado cookie (CookieOidcRefresher.cs).

A DelegatingHandler (TokenHandler) gerencia a anexação do token de acesso de um usuário às solicitações de saída. O manipulador de token só é executado durante a renderização estática do lado do servidor (SSR estático), portanto, o uso HttpContext é seguro neste cenário. Para obter mais informações, consulte IHttpContextAccessor/HttpContext em aplicativos ASP.NET Core Blazor e ASP.NET Core do lado do servidor e Blazor Web App cenários adicionais de segurança.

TokenHandler.cs:

public class TokenHandler(IHttpContextAccessor httpContextAccessor) : 
    DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (httpContextAccessor.HttpContext is null)
        {
            throw new Exception("HttpContext not available");
        }

        var accessToken = await httpContextAccessor.HttpContext
            .GetTokenAsync("access_token");

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", accessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}

No Program arquivo do projeto, o manipulador de tokens (TokenHandler) é registado como um serviço e definido como o manipulador de mensagens com AddHttpMessageHandler para realizar pedidos seguros para a MinimalApiJwt API web de back-end, utilizando um cliente HTTP nomeado ("ExternalApi").

builder.Services.AddScoped<TokenHandler>();

builder.Services.AddHttpClient("ExternalApi",
      client => client.BaseAddress = new Uri(builder.Configuration["ExternalApiUri"] ?? 
          throw new Exception("Missing base address!")))
      .AddHttpMessageHandler<TokenHandler>();

O componente Weather usa o atributo [Authorize] para impedir o acesso não autorizado. Para obter mais informações sobre como exigir autorização em todo o aplicativo por meio de uma política de autorização de e não exigir autorização em um subconjunto de pontos públicos finais, consulte a orientação OIDC das páginas Razor.

O ExternalApi cliente HTTP é usado para fazer uma solicitação de dados meteorológicos para a API da Web segura. No evento do ciclo de vidaOnInitializedAsync de Weather.razor

using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = ClientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();

forecasts = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
    throw new IOException("No weather forecast!");

No arquivo do projeto appsettings.json, configure o URL da API externa:

"ExternalApiUri": "{BASE ADDRESS}"

Exemplo:

"ExternalApiUri": "https://localhost:7277"

A seguinte configuração OpenIdConnectOptions é encontrada no ficheiro Program do projeto durante a chamada para AddOpenIdConnect:

PushedAuthorizationBehavior: Controla o suporte a solicitações de autorização push (PAR). Por padrão, a configuração é usar PAR se o documento de descoberta do provedor de identidade (geralmente encontrado em .well-known/openid-configuration) anunciar suporte para PAR. Se desejar exigir suporte PAR para o aplicativo, você pode atribuir um valor de PushedAuthorizationBehavior.Require. O PAR não é suportado pelo Microsoft Entra, e não há planos para que o Entra o suporte no futuro.

oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.UseIfAvailable;

SignInScheme: Define o esquema de autenticação correspondente ao middleware responsável pela persistência da identidade do usuário após uma autenticação bem-sucedida. O manipulador OIDC precisa usar um esquema de entrada que seja capaz de persistir as credenciais do usuário nas solicitações. A linha seguinte está presente apenas para fins de demonstração. Se omitido, DefaultSignInScheme é usado como um valor de fallback.

oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

Escopos para openid e profile (Scope) (Opcional): Os escopos openid e profile também são configurados por padrão porque são necessários para que o manipulador OIDC funcione, mas podem precisar ser adicionados novamente se os escopos forem incluídos na configuração Authentication:Schemes:MicrosoftOidc:Scope. Para obter orientações gerais de configuração, consulte Configuração no ASP.NET Core e configuração no ASP.NET Core Blazor.

oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);

Configure o Weather.Get âmbito para aceder à API externa da web para dados meteorológicos. O exemplo a seguir é baseado no uso do Entra ID num domínio de inquilino ME-ID. No exemplo a seguir, o espaço reservado {APP ID URI} é encontrado no portal do Entra ou do Azure onde a API Web está exposta. Para qualquer outro provedor de identidade, use o escopo apropriado.

oidcOptions.Scope.Add("{APP ID URI}/Weather.Get");

O formato do escopo depende do tipo de locatário em uso. Nos exemplos a seguir, o Domínio do Locatário é contoso.onmicrosoft.com, e a ID do Cliente é 11112222-bbbb-3333-cccc-4444dddd5555.

ME-ID exemplo de URI de ID de aplicativo de inquilino:

oidcOptions.Scope.Add("api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");

Exemplo de URI de ID de aplicação do inquilino B2C do Azure Active Directory:

oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");

SaveTokens: Define se os tokens de acesso e atualização devem ser armazenados no AuthenticationProperties após uma autorização bem-sucedida. Essa propriedade é configurada como true para que o token de atualização seja armazenado para uma atualização não interativa.

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

Authority e ClientId: Define a Autoridade e a ID do Cliente para chamadas OIDC.

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

O exemplo a seguir usa uma ID de locatário de aaaabbbb-0000-cccc-1111-dddd2222eeee e uma ID de cliente de 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";

Para aplicações multicliente, a autoridade "common" deve ser usada. Você também pode usar a autoridade "comum" para aplicativos de locatário único, mas é necessário um IssuerValidator personalizado, conforme mostrado mais adiante nesta seção.

oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";

ResponseType: Configura o manipulador OIDC para executar apenas o fluxo de código de autorização. As subvenções implícitas e os fluxos híbridos são desnecessários neste modo. O manipulador OIDC solicita automaticamente os tokens apropriados usando o código retornado do endpoint de autorização.

oidcOptions.ResponseType = OpenIdConnectResponseType.Code;

MapInboundClaims e 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 de declaração 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 Microsoft Entra ID (ME-ID). Consulte a documentação do seu provedor de identidade para obter 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 de caminhos: os caminhos devem corresponder aos caminhos de URI de redirecionamento (caminho de retorno de chamada de login) e redirecionamento após término de sessão (caminho de retorno de chamada após término de sessão) configurados ao registrar a aplicação com o provedor OIDC. No portal do Azure, os caminhos são configurados no bloco Autenticação do registo 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 da aplicação a partir do qual o user-agent é retornado.

Configure o caminho de callback para terminar a sessão no registo do fornecedor OIDC da aplicação. No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost:{PORT}/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 requer a porta correta.

SignedOutCallbackPath (chave de configuração: "SignedOutCallbackPath"): O caminho da solicitação dentro do caminho base da aplicação interceptado pelo manipulador OIDC para onde o agente do utilizador é redirecionado pela primeira vez após terminar sessão no fornecedor de identidade. O aplicativo de exemplo não define um valor para o caminho porque o valor padrão de "/signout-callback-oidc" é usado. Depois de intercetar a solicitação, o manipulador OIDC redireciona para o SignedOutRedirectUri ou RedirectUri, se especificado.

Configure o caminho de callback para terminar a sessão no registo do fornecedor OIDC da aplicação. No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost:{PORT}/signout-callback-oidc

Observação

Ao usar o Microsoft Entra ID, defina o caminho nas entradas de URI de Redirecionamento da configuração da plataforma Web no portal do Entra ou do Azure. Uma porta não é necessária para endereços localhost ao usar o Entra. A maioria dos outros provedores OIDC requer a porta correta. Se não adicionar o URI do caminho de retorno de chamada com a sessão terminada ao registo da aplicação no Entra, o Entra recusa-se a redirecionar o utilizador de volta para a aplicação e simplesmente pede ao utilizador para fechar a janela do navegador.

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

No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost/signout-oidc

Observação

Ao usar o Microsoft Entra ID, defina a URL de logout do Canal Frontal no portal do Entra ou do Azure. Uma porta não é necessária para endereços localhost ao usar o Entra. A maioria dos outros provedores OIDC requer a 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 apenas 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 o ID do inquilino ({TENANT ID}) retornado por https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Para obter mais informações, consulte SecurityTokenInvalidIssuerException com OpenID Connect e o endpoint "comum" do Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

Apenas para aplicações que utilizem o Microsoft Entra ID ou o Azure AD B2C com o endpoint "comum":

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

Para o Microsoft Entra ID ou Azure AD B2C, pode usar AddMicrosoftIdentityWebApp de Microsoft Identity Web, (Microsoft.Identity.Web pacote NuGet, documentação da API), que adiciona tanto os handlers de autenticação OIDC como Cookie com as definições padrão apropriadas. O aplicativo de exemplo e as diretrizes neste artigo não usam o Microsoft Identity Web. As instruções demonstram como configurar o manipulador OIDC manualmente para qualquer provedor OIDC. Para obter mais informações sobre como implementar o Microsoft Identity Web, consulte Proteger um ASP.NET Core Blazor Web App com o Microsoft Entra ID.

Esta versão do artigo aborda a implementação do OIDC com o padrão Backend for Frontend (BFF). Se a especificação do aplicativo não exigir a adoção do padrão BFF, altere o seletor de versão do artigo para padrão não-BFF (automático interativo) (renderização automática interativa) ou padrão não-BFF (servidor interativo) (renderização do servidor interativo).

Pré-requisitos

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

Além disso, consulte a seção Pré-requisitos de Guia de início rápido: crie seu primeiro aplicativo .NET Aspire.

Solução de amostra

O aplicativo de exemplo consiste nos seguintes projetos:

  • .NET Aspire:
    • Aspire.AppHost: Usado para gerir as principais questões de orquestração da aplicação.
    • Aspire.ServiceDefaults: Contém configurações padrão de aplicativos .NET Aspire que podem ser estendidas e personalizadas conforme necessário.
  • MinimalApiJwt: API de back-end da web, contendo um exemplo de Minimal API endpoint para dados meteorológicos.
  • BlazorWebAppOidc: Projeto de servidor do Blazor Web App. O projeto usa YARP para fazer proxy das solicitações para um endpoint de previsão do tempo no projeto de API da Web de backend (MinimalApiJwt) com o access_token armazenado no cookiede autenticação.
  • BlazorWebAppOidc.Client: Projeto cliente do Blazor Web App.

Acesse o exemplo através da pasta da versão mais recente no repositório de exemplos Blazor com o link a seguir. O exemplo está na BlazorWebAppOidcBff pasta para .NET 8 ou posterior.

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

O Blazor Web App usa modo de renderização automática com interatividade global.

O projeto de servidor chama AddAuthenticationStateSerialization para adicionar um provedor de estado de autenticação do lado do servidor que usa PersistentComponentState para transferir 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 é fixo para o tempo de vida do aplicativo WebAssembly.

A classe PersistingAuthenticationStateProvider (PersistingAuthenticationStateProvider.cs) é uma AuthenticationStateProvider do lado do servidor que usa PersistentComponentState para transmitir o estado de autenticação ao cliente, o qual é então mantido durante o tempo de vida da aplicação WebAssembly.

Este aplicativo é um ponto de partida para qualquer fluxo de autenticação OIDC. OIDC é configurado manualmente na app e não depende do Microsoft Entra ID ou dos pacotes Microsoft Identity Web, nem a app de exemplo requer hospedagem no Microsoft Azure. No entanto, o aplicativo de exemplo pode ser usado com o Entra, o Microsoft Identity Web e hospedado no Azure.

Atualização automática de token não interativo com a ajuda de um atualizador cookie personalizado (CookieOidcRefresher.cs).

O padrão Backend for Frontend (BFF) é adotado usando o .NET Aspire para descoberta de serviços e YARP para encaminhar solicitações de proxy para um ponto de extremidade de previsão do tempo na aplicação de back-end.

A API da Web de back-end (MinimalApiJwt) usa a autenticação de portador JWT para validar tokens JWT salvos pelo Blazor Web App no login cookie.

O Aspire melhora a experiência de criação de aplicações nativas da nuvem .NET. Ele fornece um conjunto consistente e opinativo de ferramentas e padrões para criar e executar aplicativos distribuídos.

YARP (Yet Another Reverse Proxy) é uma biblioteca usada para criar um servidor proxy reverso. MapForwarder Program no arquivo do projeto de servidor adiciona encaminhamento direto de solicitações HTTP que correspondem ao padrão especificado para 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 a classe ServerWeatherForecaster para proxy da solicitação de dados meteorológicos com o token de acesso do usuário. IHttpContextAccessor.HttpContext determina se um HttpContext está disponível para uso pelo método GetWeatherForecastAsync. Para obter mais informações, consulte componentes do ASP.NET Core Razor.
  • Quando o componente é renderizado no cliente, o componente usa a implementação de serviço ClientWeatherForecaster, que usa uma HttpClient pré-configurada (no arquivo Program do projeto cliente) para fazer uma chamada de API da Web para o projeto de servidor. Um endpoint da API Minimal (/weather-forecast) definido no arquivo do projeto de servidor Program transforma a solicitação com o token de acesso do usuário para obter os dados meteorológicos.

Para obter mais informações sobre .NET Aspire, consulte Disponibilidade geral do .NET Aspire: Simplificando o desenvolvimento do .NET Cloud-Native (maio, 2024).

Para obter mais informações sobre chamadas de API da Web usando abstrações de serviço em Blazor Web Apps, consulte Chamar uma API da Web de um aplicativo ASP.NET Core Blazor.

Registos da aplicação Microsoft Entra ID

Recomendamos o uso de registros separados para aplicativos e APIs da Web, mesmo quando os aplicativos e as APIs da Web estiverem na mesma solução. A orientação a seguir é para o aplicativo BlazorWebAppOidc e a API da web MinimalApiJwt da solução modelo, mas a mesma orientação se aplica, em geral, a quaisquer registos baseados em Entra para aplicativos e APIs da web.

Registre a API da Web (MinimalApiJwt) primeiro para que você possa conceder acesso à API da Web ao registrar o aplicativo. O ID do tenant e o ID do cliente da Web API são utilizados para configurar a Web API no seu ficheiro Program. Depois de registar a API da Web, exponha a API da Web em Registos de Aplicações>Expor uma API com o nome de escopo de Weather.Get. Registre o URI da ID do aplicativo para uso na configuração do aplicativo.

Em seguida, registre o aplicativo (BlazorWebAppOidc/BlazorWebApOidc.Client) com uma configuração de plataforma Web e um URI de redirecionamento de https://localhost/signin-oidc (uma porta não é necessária). Os IDs do locatário e do cliente do aplicativo, juntamente com o endereço base da API web, o URI do ID do aplicativo e o nome do escopo meteorológico, são usados para configurar o aplicativo no seu ficheiro Program. Conceda permissão à API para aceder à API da web em Registros de aplicativos>Permissões da API. Se a especificação de segurança do aplicativo exigir, você poderá conceder consentimento de administrador para que a organização acesse a API da Web. Os utilizadores e grupos autorizados são atribuídos ao registo da aplicação em Registos de> aplicaçõesAplicações empresariais.

Na configuração de registo do portal do Entra ou do Azure para "concessão implícita e fluxos híbridos", não marque nenhuma das caixas de seleção do ponto de extremidade de autorização para que retornem tokens de acesso ou tokens de ID. O manipulador OpenID Connect solicita automaticamente os tokens apropriados usando o código retornado do endpoint de autorização.

Crie um segredo do cliente no registro do aplicativo no portal do Entra ou do Azure (Gerenciar>certificados & segredos> do cliente). Mantenha o valor secreto do cliente para uso na próxima seção.

Orientações adicionais de configuração do Entra para configurações específicas são fornecidas mais adiante neste artigo.

Defina o segredo do cliente

Esta seção só se aplica ao projeto de servidor do Blazor Web App (BlazorWebAppOidc projeto).

Advertência

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

Para testes de desenvolvimento local, use a ferramenta Secret Manager para armazenar o segredo do cliente do projeto de servidor sob a chave de configuração Blazor.

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

dotnet user-secrets init

Execute o seguinte comando para definir o segredo do cliente. O marcador {SECRET} é o segredo do cliente obtido a partir do registo da aplicação:

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

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

.NET Aspire projetos

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

Confirme se você atendeu aos pré-requisitos para .NET Aspire. Para obter mais informações, consulte a seção Pré-requisitos de Início Rápido: crie a sua primeira aplicação .NET Aspire.

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

MinimalApiJwt projeto

O projeto MinimalApiJwt é uma API web de back-end para vários projetos de frontend. O projeto configura um endpoint de API Minimal para dados meteorológicos. As solicitações do projeto do lado do servidor Blazor Web App (BlazorWebAppOidc) são intermediadas por proxy para o projeto MinimalApiJwt.

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

O projeto inclui pacotes e configuração para produzir documentos OpenAPI e a UI do Swagger no ambiente de desenvolvimento. Para obter mais informações, consulte Usar os documentos OpenAPI gerados.

Um endpoint seguro de dados de previsão do tempo está 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.

Configure o projeto em JwtBearerOptions da chamada AddJwtBearer no ficheiro Program do projeto.

O Authority define a autoridade para fazer chamadas OIDC. Recomendamos o uso de um registro de aplicativo separado para o MinimalApiJwt projeto. A autoridade confirma o emissor (iss) do JWT retornado pelo provedor de identidade.

jwtOptions.Authority = "{AUTHORITY}";

O formato da Autoridade depende do tipo de inquilino utilizado. Os exemplos a seguir para o Microsoft Entra ID usam uma ID de locatário de aaaabbbb-0000-cccc-1111-dddd2222eeee.

ME-ID exemplo de autoridade do inquilino:

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

Exemplo de Autoridade de locatário B2C do AAD:

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

O Audience define a audiência para qualquer token OIDC recebido.

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

Observação

Ao utilizar o Microsoft Entra ID, corresponda o valor apenas ao caminho do URI da ID da Aplicação configurado ao adicionar o Weather.Get escopo em Expor uma API no portal do Entra ou do Azure. Não inclua o nome do escopo, "Weather.Get," no valor.

O formato do Público depende do tipo de locatário em uso. Os exemplos a seguir para o Microsoft Entra ID usam uma ID de Locatário de contoso e uma ID de Cliente de 11112222-bbbb-3333-cccc-4444dddd5555.

ME-ID exemplo de URI de ID de aplicativo de inquilino:

jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";

Exemplo de URI de ID de aplicação do inquilino B2C do Azure Active Directory:

jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";

Projeto Blazor Web App do lado do servidor (BlazorWebAppOidc)

Esta seção explica como configurar o projeto do lado Blazor do servidor.

A seguinte configuração OpenIdConnectOptions é encontrada no arquivo de Program do projeto na chamada para AddOpenIdConnect.

PushedAuthorizationBehavior: Controla o suporte a solicitações de autorização push (PAR). Por padrão, a configuração é usar PAR se o documento de descoberta do provedor de identidade (geralmente encontrado em .well-known/openid-configuration) anunciar suporte para PAR. Se desejar exigir suporte PAR para o aplicativo, você pode atribuir um valor de PushedAuthorizationBehavior.Require. O PAR não é suportado pelo Microsoft Entra, e não há planos para que o Entra o suporte no futuro.

oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.UseIfAvailable;

SignInScheme: Define o esquema de autenticação correspondente ao middleware responsável pela persistência da identidade do usuário após uma autenticação bem-sucedida. O manipulador OIDC precisa usar um esquema de entrada que seja capaz de persistir as credenciais do usuário nas solicitações. A linha seguinte está presente apenas para fins de demonstração. Se omitido, DefaultSignInScheme é usado como um valor de fallback.

oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

Escopos para openid e profile (Scope) (Opcional): Os escopos openid e profile também são configurados por padrão porque são necessários para que o manipulador OIDC funcione, mas podem precisar ser adicionados novamente se os escopos forem incluídos na configuração Authentication:Schemes:MicrosoftOidc:Scope. Para obter orientações gerais de configuração, consulte Configuração no ASP.NET Core e configuração no ASP.NET Core Blazor.

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 as solicitações de dados meteorológicos do projeto de API da 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 da Web (Scope): Configure o Weather.Get escopo para acessar a API da Web externa para dados meteorológicos. No exemplo a seguir, o espaço reservado {APP ID URI} é encontrado no portal do Entra ou do Azure onde a API Web está exposta. Para qualquer outro provedor de identidade, use o escopo apropriado.

oidcOptions.Scope.Add("{APP ID URI}/Weather.Get");

O formato do escopo depende do tipo de locatário em uso. Nos exemplos a seguir, o Domínio do Locatário é contoso.onmicrosoft.com, e a ID do Cliente é 11112222-bbbb-3333-cccc-4444dddd5555.

ME-ID exemplo de URI de ID de aplicativo de inquilino:

oidcOptions.Scope.Add("api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");

Exemplo de URI de ID de aplicação do inquilino B2C do Azure Active Directory:

oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get");

Authority e ClientId: Define a Autoridade e a ID do Cliente para chamadas OIDC.

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

O exemplo a seguir usa uma ID de locatário de aaaabbbb-0000-cccc-1111-dddd2222eeee e uma ID de cliente de 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";

Para aplicações multicliente, a autoridade "common" deve ser usada. Você também pode usar a autoridade "comum" para aplicativos de locatário único, mas é necessário um IssuerValidator personalizado, conforme mostrado mais adiante nesta seção.

oidcOptions.Authority = "https://login.microsoftonline.com/common/v2.0/";

ResponseType: Configura o manipulador OIDC para executar apenas o fluxo de código de autorização. As subvenções implícitas e os fluxos híbridos são desnecessários neste modo. O manipulador OIDC solicita automaticamente os tokens apropriados usando o código retornado do endpoint de autorização.

oidcOptions.ResponseType = OpenIdConnectResponseType.Code;

MapInboundClaims e 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 é definido como false, o manipulador não executa mapeamentos de declarações e os nomes de declaração 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 Microsoft Entra ID (ME-ID). Consulte a documentação do seu provedor de identidade para obter 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 de caminhos: os caminhos devem corresponder aos caminhos de URI de redirecionamento (caminho de retorno de chamada de login) e redirecionamento após término de sessão (caminho de retorno de chamada após término de sessão) configurados ao registrar a aplicação com o provedor OIDC. No portal do Azure, os caminhos são configurados no bloco Autenticação do registo 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.

Configure o caminho de callback para terminar a sessão no registo do fornecedor OIDC da aplicação. No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost:{PORT}/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 requer a porta correta.

SignedOutCallbackPath (chave de configuração: "SignedOutCallbackPath"): O caminho da solicitação dentro do caminho base da aplicação interceptado pelo manipulador OIDC para onde o agente do utilizador é redirecionado pela primeira vez após terminar sessão no fornecedor de identidade. O aplicativo de exemplo não define um valor para o caminho porque o valor padrão de "/signout-callback-oidc" é usado. Depois de intercetar a solicitação, o manipulador OIDC redireciona para o SignedOutRedirectUri ou RedirectUri, se especificado.

Configure o caminho de callback para terminar a sessão no registo do fornecedor OIDC da aplicação. No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost:{PORT}/signout-callback-oidc

Observação

Ao usar o Microsoft Entra ID, defina o caminho nas entradas de URI de Redirecionamento da configuração da plataforma Web no portal do Entra ou do Azure. Uma porta não é necessária para endereços localhost ao usar o Entra. A maioria dos outros provedores OIDC requer a porta correta. Se não adicionar o URI do caminho de retorno de chamada com a sessão terminada ao registo da aplicação no Entra, o Entra recusa-se a redirecionar o utilizador de volta para a aplicação e simplesmente pede ao utilizador para fechar a janela do navegador.

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

No exemplo a seguir, o marcador de posição {PORT} é a porta da app.

https://localhost/signout-oidc

Observação

Ao usar o Microsoft Entra ID, defina a URL de logout do Canal Frontal no portal do Entra ou do Azure. Uma porta não é necessária para endereços localhost ao usar o Entra. A maioria dos outros provedores OIDC requer a 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 apenas 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 o ID do inquilino ({TENANT ID}) retornado por https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration. Para obter mais informações, consulte SecurityTokenInvalidIssuerException com OpenID Connect e o endpoint "comum" do Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

Apenas para aplicações que utilizem o Microsoft Entra ID ou o Azure AD B2C com o endpoint "comum":

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

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

O projeto BlazorWebAppOidc.Client é o projeto do lado 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 é fixo para o tempo de vida do aplicativo WebAssembly.

A classe PersistentAuthenticationStateProvider (PersistentAuthenticationStateProvider.cs) é um AuthenticationStateProvider do lado do cliente que determina o estado de autenticação do utilizador procurando dados persistentes na página quando é renderizada no servidor. O estado de autenticação é fixo para o tempo de vida do aplicativo WebAssembly.

Se o usuário precisar fazer login ou logout, será necessário um recarregamento de página inteira.

O aplicativo de exemplo fornece apenas um nome de usuário e e-mail para fins de exibição.

Serializar apenas as declarações de nome e função

Esta seção só se aplica ao padrão não-BFF (Interactive Auto) e ao padrão BFF (Interactive Auto) e seus aplicativos de exemplo.

No arquivo Program, todas as declarações são serializadas definindo SerializeAllClaims para true. Se você quiser apenas que as declarações de nome e função sejam serializadas para CSR, remova a opção ou defina-a como false.

Configuração de fornecimento com o provedor de configuração JSON (configurações do aplicativo)

Os projetos de solução de exemplo configuram a autenticação de portadores OIDC e JWT nos seus Program arquivos, de modo a tornar as definições de configuração detectáveis usando o preenchimento automático em C#. Os aplicativos profissionais geralmente usam um provedor de configuração para configurar opções OIDC, como o provedor de configuração JSON padrão. O provedor de configuração JSON carrega a configuração dos arquivos de configuração do aplicativo appsettings.json/appsettings.{ENVIRONMENT}.json, em que o espaço reservado {ENVIRONMENT} é o ambiente de tempo de execução do aplicativo. Siga as orientações nesta seção para usar os arquivos de configurações do aplicativo para configuração.

No arquivo de configurações do aplicativo (appsettings.json) do projeto BlazorWebAppOidc ou BlazorWebAppOidcServer, adicione a seguinte configuração JSON:

"Authentication": {
  "Schemes": {
    "MicrosoftOidc": {
      "Authority": "https://login.microsoftonline.com/{TENANT ID (BLAZOR APP)}/v2.0/",
      "ClientId": "{CLIENT ID (BLAZOR APP)}",
      "CallbackPath": "/signin-oidc",
      "SignedOutCallbackPath": "/signout-callback-oidc",
      "RemoteSignOutPath": "/signout-oidc",
      "SignedOutRedirectUri": "/",
      "Scope": [
        "openid",
        "profile",
        "offline_access",
        "{APP ID URI (WEB API)}/Weather.Get"
      ]
    }
  }
},

Atualize os placeholders na configuração anterior para corresponder aos valores que a aplicação usa no arquivo Program.

  • {TENANT ID (BLAZOR APP)}: O Id do inquilino da aplicação Blazor.
  • {CLIENT ID (BLAZOR APP)}: A ID do cliente do Blazor aplicativo.
  • {APP ID URI (WEB API)}: O URI do ID da aplicação da API Web.

A Autoridade "comum" (https://login.microsoftonline.com/common/v2.0/) deve ser usada para aplicativos multilocatário. Para usar a Autoridade "comum" para aplicativos de locatário único, consulte a seção Usar a Autoridade "comum" para aplicativos de locatário único .

Atualize quaisquer outros valores na configuração anterior para corresponder aos valores personalizados/não padrão usados no Program arquivo.

A configuração é captada automaticamente pelo construtor de autenticação.

Remova as seguintes linhas do Program arquivo:

- oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile);
- oidcOptions.Scope.Add("...");
- oidcOptions.CallbackPath = new PathString("...");
- oidcOptions.SignedOutCallbackPath = new PathString("...");
- oidcOptions.RemoteSignOutPath = new PathString("...");
- oidcOptions.Authority = "...";
- oidcOptions.ClientId = "...";

No método ConfigureCookieOidc de CookieOidcServiceCollectionExtensions.cs, remova a seguinte linha:

- oidcOptions.Scope.Add(OpenIdConnectScope.OfflineAccess);

No projeto MinimalApiJwt, adicione a seguinte configuração de definições da aplicação ao ficheiro appsettings.json.

"Authentication": {
  "Schemes": {
    "Bearer": {
      "Authority": "https://sts.windows.net/{TENANT ID (WEB API)}/",
      "ValidAudiences": [ "{APP ID URI (WEB API)}" ]
    }
  }
},

Atualize os placeholders na configuração anterior para corresponder aos valores que a aplicação usa no arquivo Program.

  • {TENANT ID (WEB API)}: O ID do inquilino da API web.
  • {APP ID URI (WEB API)}: O URI do ID da aplicação da API Web.

Os formatos das autoridades adotam os seguintes padrões:

  • ME-ID tipo de inquilino: https://sts.windows.net/{TENANT ID}/
  • Tipo de inquilino B2C: https://login.microsoftonline.com/{TENANT ID}/v2.0/

Os formatos de audiência adotam os seguintes padrões ({CLIENT ID} é o ID do cliente da API da Web; {DIRECTORY NAME} é o nome do diretório, por exemplo, contoso):

  • ME-ID tipo de inquilino: api://{CLIENT ID}
  • Tipo de inquilino B2C: https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}

A configuração é reconhecida automaticamente pelo gerador de autenticação de portador JWT.

Remova as seguintes linhas do Program arquivo:

- jwtOptions.Authority = "...";
- jwtOptions.Audience = "...";

Para obter mais informações sobre configuração, consulte os seguintes recursos:

Usar a Autoridade "comum" para aplicativos de locatário único

Você pode usar a Autoridade "comum" para aplicações de um único inquilino, mas deve cumprir as etapas seguintes para implementar um validador de emissor personalizado.

Adicione o Microsoft.IdentityModel.Validators pacote NuGet ao BlazorWebAppOidc, BlazorWebAppOidcServerou BlazorWebAppOidcBff projeto.

Observação

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

Na parte superior do Program arquivo, disponibilize o Microsoft.IdentityModel.Validators namespace:

using Microsoft.IdentityModel.Validators;

Use o seguinte código no arquivo onde as Program opções OIDC estão configuradas:

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

Para obter mais informações, consulte SecurityTokenInvalidIssuerException com OpenID Connect e o endpoint "comum" do Azure AD (AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet #1731).

Redirecionar para a página inicial ao sair

O componente LogInOrOut (Layout/LogInOrOut.razor) define um campo oculto para o URL de retorno (ReturnUrl) para o URL atual (currentURL). Quando o usuário sai do aplicativo, o provedor de identidade retorna o usuário para a página da qual ele fez logout. Se o usuário fizer logout de uma página segura, ele será retornado à mesma página segura e enviado de volta através do processo de autenticação. Esse fluxo de autenticação é razoável quando os usuários precisam alterar contas regularmente.

Como alternativa, use o seguinte componente LogInOrOut, que não fornece uma URL de retorno ao fazer logout.

Layout/LogInOrOut.razor:

<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
                </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>

Atualização de token

A implementação personalizada de atualização cookie (CookieOidcRefresher.cs) atualiza automaticamente as reivindicações de utilizador quando expiram. A implementação atual espera receber um token de identificação do endpoint do token em troca do token de atualização. As declarações nesse token de ID são utilizadas para sobrescrever as declarações do usuário.

A implementação de exemplo não inclui código para solicitar reivindicações do endpoint UserInfo durante a atualização do token. Para obter mais informações, consulte BlazorWebAppOidc AddOpenIdConnect with GetClaimsFromUserInfoEndpoint = true doesn't propogate [sic] role claims to client (dotnet/aspnetcore #58826).

Observação

Alguns provedores de identidade só retornam um token de acesso ao usar um token de atualização. O CookieOidcRefresher pode ser atualizado com lógica adicional para continuar a usar o conjunto anterior de declarações armazenadas no sistema de autenticação cookie ou para utilizar o token de acesso, a fim de solicitar declarações do UserInfo endpoint.

Nonce criptográfico

Um nonce é um valor alfanumérico que associa a sessão de um cliente a um token de identificação para mitigar ataques de repetição.

Se receber um erro nonce durante o desenvolvimento e teste de autenticação, utilize uma nova sessão do navegador em modo de navegação privada/anónima para cada realização de teste, independentemente do quão pequena seja a alteração feita na aplicação ou no utilizador de teste, pois dados cookie obsoletos podem originar um erro nonce. Para obter mais informações, consulte a secção Cookies e dados do site.

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

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

Esta seção diz respeito a aplicativos que não usam Microsoft Entra ID (ME-ID) como provedor de identidade. Para aplicações registadas com ME-ID, consulte a secção Funções de aplicação para aplicações registadas no 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 identidade OIDC, o tipo de declaração de função é role. Verifique a documentação do seu provedor de identidade 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 a autorização baseada em função e política . As funções do aplicativo aparecem em role declarações, uma declaração por função.

Funções de aplicação para aplicações registadas no Microsoft Entra (ME-ID)

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

A abordagem descrita nesta seção configura ME-ID para enviar grupos e funções no cabeçalho de autenticação do cookie. Quando os usuários são apenas membros de alguns grupos e funções de segurança, a abordagem a seguir deve funcionar para a maioria das plataformas de hospedagem sem enfrentar 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 orientações nesta seção em favor da implementação do Microsoft Graph para obter, de forma separada, grupos e funções de um usuário de ME-ID, uma abordagem que não aumenta o tamanho do cabeçalho de 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 a usuários e receber declarações de função para usuários com uma conta padrão do Azure. As orientações nesta seção não exigem uma conta ME-ID Premium.

Ao trabalhar com o diretório padrão, siga as orientações em Adicionar funções à sua aplicação e recebê-las no token (documentação 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 atribuição de funções (documentação do ME-ID).

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

No manifesto do aplicativo no portal do Azure, defina o atributo groupMembershipClaims como All. Um valor de All resulta que ME-ID envia todos os grupos de segurança/distribuição (reivindicaçõesgroups) e funções (reivindicaçõeswids) do utilizador com sessão iniciada. Para definir o atributo groupMembershipClaims:

  1. Abra o registro do aplicativo no portal do Azure.
  2. Selecione Gerir>Manifesto na barra lateral.
  3. Encontre 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ções e em políticas:

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

Solução de problemas

Registo

O aplicativo de servidor é um aplicativo ASP.NET Core padrão. Consulte as diretrizes de registo do ASP.NET Core para habilitar um nível de registo mais baixo na aplicação de servidor.

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

Erros comuns

  • O depurador interrompe numa exceção durante a finalização de sessão com o Microsoft Entra ID Externa

    A seguinte exceção interrompe o depurador do Visual Studio durante o logout com a Microsoft Entra External ID:

    Uncaught TypeError TypeError: Failed to execute 'postMessage' on 'Window': The provided value cannot be converted to a sequence.

    Interrupção do depurador do Visual Studio devido a uma exceção JavaScript durante o logout

    A exceção é lançada pelo código JavaScript do Entra, portanto não representa um problema com o ASP.NET Core. A exceção não afeta a funcionalidade do aplicativo na produção, portanto, a exceção pode ser ignorada durante os testes de desenvolvimento local.

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

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

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

    A cobertura de configuração neste artigo mostra exemplos da configuração correta. Verifique cuidadosamente a configuração procurando por configuração incorreta de aplicativo e IP.

    Se a configuração parecer correta:

    • Analise os logs do aplicativo.

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

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

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

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

  • Cliente não autorizado para ME-ID

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

    Erro ao retornar a chamada de login 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

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

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

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

  • Configurar um navegador
    • Utilize um navegador para testar se consegue configurar para excluir todos os dados cookie e os dados do site quando fechar o navegador.
    • Verifique se o navegador está fechado manualmente ou pelo IDE para qualquer alteração na configuração do aplicativo, do usuário de teste ou do provedor.
  • Use um comando personalizado para abrir um navegador no modo InPrivate ou de navegação anônima no Visual Studio:
    • Abra a caixa de diálogo 'Procurar com' a partir do botão 'Executar' do Visual Studio.
    • Selecione o botão Adicionar.
    • Forneça o caminho para o seu navegador no campo Programa. Os seguintes caminhos executáveis são locais de instalação típicos para o Windows 10. Se o seu navegador estiver instalado em um local diferente ou você não estiver usando o Windows 10, forneça o caminho para o executável do navegador.
      • Microsoft Edge: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox: C:\Program Files\Mozilla Firefox\firefox.exe
    • No campo Argumentos, forneça a opção de linha de comando que o navegador usa para abrir no modo InPrivate ou no modo de navegação anónima. Alguns navegadores exigem o URL do aplicativo.
      • Microsoft Edge: Utilize -inprivate.
      • Google Chrome: use --incognito --new-window {URL}, onde o espaço reservado {URL} é o URL a ser aberto (por exemplo, https://localhost:5001).
      • Mozilla Firefox: Utilize -private -url {URL}, onde o espaço reservado {URL} é o URL a ser aberto (por exemplo, https://localhost:5001).
    • Forneça um nome no campo Nome amigável. Por exemplo, Firefox Auth Testing.
    • Selecione o botão OK.
    • Para evitar ter de selecionar o perfil do navegador para cada iteração de teste com uma aplicação, defina o perfil como predefinido com o botão Definir como Padrão.
    • Certifique-se de que o navegador está fechado pelo IDE para qualquer alteração na configuração do aplicativo, usuário de teste ou provedor.

Atualizações de aplicativos

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

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

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, utilize a Galeria NuGet.

Inicie a solução a partir do projeto correto

Blazor Web Apps:

  • Para um dos exemplos de padrão Backend-for-Frontend (BFF), inicie a solução a partir do projeto Aspire/Aspire.AppHost.
  • Para um dos exemplos de padrão não-BFF, inicie a solução a partir do projeto de servidor.

Blazor Server:

Inicie a solução a partir 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.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