ASP.NET - Manipulador de autenticação Bearer Token


  Neste artigo vou apresentar o novo manipulador de autenticação Bearer Token.

Já faz muitos anos que os desenvolvedores ASP.NET Core têm na ASP.NET Core Identity(Identity)  um framweork integrado para adicionar suporte para autenticação e autorização local.

O Identity foi projetado para aplicações Web renderizadas por servidor, e possui desafios com aplicações Single Page Applications, onde a autenticação baseada em token parece ser mais pertinente

Para resolver esse problema, a Microsoft forneceu modelos de projeto integrados para aplicações SPAs baseadas em Angular e React com suporte ao Identity Server desde o .NET Core 3.1. No entanto, parece que a comunidade .NET não ficou totalmente satisfeita com esta abordagem.

Assim, no Net 8.0,  a Microsoft removeu o suporte padrão para Identity Server e redesenhou a arquitetura interna da ASP.NET Core Identity para ser mais adequada para aplicações nativas e aplicações SPAs .

Novo manipulador bearer token

No .NET 8 a ASP.NET Core 8 apresenta o novo manipulador de autenticação bearer token.  Esse manipulador é semelhante ao manipulador clássico de autenticação de cookie que a ASP.NET Core Identity usa por padrão

Ele é  Responsável por:

1 - Criar de um novo token após a autenticação do usuário;
2 - Construir um objeto de usuário ClaimsPrincipal com base em um token válido recebido no request HTTP;

Em outras palavras, o manipulador bearer token imita o comportamento do manipulador de cookies para gerenciar sessões autenticadas com base em um token em vez de um cookie.

Obs: Cabe destacar que os tokens gerados pelo manipulador bearer token não estão no formato JWT.

Como funciona a autenticação via cookies ?

Vamos recordar como é feita a autenticação via cookies.



- Após de realizar a autenticação do usuário a aplicação emite um cookie de sessão
- Este cookie contém sua identidade de usuário na forma de ClaimsPrincipal.
- O conteúdo do cookie é protegido e criptografado usando a API integrada de proteção de dados.
- O emissor deste cookie é o manipulador de autenticação de cookies.
- Este manipulador tem dois propósitos principais:
- Quando um usuário é autenticado, o manipulador emite um novo cookie de sessão com base nos detalhes do usuário fornecidos

Assim a autentição dos requests recebidos é feita procurando por um cookie de sessão.

Se um cookie válido for encontrado, ele criará um objeto de usuário ClaimsPrincipal com base nas informações contidas no cookie. Esse objeto de usuário é então passado pelo pipeline de request da ASP.NET Core e o request é validado.

Desta forma, atualmente, os sistemas dependem do cookie de sessão para autenticar as requisições dos usuários. Isso significa que você é forçado a usar a interface do usuário fornecida pelo servidor backend, com poucas opções de personalização.

Isso geralmente resulta em uma experiência inconsistente para os usuários quando eles fazem a transição de uma experiência de aplicativo baseada em navegador para uma experiência renderizada em servidor.

Assim os objetivos do manipulador bearer Token são :

1- Melhorar a autenticação baseada em cookies:
Introduzindo mais personalização e experiência de usuário consistente entre aplicativos de página única e renderizados no servidor.

2- Introduzir a autenticação baseada em token:
Alterar a autenticação baseada em cookies para um sistema de autenticação baseado em token mais flexível e amplamente usado

Com isso, o novo manipulador Bearer Token pode ser visto como uma alternativa ao manipulador de cookies, mas com uma diferença. Em vez de emitir cookies, ele emite um token de acesso e atualização. que devem ser usados entre o cliente e as aplicações.

O manipulador Bearer Token não pretende ser um componente independente. Em vez disso, ele deve ser usado com o Identity integrando outros recursos.

Na ASP.NET Core, agora temos três manipuladores que fazem quase a mesma coisa.



1- O manipulador de cookies
2- O manipulador de token JWT Bearer
3- O manipulador Bearer Token

Todos os três visam autenticar requisições de usuários e podem ser usados em aplicações ASP.NET Core.

Criando o projeto no Visual Studio

Agora eu vou mostrar um exemplo prático e básico de utilização do manipulador bearer token. Vamos criar um projeto ASP.NET Core Web API no VS 2022 preview chamado Net8BearerToken usando o .NET 8.0.

Na classe Program vamos usar o método de extensão AddBearerToken para configurar o middleware de autenticação com o manipulador do token de acesso :

builder.Services.AddAuthentication().AddBearerToken();

