Compartilhar via


Configurar a autenticação da Web (UI) do OpenID Connect no ASP.NET Core

Por Damien Bowden

Exibir ou baixar o código de exemplo

Esse artigo aborda os seguintes tópicos:

  • O que é um cliente interativo confidencial do OpenID Connect
  • Criar um cliente OpenID Connect no ASP.NET Core
  • Exemplos de cliente OpenID Connect com trechos de código
  • Usando clientes de provedores OpenID Connect de terceiros
  • Arquitetura de segurança de back-end para front-end (BFF)
  • Recursos avançados, padrões, estendendo o cliente OpenID Connect

Para obter uma experiência alternativa usando a Biblioteca de Autenticação da Microsoft para .NET, Microsoft Identity Webe Microsoft Entra ID, consulte Início Rápido: Fazer login de usuários e chamar a API do Microsoft Graph de um aplicativo Web ASP.NET Core (documentação do Azure).

Para ver um exemplo usando o servidor OIDC do Microsoft Entra External ID, consulte Fazer login de usuários para um aplicativo Web ASP.NET Core de exemplo em um locatário externo e Um aplicativo Web ASP.NET Core autenticando usuários no Microsoft Entra External ID usando Microsoft Identity Web.

O que é um cliente interativo confidencial do OpenID Connect

O OpenID Connect pode ser usado para implementar a autenticação em aplicativos ASP.NET Core. A maneira recomendada é usar um cliente confidencial do OpenID Connect usando o fluxo de código. O uso da chave de prova para troca de código por clientes públicos OAuth (PKCE) é recomendado para essa implementação. O cliente do aplicativo e o usuário do aplicativo são autenticados no fluxo confidencial. O cliente do aplicativo usa um segredo do cliente ou uma declaração do cliente para autenticar.

Os clientes OpenID Connect/OAuth públicos não são mais recomendados para aplicativos Web.

O fluxo padrão funciona conforme mostrado no diagrama a seguir:

Cliente confidencial de fluxo de código OIDC usando PKCE

O OpenID Connect vem em muitas variações e todas as implementações de servidor têm parâmetros e requisitos ligeiramente diferentes. Alguns servidores não dão suporte ao ponto de extremidade de informações do usuário, alguns ainda não dão suporte ao PKCE e outros exigem parâmetros especiais na solicitação de token. As asserções do cliente podem ser usadas em vez de segredos do cliente. Também existem novos padrões que adicionam segurança extra ao OpenID Connect Core, por exemplo, FAPI, CIBA ou DPoP para APIs downstream.

Observação

No .NET 9, o OAuth 2.0 Push Authorization Requests (PAR) RFC 9126 é usado por padrão, se o servidor OpenID Connect der suporte a isso. Este é um fluxo de três etapas e não um fluxo de duas etapas, como mostrado acima. (A solicitação de informações do usuário é uma etapa opcional.)

Criar um cliente de fluxo de código do Open ID Connect usando o Razor Pages

A seção a seguir mostra como implementar um cliente OpenID Connect em um projeto de página ASP.NET Core Razor vazio. A mesma lógica pode ser aplicada a qualquer projeto Web ASP.NET Core com apenas a integração da interface do usuário sendo diferente.

Adicionar suporte ao OpenID Connect

Adicione os pacotes nuget Microsoft.AspNetCore.Authentication.OpenIdConnect ao projeto ASP.NET Core.

Configurar o cliente OpenID Connect

Adicione a autenticação ao aplicativo web usando builder.Services no arquivo Program.cs. A configuração depende do servidor OpenID Connect. Cada servidor OpenID Connect requer pequenas diferenças na configuração.

O manipulador OpenID Connect é usado para desafios e saída. O cookie é usado para manipular a sessão no aplicativo Web. Os esquemas padrão para a autenticação podem ser especificados conforme necessário.

Para obter mais informações, confira as diretrizes do ASP.NET Coreauthentication-handler.

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
    var oidcConfig = builder.Configuration.GetSection("OpenIDConnectSettings");

    options.Authority = oidcConfig["Authority"];
    options.ClientId = oidcConfig["ClientId"];
    options.ClientSecret = oidcConfig["ClientSecret"];

    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.ResponseType = OpenIdConnectResponseType.Code;

    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.MapInboundClaims = false;
    options.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
    options.TokenValidationParameters.RoleClaimType = "roles";
});

Para obter detalhes sobre as diferentes opções do OpenID Connect, consulte Proteger um ASP.NET Core Blazor Web App com OpenID Connect (OIDC).

Para obter as diferentes possibilidades de mapeamento de declarações, consulte Mapeamento, personalização e transformação de declarações no ASP.NET Core.

Observação

Os seguintes namespaces são necessários:

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;

Configurar as propriedades de configuração

Adicione as configurações do cliente OpenID Connect às propriedades de configuração do aplicativo. As configurações devem corresponder à configuração do cliente no servidor OpenID Connect. Nenhum segredo deve persistir nas configurações do aplicativo onde possa ser verificado acidentalmente. Os segredos devem ser armazenados em um local seguro, como o Azure Key Vault em ambientes de produção ou em segredos do usuário em um ambiente de desenvolvimento. Para obter mais informações, confira Armazenamento seguro de segredos do aplicativo em desenvolvimento no ASP.NET Core.

