ASP .NET Core -  Implementando a autenticação JWT - I


Neste artigo vamos recordar como implementar a autenticação JWT na ASP .NET Core.

Eu já apresentei os conceitos e utilização do padrão JSON Web Tokens em duas vídeo aulas:

Como eu expliquei nesses vídeos, a JWT significa JSON Web Tokens, e, é um padrão aberto ( RFC-7519 ) para passar dados do usuário entre o cliente e o servidor. O JWT possui muitas vantagens em relação à autenticação tradicional de cookies.

O JWT é mais seguro e também pode ser usado com outros tipos de clientes além do cliente web. Em um cenário Single Pagel Application o JWT é uma opção preferencial para implementar a autenticação.

Um token JWT consiste em três partes:

  1. Cabeçalho (header) - objeto json que define o tipo de token e o algoritmo de criptografia usado;
  2. Carga útil(payload) - objeto josn contendo as claims;
  3. Assinatura(signature) - É a concatenação dos hashes gerados a partir do Header e do PayLoad;



Ao contrário dos cookies, que são passados ​​automaticamente para o servidor, o token JWT precisa ser explicitamente passado para o servidor pelo cliente.

Então, um fluxo simplificado de operações seria o seguinte:

1- O cliente envia credenciais de segurança, como nome de usuário e senha, para o servidor para validação;
2- O servidor valida o nome de usuário e a senha;
3- Se as credenciais forem válidas, o servidor gera e emite um token JWT para o cliente;
4- O cliente recebe o token e o armazena em algum lugar;
5- Ao solicitar qualquer recurso ou ação do servidor, o cliente adiciona o token JWT emitido anteriormente no cabeçalho Authorization da requisição;
6- O servidor lê o cabeçalho de autorização para recuperar o token JWT;
7- Se o token for válido, o servidor executará a ação solicitada pelo cliente;


Então, basta pensar no token JWT como um ticket. Se a requisição recebida tiver um ticket válido, ela poderá acessar um recurso.

Dessa forma para implementar a autenticação baseada em JWT, precismos executar as seguintes etapas:

1- Armazenar os detalhes do JWT em um arquivo de configuração.
2- Ativar o esquema de autenticação JWT na inicialização do aplicativo.
3- Criar algum mecanismo que valide o nome de usuário e a senha e emita um token JWT.
4- Crie uma API protegida (Authorize)
5- Invocar a API de um cliente usando o token

Neste artigo vamos seguir esse roteiro básico.

Recursos Usados:

Criando o projeto ASP .NET Core Web API

Abra o VS 2017 Community e clique em New Project;

Selecione o template ASP .NET Core Web Application e informe o nome ApiJwt1 e clique em OK;

Selecione a seguir o template API para criar a Web API clique no botão OK.

Ao final teremos um projeto com uma estrutura padrão e um controlador ValuesController na pasta Controllers que iremos usar para testar a autenticação via JWT.

Para isso vamos incluir o atributo Authorize no método Get e Get(int id) do controlador ValuesController :

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        [Authorize]
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "Maria", "Paulo", "Pedro", "Marcia", "Armando" };
        }
        [Authorize]
        // GET api/values/5
        [HttpGet("{id}")]
        public ActionResult<string> Get(int id)
        {
            return "Marta";
        }
        ....
}

Nesta etapa vamos definir a classe de domínio User na pasta Models do projeto com o código abaixo:

        public class User
        {
          public string UserName { get; set; }
          public string Password { get; set; }
        }

1- Armazenar os detalhes do JWT em um arquivo de configuração.

Inclua no arquivo appsettings.json do projeto uma seção Jwt com as informações a seguir:

{
   "Jwt": {
        "Key": "dfusa7f9090dfsiaisfdasfiuasjasdfa90cvzxxzcvasf998dfspd",
        "Issuer": "MacorattiIssuer",
        "Audience": "MacorattiAudience"
   }
}

Você pode definir o nome da seção a seu gosto, e, ela define 3 chaves:

  1. Key - Aqui você informa uma chave secreta que vai ser usada para assinar o token;
  2. Issuer - Indica quem esta emitindo o token jwt;
  3. Audience - Indica os destinatários do token jwt;

2- Ativar o esquema de autenticação JWT na inicialização do aplicativo.

Abra o arquivo Startup e modifique o método ConfigureServices() conforme mostrado abaixo:

public void ConfigureServices(IServiceCollection services)
{
     services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

     services.AddAuthentication
	(JwtBearerDefaults.AuthenticationScheme)
	.AddJwtBearer(options =>
	{
		options.TokenValidationParameters = new TokenValidationParameters
		{
			ValidateIssuer = true,
			ValidateAudience = true,
			ValidateLifetime = true,
			ValidateIssuerSigningKey = true,
			ValidIssuer = Configuration["Jwt:Issuer"],
			ValidAudience = Configuration["Jwt:Audience"],
			IssuerSigningKey = new SymmetricSecurityKey
		(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
		};
     	});
}

Neste código chamamos o método AddAuthentication() e especificamos o esquema do portador JWT para ser o esquema de autenticação.

Também especificamos várias opções para o esquema do portador JWT. Se você observar atentamente o objeto TokenValidationParameters, verá que ele indica se o emissor, a audiência, a vida útil e a chave de assinatura devem ser validados ou não.

Também especificamos um emissor válido, um público-alvo válido e uma chave de assinatura válida. Esses valores são recuperados do arquivo de configuração que definimos anteriormente.

Os namespaces usados para poder usar esses recursos são :

Para concluir precisamos incluir a linha de código app.UseAuthentication no método Configure():

       public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }
            app.UseHttpsRedirection();

            app.UseAuthentication();
            app.UseMvc();
        }

