.NET 7 - Autenticação e Autorização : novidades


 Hoje vou apresentar as novidades do .NET 7.0 relacionadas com a autenticação e autorização.

O lançamento do .NET 7 dá continuidade ao esforço de simplificação iniciado com o .NET 5 fornecendo alguns recursos relacionados à autenticação e autorização que facilitam um pouco a vida dos desenvolvedores .NET.

Vamos dar uma olhada rápida nesses recursos, que variam de simplificações na configuração de autenticação à adição de novas ferramentas de teste de autorização, a melhorias no suporte à autenticação do Blazor.

Esquema de autenticação padrão

Ao configurar a autenticação para sua aplicação, você precisa registrar o serviço de autenticação por meio de AddAuthentication(). Por exemplo, o código a seguir é necessário para configurar o JwtBearer como o esquema de autenticação em uma API da Web ASP.NET Core:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
  {
    options.Authority =
$"https://{builder.Configuration["Auth0:Domain"]}";
     options.TokenValidationParameters =
     
new Microsoft.IdentityModel.Tokens.TokenValidationParameters
      {
         ValidAudience = builder.Configuration[
"Auth0:Audience"],
          ValidIssuer =
$"{builder.Configuration["Auth0:Domain"]}"
      };
});

Embora você esteja definindo apenas um esquema de autenticação (JwtBearerDefaults.AuthenticationScheme), o método AddAuthentication() requer que você especifique o esquema padrão a ser usado quando não for especificado em seus endpopints da API.

A partir do .NET 7, o esquema padrão não é mais necessário quando você define apenas um esquema de autenticação pois agora ele é inferido automaticamente pelo framework. Na prática, você pode escrever o código anterior da seguinte forma:

builder.Services.AddAuthentication()
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.Authority =
$"https://{builder.Configuration["Auth0:Domain"]}";
        options.TokenValidationParameters =
         
new Microsoft.IdentityModel.Tokens.TokenValidationParameters
          {
             ValidAudience = builder.Configuration[
"Auth0:Audience"],
             ValidIssuer =
$"{builder.Configuration["Auth0:Domain"]}"
          };
});

Caso precise restaurar o comportamento antigo por qualquer motivo, você pode desativar o novo recurso usando a seguinte instrução:


  AppContext.SetSwitch(
"Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", true);
 

Configuração Simplificada

Uma das críticas recorrentes à autenticação e autorização na plataforma .NET é sua complexidade. Na verdade, o .NET fornece aos desenvolvedores um sistema articulado para gerenciamento de autenticação e autorização. Este sistema é ótimo pela flexibilidade que oferece, mas pode ser difícil para um iniciante digerir todos os detalhes.

Para contornar esse problema a  versão .NET 7 traz uma abordagem simplificada para configurar a autorização de APIs da Web ASP.NET Core com base em tokens de acesso no formato JWT.

Se você já protegeu uma API da Web ASP.NET Core com Auth0, o código a seguir deve parecer familiar para você:

using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
     .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
     {
         options.Authority = $"https://{builder.Configuration["Auth0:Domain"]}";
         options.TokenValidationParameters =
           new Microsoft.IdentityModel.Tokens.TokenValidationParameters
         {
             ValidAudience = builder.Configuration["Auth0:Audience"],
             ValidIssuer = $"{builder.Configuration["Auth0:Domain"]}"
         };
     });

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
...

Agora, você pode confiar diretamente no sistema de configuração integrado para definir as opções de autorização de sua API da Web. Na verdade, além dos esquemas de autenticação padrão, o .NET 7 carrega automaticamente as opções para configurar o serviço de autenticação da nova seção Authentication do arquivo de configuração appsettings.json.

Com esta nova funcionalidade, o código mostrado anteriormente passa a ser o seguinte:

using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

//novo recurso

builder.Services.AddAuthentication().AddJwtBearer();

builder.Services.AddAuthorization();

var app = builder.Build();
 

Todas as definições de configuração são movidas para o arquivo appsetting.json da seguinte maneira:

{
 
"Logging": {
   
"LogLevel": {
      
"Default": "Information",
         
"Microsoft.AspNetCore": "Warning"
   }
},
"AllowedHosts": "*",
//nova seção
 
"Authentication": {
    
"Schemes": {
       
"Bearer": {
          
"Authority": "https://YOUR_AUTH0_DOMAIN",
          
"ValidAudiences": [ "YOUR_AUDIENCE" ],
          
"ValidIssuer": "YOUR_AUTH0_DOMAIN"
        }
     }
  }
}
 

A nova seção Authentication mantém as definições de configuração para qualquer esquema de autenticação suportado por seu aplicativo, embora atualmente apenas o esquema JwtBearer seja suportado.

Observe que agora você não precisa mais chamar UseAuthentication() e UseAuthorization(). O framework cuida de adicionar automaticamente o middleware necessário ao pipeline de solicitação.

Políticas de autorização para endpoints específicos