"OpenIDConnectSettings": {
  // OpenID Connect URL. (The base URL for the /.well-known/openid-configuration)
  "Authority": "<Authority>",
  // client ID from the OpenID Connect server
  "ClientId": "<Client ID>",
  //"ClientSecret": "--stored-in-user-secrets-or-key-vault--"
},

Configuração do caminho de callback pós-desconexão

O SignedOutCallbackPath (chave de configuração: "SignedOutCallbackPath") é o caminho de solicitação dentro do caminho base do aplicativo interceptado pelo manipulador do OpenID Connect em que o agente do usuário é retornado pela primeira vez após sair do provedor 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 interceptar a solicitação, o manipulador do OpenID Connect redireciona para o SignedOutRedirectUri ou RedirectUri, se especificado.

Configure o caminho de retorno de logout no registro do provedor OIDC do aplicativo. No exemplo a seguir, o placeholder {PORT} é a porta do app:

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

Observação

Ao usar o Microsoft Entra ID, defina o caminho nas entradas URI de Redirecionamento da configuração de plataforma da 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 exige a porta correta. Se você não adicionar o URI de caminho de retorno de chamada após desconexão ao registro do aplicativo no Entra, o Entra se recusará a redirecionar o usuário de volta para o aplicativo e apenas solicitará que ele feche a janela do navegador.

Atualize o método de pipeline ASP.NET Core na classe de programa.

O método UseRouting deve ser implementado antes do método UseAuthorization.

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();
app.UseAuthentication();
// Authorization is applied for middleware after the UseAuthorization method
app.UseAuthorization();
app.MapRazorPages();

Autorização forçada

Adicione o atributo [Authorize] às páginas de Razor protegidas:

[Authorize]

Uma abordagem melhor é forçar a autorização para todo o aplicativo e abrir exceções para páginas não seguras.

var requireAuthPolicy = new AuthorizationPolicyBuilder()
    .RequireAuthenticatedUser()
    .Build();

builder.Services.AddAuthorizationBuilder()
    .SetFallbackPolicy(requireAuthPolicy);

Opte pela não autorização em pontos de extremidade públicos aplicando o [AllowAnonymous] atributo aos pontos de extremidade públicos. Para obter exemplos, consulte as seções Adicionar uma nova Logout.cshtml e as páginas SignedOut.cshtmlRazor ao projeto e implementar a página Login.

Adicionar novas páginas Logout.cshtml e SignedOut.cshtmlRazor ao projeto

Para sair de ambas as sessões, cookie e OpenID Connect, é necessário fazer logout. Todo o aplicativo precisa redirecionar para o servidor OpenID Connect para fazer logout. Após um logout bem-sucedido, o aplicativo abre a rota RedirectUri.

Implemente uma página de saída padrão e altere o código da página razor Logout para o seguinte:

[Authorize]
public class LogoutModel : PageModel
{
    public IActionResult OnGetAsync()
    {
        return SignOut(new AuthenticationProperties
        {
            RedirectUri = "/SignedOut"
        },
        // Clear auth cookie
        CookieAuthenticationDefaults.AuthenticationScheme,
        // Redirect to OIDC provider signout endpoint
        OpenIdConnectDefaults.AuthenticationScheme);
    }
}

O SignedOut.cshtml requer o atributo [AllowAnonymous]:

[AllowAnonymous]
public class SignedOutModel : PageModel
{
    public void OnGet()
    {
    }
}

Implementar página Login

Uma página LoginRazor também pode ser implementada para chamar diretamente o ChallengeAsync com o AuthProperties necessário. Isso não será necessário se o aplicativo Web exigir autenticação e o desafio padrão for usado.

A página Login.cshtml requer o atributo [AllowAnonymous]:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPageOidc.Pages;

[AllowAnonymous]
public class LoginModel : PageModel
{
    [BindProperty(SupportsGet = true)]
    public string? ReturnUrl { get; set; }

    public async Task OnGetAsync()
    {
        var properties = GetAuthProperties(ReturnUrl);
        await HttpContext.ChallengeAsync(properties);
    }

    private static AuthenticationProperties GetAuthProperties(string? returnUrl)
    {
        const string pathBase = "/";

        // Prevent open redirects.
        if (string.IsNullOrEmpty(returnUrl))
        {
            returnUrl = pathBase;
        }
        else if (!Uri.IsWellFormedUriString(returnUrl, UriKind.Relative))
        {
            returnUrl = new Uri(returnUrl, UriKind.Absolute).PathAndQuery;
        }
        else if (returnUrl[0] != '/')
        {
            returnUrl = $"{pathBase}{returnUrl}";
        }

        return new AuthenticationProperties { RedirectUri = returnUrl };
    }
}

Adicionar um botão de login e logoff para o usuário

@if (Context.User.Identity!.IsAuthenticated)
{
	<li class="nav-item">
		<a class="nav-link text-dark" asp-area="" asp-page="/Logout">Logout</a>
	</li>

	<span class="nav-link text-dark">Hi @Context.User.Identity.Name</span>
}
else
{
	<li class="nav-item">
		<a class="nav-link text-dark" asp-area="" asp-page="/Index">Login</a>
	</li>
}