Precisamos desse código para vincular o middleware de autenticação no pipeline HTTP.

3- Criar algum mecanismo que valide o nome de usuário e a senha e emita um token JWT.

Vamos implementar agora o código que vai validar as credenciais do usuário : username e password.

Para isso vamos criar um novo Controlador chamado SegurancaController na pasta Controllers e vamos definir 3 métodos:

  1. Login()
  2. ValidarUsuario()
  3. GerarTokenJwt()

Esse controlador vai precisar ter uma instância de IConfiguration para poder acessar as informações no arquivo de configuração.

using Api_Jwt1.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
namespace Api_Jwt1.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class SegurancaController : ControllerBase
    {
        private readonly IConfiguration _config;
        public SegurancaController(IConfiguration configuration)
        {
            _config = configuration;
        }
        [HttpPost]
        public IActionResult Login([FromBody]User loginDetails)
        {
            bool resultado = ValidarUsuario(loginDetails);
            if (resultado)
            {
                var tokenString = GerarTokenJwt();
                return Ok(new { token = tokenString });
            }
            else
            {
                return Unauthorized();
            }
        }
        private bool ValidarUsuario(User loginDetails)
        {
            if (loginDetails.UserName == "mac" && loginDetails.Password == "numsey")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public string GerarTokenJwt()
        {
            var issuer = _config["Jwt:Issuer"];
            var audience = _config["Jwt:Audience"];
            var expiry = DateTime.Now.AddMinutes(60);
            var securityKey = new SymmetricSecurityKey
                              (Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
            var credentials = new SigningCredentials
                              (securityKey, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(issuer: issuer,
                                             audience: audience,
                                             expires: DateTime.Now.AddMinutes(120),
                                             signingCredentials: credentials);
            var tokenHandler = new JwtSecurityTokenHandler();
            var stringToken = tokenHandler.WriteToken(token);
            return stringToken;
        }
    }
}

Observe que a Action Login esta marcada com o atributo [HttPost] e portanto essa ation será invocada pelo aplicativo cliente e o cliente, de alguma forma, fornecerá os detalhes do usuário, como nome de usuário e senha.

A seguir chamamos o método auxiliar ValidaUsuario() para verificar se o nome de usuário e a senha são válidos. Se as credenciais do usuário forem válidas, chamamos GerarTokenJwt() para gerar um token JWT. O token de string é retornado ao cliente com o status HTTP de Ok (código de status - 200).

Observe que não implementamos o acesso a um banco de dados. Para simplificar o método ValidarUsuario definimos uma validação fixa, mas  você poderia usar o ASP.NET Core Identity ou qualquer técnica personalizada para validar um usuário. Se as credenciais do usuário forem válidas, retornamos true, caso contrário, retornaremos false.

O método mais importante é o método GerarTokenJwt() onde temos :

A recuperação do emissor, do público-alvo e da chave do arquivo de configuração. Em seguida, temos a criação de uma nova SymmetricSecurityKey com base na chave.

O objeto SigningCredentials é gerado com base na SymmetricSecurityKey. Observe que usamos o algoritmo HS256 ao gerar a assinatura digital.

Agora podemos seguir em frente e criar um token JWT. Isso é feito usando a classe JwtSecurityToken. Passamos o emissor, o público-alvo, um DateTime de expiração para o token e as credenciais de assinatura no construtor.

Queremos o JWT em um formato de string para que possa ser facilmente enviado ao cliente. Isso é feito usando a classe JwtSecurityTokenHandler. O método WriteToken() aceita um JwtSecurityToken criado anteriormente e o retorna como uma string de formato serializado compact JSON.

Podemos agora testar a nossa implementação fazendo o Login e gerando um Token e a seguir acessando a API.

Usando o Postman - Gerando o Token e acessando a API

Para instalar o Postman acesse esse link : https://www.getpostman.com/apps ou abra o Google Chrome e digite postam e a seguir clique no link:  Postman - Chrome Web Store

Execute o projeto no VS 2017.

A seguir abra o Postman e vamos definir a seguinte requisição para criar um usuário:

1 - https://localhost:44355/api/seguranca

E no corpo da requisição (Body) usando o formato JSON (application/json) vamos passr as credencias:
{
   "Username" : "mac",
   "Password" :  "numsey"
}

Conforme mostra a figura a abaixo:

Ao clicar no botão Send vemos o token gerado exibido na resposta.

Agora já temos o token, e, vamos acessar a API ValuesController enviando uma requisição com o token.

Para isso vamos copiar o token obtido e montar a seguinte requisição GET no Postman:

3- https://localhost:44355/api/values

A seguir em Authorization marque a opção Bearer Token e informe o token conforme gerado figura abaixo:

Observe que obtivemos os valores retornados pelo método GET (que esta protegido com Authorize) indicando que o nosso token foi validado com sucesso.

Na próxima parte do artigo veremos como consumir essa API usando jQuery.

"O Senhor é o meu rochedo, e o meu lugar forte, e o meu libertador; o meu Deus, a minha fortaleza, em quem confio; o meu escudo, a força da minha salvação, e o meu alto refúgio."
Salmos 18:2

Referências:


José Carlos Macoratti