A seguir vamos adicionar os serviços de autorização :

builder.Services.AddAuthorization();

E,  para permitir que a interface do swagger recebe a informação do token vamos alterar a a configuração do addSwaggerGen() conforme abaixo:

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "apinet8", Version = "v1" });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
    {
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer",
        BearerFormat = "Token",
        In = ParameterLocation.Header,
        Description = "Token Access",
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                          new OpenApiSecurityScheme
                          {
                              Reference = new OpenApiReference
                              {
                                  Type = ReferenceType.SecurityScheme,
                                  Id = "Bearer"
                              }
                          },
                         new string[] {}
                    }
                });
});

Eu não vou usar um banco de dados para armazenar as informações do usuário, nem vou gerar os endpoints para o refresh token e para o registro do usuário.

Na pasta Controllers vamos criar o controlador AuthController com o seguinte código:

using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace Net8BearerToken.Controllers;
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    [HttpGet("/login")]
    public IActionResult Login(string username, string password)
    {
        if (IsValidUser(username, password))
        {
            var claimsPrincipal = new ClaimsPrincipal(
                new ClaimsIdentity(
                    new[] { new Claim(ClaimTypes.Name, username) },
                    BearerTokenDefaults.AuthenticationScheme
                )
            );
            return SignIn(claimsPrincipal);
        }
        return Unauthorized("Credenciais inválidas");
    }
    private bool IsValidUser(string username, string password)
    {
        return username == "macoratti" && password == "123456";
    }
    [HttpGet("/user")]
    [Authorize]
    public IActionResult GetUser()
    {
        var user = User;
        if (user?.Identity?.IsAuthenticated ?? false)
        {
            return Ok($"Bem-Vindo {user.Identity.Name}!");
        }
        return Unauthorized();
    }
}

Neste código  criamos o endpoint para login que cria um objeto de usuário ClaimsPrincipal com base no nome de usuário enviado na solicitação HTTP e retorna o resultado do login;

- var claimsPrincipal = new ClaimsPrincipal(...);

Se as credenciais são válidas, um objeto ClaimsPrincipal é criado. Este é um objeto que representa a identidade do usuário e é comumente usado para autenticação e autorização.

- new ClaimsIdentity(...);

Um objeto ClaimsIdentity é criado, que representa as reivindicações (claims) associadas ao usuário. No caso, apenas uma reivindicação é adicionada, indicando o nome do usuário.

Estamos definindo o esquema de autenticação usado para criar o ClaimsIdentity. Neste caso, está sendo utilizado o esquema Bearer Token.

- return SignIn(claimsPrincipal);

A função SignIn é chamada para efetuar a autenticação do usuário. O objeto ClaimsPrincipal é passado como parâmetro para indicar a identidade do usuário autenticado.

O segundo endpoint permite obter informações do usuário autenticado. A ação é protegida pelo atributo [Authorize], garantindo que apenas usuários autenticados possam acessar essa funcionalidade

var user = User;

Aqui, obtemos o objeto ClaimsPrincipal associado ao usuário atual. User é uma propriedade que retorna a ClaimsPrincipal associado ao request atual.

if (user?.Identity?.IsAuthenticated ?? false) { ... }

Verificamos se o usuário está autenticado, e se estiver, ele continua a executar o código dentro desse bloco

return Unauthorized();

Se o usuário não estiver autenticado, a ação retorna um resultado HTTP 401 (Unauthorized).

Observe as linhas destacadas no código, note que temos uma referência ao novo namespace Microsoft.AspNetCore.Authentication.BearerToken, que dá acesso ao valor BearerTokenDefaults.AuthenticationScheme.

Na classe Program vamos definir o código para configurar o middleware do token:

using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
...
builder.Services.AddAuthentication().AddBearerToken();
builder.Services.AddAuthorization();
var app = builder.Build();
...
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();


A configuração é feita usando o método de extensão AddBearerToken().

Executando o projeto teremos o seguinte resultado:

Acionando o endpoint GET /login e informando as credenciais:

Ao clicar em execute temos as seguintes informações geradas:

O token de acesso do tipo Bearer a data de expiração e o refresh token.

Vamos usar o token para acessar o endpoint /user que esta protegido.

Acessando o endpoint GET /user

Pegue o projeto aqui:  https://github.com/macoratti/Net8-BearerToken  

"Jesus disse-lhes: A minha comida é fazer a vontade daquele que me enviou, e realizar a sua obra."
João 4:34

Referências:


José Carlos Macoratti