Exemplos com trechos de código

Exemplo usando o ponto de extremidade de informações do usuário

As opções do OpenID Connect podem ser usadas para mapear declarações, implementar manipuladores ou até mesmo salvar os tokens na sessão para uso posterior.

A opção Scope pode ser usada para solicitar declarações diferentes ou um token de atualização que é enviado como informações para o servidor OpenID Connect. Solicitar o offline_access está solicitando que o servidor retorne um token de referência que pode ser usado para atualizar a sessão sem autenticar o usuário do aplicativo novamente.

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    var oidcConfig = builder.Configuration.GetSection("OpenIDConnectSettings");
    options.Authority = oidcConfig["IdentityProviderUrl"];
    options.ClientSecret = oidcConfig["ClientSecret"];
    options.ClientId = oidcConfig["Audience"];
    options.ResponseType = OpenIdConnectResponseType.Code;

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");
    options.Scope.Add("offline_access");

    options.ClaimActions.Remove("amr");
    options.ClaimActions.MapUniqueJsonKey("website", "website");

    options.GetClaimsFromUserInfoEndpoint = true;
    options.SaveTokens = true;

    // .NET 9 feature
    options.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Require;

    options.TokenValidationParameters.NameClaimType = "name";
    options.TokenValidationParameters.RoleClaimType = "role";
});

Implementando provedores de identidade da Microsoft

A Microsoft tem vários provedores de identidade e implementações do OpenID Connect. A Microsoft tem diferentes servidores OpenID Connect:

  • Microsoft Entra ID
  • ID externa do Microsoft Entra
  • Azure AD B2C

Se a autenticação estiver usando um dos provedores de identidade da Microsoft no ASP.NET Core, é recomendável usar os pacotes nuget Microsoft.Identity.Web.

Os pacotes Microsoft.Identity.Web Nuget são clientes específicos da Microsoft construídos com base no cliente ASP.NET Core OpenID Connect, com algumas modificações no cliente padrão.

Usando clientes de provedores OpenID Connect de terceiros

Muitas implementações de servidor OpenID Connect criam pacotes Nuget que são otimizados para a mesma implementação do OpenID Connect. Esses pacotes implementam as especificações do cliente OpenID Connect com os extras exigidos pelo servidor OpenID Connect específico. Microsoft.Identity.Web é um exemplo disso.

Se estiver implementando vários clientes do OpenID Connect de diferentes servidores OpenID Connect em um único aplicativo, normalmente é melhor reverter para a implementação padrão do ASP.NET Core, pois os diferentes clientes substituem algumas opções que afetam os outros clientes.

Os provedores da Web OpenIddict são uma implementação de cliente que suporta muitas implementações de servidor diferentes.

IdentityModel é uma biblioteca auxiliar padrão do .NET para identidade baseada em declarações, OAuth 2.0 e OpenID Connect. Isso também pode ser usado para ajudar na implementação do cliente.

Arquitetura de segurança de back-end para front-end (BFF)

Não é mais recomendável implementar clientes públicos do OpenID Connect para aplicativos Web.

Para obter mais informações, consulte o rascunho OAuth 2.0 para aplicativos baseados em navegador.

Ao implementar aplicativos Web que não tenham back-end independente, recomendamos usar a arquitetura de segurança do padrão BFF (Back-end para Front-end). Esse padrão pode ser implementado de maneiras diferentes, mas a autenticação sempre é implementada no back-end e nenhum dado confidencial é enviado ao cliente Web para mais fluxos de autorização ou autenticação.

Recursos avançados, padrões, estendendo o cliente OIDC

Registro em log

A depuração de clientes do OpenID Connect pode ser difícil. Os dados de informações de identificação pessoal (PII) não são registrados por padrão. Se estiver depurando no modo de desenvolvimento, o IdentityModelEventSource.ShowPII poderá ser usado para registrar dados pessoais confidenciais. Não implante um aplicativo com IdentityModelEventSource.ShowPII em servidores produtivos.

//using ...

using Microsoft.IdentityModel.Logging;

var builder = WebApplication.CreateBuilder(args);

//... code 

var app = builder.Build();

IdentityModelEventSource.ShowPII = true;

//... code 

app.Run();

Para obter mais informações, veja Registro em log.

Observação

Talvez você queira diminuir o nível de log configurado para ver todos os logs necessários.

Personalização do parâmetro OIDC e OAuth

A opção de manipuladores de autenticação OAuth e OIDC (AdditionalAuthorizationParameters) permite a personalização dos parâmetros de mensagem de autorização que geralmente são incluídos como parte da cadeia de caracteres de consulta de redirecionamento.

Mapear declarações do OpenID Connect

Para obter mais informações, consulte Mapeamento, personalização e transformação de declarações em ASP.NET Core.

Blazor Conexão OpenID

Para obter mais informações, consulte Proteger um aplicativo Web ASP.NET Core Blazor Web App com OpenID Connect (OIDC).

Padrões