A ASP.NET Core permite que você especifique políticas para tomar decisões de autorização mais precisas com base no conteúdo do token de acesso ou em outros critérios avançados. Normalmente, essas políticas são definidas globalmente no nível do aplicativo e anexadas às definições de terminal às quais a política se aplica.

Definir políticas globalmente é muito bom para reutilização. No entanto, às vezes você pode precisar apenas de uma política específica para um endpoint. Nesse caso, você pode especificar sua política diretamente na definição do endpoint.

Veja a seguir um exemplo de como você pode usar esse novo recurso:


  app.MapGet(
"/api/special-endpoint", () => "A special endpoint!")
    .RequireAuthorization(p => p.RequireClaim(
"scope", "api:special"));

 

A ferramenta user-jwts

Testar uma API da Web protegida por token pode ser complexo. Você pode facilmente usar ferramentas como curl ou Postman, mas precisa passar um token de acesso válido junto com sua solicitação HTTP. Para obter um token de acesso válido, você geralmente precisa de um sistema de gerenciamento de identidade e acesso e precisar gerar o token. Você precisa configurar sua API com este sistema e fazer login fornecendo credenciais válidas.

O .NET 7 traz uma nova ferramenta CLI  - user-jwts  - que ajuda a simplificar o teste de API da Web protegido.

Esta ferramenta esta integrada ao .NET CLI e permite gerar tokens de acesso no formato JWT. A ferramenta é baseada na mesma infraestrutura do gerenciador de segredos e permite que seu aplicativo valide o token de acesso gerado. Basicamente, o seguinte comando permite gerar um novo token de acesso:


 
dotnet user-jwts create     
 

Você pode usar esse token com curl, Postman ou qualquer outro cliente HTTP para fazer sua chamada para os endpoints protegidos de sua API da Web sem a necessidade de registrar seu aplicativo com um sistema real de gerenciamento de identidade e acesso.

Além do uso básico, a ferramenta user-jwts permite que você personalize seu token de acesso especificando os escopos, declarações personalizadas e outras propriedades para que você também possa testar endpoints com políticas de acesso.

Requests de autenticação dinâmica no Blazor WASM

O Blazor WebAssembly usa o pacote Microsoft.AspNetCore.Components.WebAssembly.Authentication para manipular a autenticação OpenID Connect. Este pacote permite que você defina estaticamente no momento da inicialização como a autenticação ocorrerá. No entanto, há cenários em que pode ser necessário autenticar usuários em tempo de execução com uma configuração diferente. Por exemplo, você pode precisar executar autenticação avançada ou pode querer que o usuário altere sua conta.

No .NET 7, o Blazor WebAssembly dá suporte à criação de solicitações de autenticação dinâmica usando a nova classe InteractiveRequestOptions e o método NavigateToLogin() do objeto NavigationManager.

O exemplo a seguir mostra o uso básico desses novos recursos:

InteractiveRequestOptions requestOptions = new()
{
   Interaction = InteractionType.SignIn,
   ReturnUrl = NavigationManager.Uri,
};

NavigationManager.NavigateToLogin(
"authentication/login", requestOptions);
 

A classe InteractiveRequestOptions também fornece uma maneira de adicionar parâmetros personalizados à solicitação de autenticação. Por exemplo, considere o parâmetro screen_hint de Auth0 que permite que você mostre o formulário de inscrição em vez do formulário de login padrão. Você pode enviar este parâmetro para Auth0 conforme mostrado abaixo:

InteractiveRequestOptions requestOptions = new()
{
   Interaction = InteractionType.SignIn,
   ReturnUrl = NavigationManager.Uri,
};
requestOptions.TryAddAdditionalParameter(
"screen_hint", "signup");

NavigationManager.NavigateToLogin(
"authentication/login", requestOptions);
 

Blazor WASM Authentication Diagnostics

Detectar problemas de autenticação não é fácil, especialmente se você estiver executando um código WebAssembly, como acontece com os aplicativos Blazor WebAssembly.

A versão .NET 7 tenta melhorar seu suporte para diagnóstico de autenticação introduzindo o nível de depuração para a biblioteca Microsoft.AspNetCore.Components.WebAssembly.Authentication. Depois de habilitar o log para Blazor WebAssembly, você pode adicionar a seguinte configuração no arquivo de configuração wwwroot/appsettings.json para obter informações detalhadas sobre o processo de autenticação:

"Logging": {
   
"LogLevel": {
     
"Microsoft.AspNetCore.Components.WebAssembly.Authentication": "Debug"
  }
}

Conclusão

Provavelmente, os novos recursos introduzidos pelo .NET 7 para mitigar a complexidade do suporte à autenticação e autorização não mudarão drasticamente a sua vida como desenvolvedor mas vão facilitar a realização das tarefas relacionadas com estas operações.

E estamos conversados.

"Jesus dizia a todos: "Se alguém quiser acompanhar-me, negue-se a si mesmo, tome diariamente a sua cruz e siga-me."
Lucas 9:23

Referências:


José Carlos Macoratti