Anexsoft | Blog y dictado de cursos de Tecnología

En este encontrarás tutoriales de tecnología como PHP, ASP.NET MVC, Front-End, entre otros y cursos exigentes de modalidad online que te ayudarán a crecer profesionalmente.

Autenticación JWT usando ASP.NET Core
Actualizado el 27 Enero, 2018 y leído 2,933 veces
Calificación: 9.67 / 10

Autenticación JWT usando ASP.NET Core

Anexsoft

¿Qué vamos hacer?

Vamos a crear un proyecto netcore ya sea para webapi o asp.net mvc. Luego vamos a proteger nuestros controladores mediante token usando JwT. Sino tienes idea que es JwT te invito a leer el siguiente artículo.

Creando los parámetros de configuración en nuestro AppSetting

Crearemos unos parámetros básicos de configuración en nuestra appsettings que serán los responsables de configurar nuestro token.

{
  "ApiAuth": {
    "Issuer": "http://localhost:51761",
    "Audience": "MyTestApi",
    "SecretKey": "asdwda1d8a4sd8w4das8d*w8d*asd@#"
  }
}
  • Issuer: comunmente se suele poner la url de nuestro servidor que levanta el API. Este parámetro es opcional pero vamos a setearlo.
  • Audience: se suele usar para dar el nombre a tu API, en este caso le puse MyTestApi.
  • SecretKey: valor único que usaremos para proteger nuestro token.

 

Configurando nuestro token en ASP.NET Core

Nos vamos a nuestro archivo StartUp y comenzamos a configurar el middleware de autenticación mediante token.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options => {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(jwtBearerOptions =>
    {
        jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateActor = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["ApiAuth:Issuer"],
            ValidAudience = Configuration["ApiAuth:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["ApiAuth:SecretKey"]))
        };
    });

    services.AddMvc();
}
  • ValidateAudience: le decimos que debe validar el audience que hemos definido.
  • ValidateLifeTime: como bien sabes nuestro token puede tener un tiempo de expiración, por lo tanto le diremos que valide esto.
  • ValidateIssuerSigningKey: que tome en cuenta la validación de nuestro secretkey.
  • ValidIssuer: seteamos nuestro valor que definimos en el appsettings.
  • ValidAudience: seteamos nuestro valor que definimos en el appsettings.
  • IssuerSigningKey: seteamos nuestro secret key para generar un TOKEN único que evita que sea usado por otra persona.

Estos son los pasos básicos para decirle a nuestro token como debe comportarse y apartir en adelante los que quieran ingresa a nuestra API deberán usar un token como hemos especificado.

Luego le decimos a ASP.NET Core que lo use en el runtime

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

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

 

Generando un token válido

Vamos a crear un controlador de autenticación y generación del token.

[Route("[controller]")]
public class AuthController : Controller
{
    private readonly IConfiguration _configuration;

    public AuthController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpPost]
    [Route("[action]")]
    public IActionResult Login(LoginViewModel model)
    {
        /*
         * Aquí deberá ir su lógica de validación, en nuestro ASP.NET Identity
         * y luego de verificar que sea una autenticación correcta vamos a proceder a
         * generar el token
         */

        // Asumamos que tenemos un usuario válido
        var user = new User
        {
            Name = "Eduardo",
            Role = "Admin",
            UserId = Guid.NewGuid().ToString()
        };

        /* Creamos la información que queremos transportar en el token,
         * en nuestro los datos del usuario
         */
        var claims = new[]
        {
            new Claim("UserData", JsonConvert.SerializeObject(user))
        };

        // Generamos el Token
        var token = new JwtSecurityToken
        (
            issuer: _configuration["ApiAuth:Issuer"],
            audience: _configuration["ApiAuth:Audience"],
            claims: claims,
            expires: DateTime.UtcNow.AddDays(60),
            notBefore: DateTime.UtcNow,
            signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["ApiAuth:SecretKey"])),
                    SecurityAlgorithms.HmacSha256)
        );

        // Retornamos el token
        return Ok(
            new {
                response = new JwtSecurityTokenHandler().WriteToken(token)
            }
        );
    }
}

Expires: es el tiempo de expiración que va a durar nuestro token, es decir a partir de ahorita hasta 60 días después vivirá el token del caso contrario el token dejará de ser válido.

 

Acerca de los claims

Los claims es como decir una bolsa donde guardamos información que queremos persistir en el token. En mi caso voy a crear un claims llamado userData y contendrá toda la información de nuestro usuario siendo serializada a Json.

var claims = new[]
{
    new Claim("UserData", JsonConvert.SerializeObject(user))
};

 

¿Cómo sería con ASP.NET Identity?

Para este ejemplo no he usado ASP.NET Identity pero la lógica es muy sencilla, después de haber validado la autenticación con el SignInManager simplemente copiamos el código anterior después de corrobar el éxito. Comparto un ejemplo de como lo tengo en un proyecto real.

var user = await _userManager.FindByEmailAsync(model.Email);

if (user != null)
{
    var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);

    if (result.Succeeded)
    {
        var claims = new[]
        {
          new Claim("id", user.Id),
          new Claim("email", user.Email),
          new Claim("name", user.Name),
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(ApiConfiguration.SecretKey));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            "self",
            _configuration["Token:Issuer"],
            claims,
            expires: DateTime.Now.AddDays(30),
            signingCredentials: creds
        );
        
        // Tu hermoso código de respuesta
    }
}

La lógica es la siguiente:

  1. Busco al usuario solo por el email, no importa que no haya pasado la validación.
  2. Luego intento autenticar al usuario obtenido (por eso lo recupere previamente) con sus credenciales (password) para validar si tienes acceso o no.
  3. En caso de éxito generamos los claims, el tiempo de expiración y todo lo que ya hemos visto.

 

Generando un token mediante el Postman

Abran el postman y manden estos parámetros mediante una petición POST a la siguiente ruta

http://localhost:51761/auth/login

Y como no estamos realmente validando una autenticación real no hace falta mandarle mayores parámetros. Por lo tanto, vamos a recibir la siguiente respuesta.

{
    "response": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyRGF0YSI6IntcIlVzZXJJZFwiOlwiY2Y4ZjRhY2QtZTdlZC00ZTY1LWJmNmQtNTk5YWY3NjhhYmZlXCIsXCJOYW1lXCI6XCJFZHVhcmRvXCIsXCJSb2xlXCI6XCJBZG1pblwifSIsIm5iZiI6MTUxNjg1NTk2NiwiZXhwIjoxNTIyMDM5OTY2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUxNzYxIiwiYXVkIjoiTXlUZXN0QXBpIn0.zDLc5YofTK4Y2J0tbXFPRK3IrbDZPbjZnZUDipZS5v4"
}

Listo ya tenemos un Token válido.

 

Protegiendo un controlador con nuestro token

Agregamos el tag [Authorize] a nuestro controlador para protegerlo

[Route("[controller]")]
[Authorize]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

Y realizamos una prueba mediante postman sin especificar ningún parámetro, la ruta de prueba es:

GET - http://localhost:51761/values

La respuesta es 401 (Unauthorized) el cual quiere decir acceso denegado.

Hagamos otra prueba pasando el token por los headers deberán crear un header de la siguiente manera

Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyRGF0YSI6IntcIlVzZXJJZFwiOlwiYjA1ZDJlZmEtZDBjMy00Njg3LWEwMjUtNmQ1Mjg1MDNiMmJiXCIsXCJOYW1lXCI6XCJFZHVhcmRvXCIsXCJSb2xlXCI6XCJBZG1pblwifSIsIm5iZiI6MTUxNjg1NjUyMywiZXhwIjoxNTIyMDQwNTIzLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUxNzYxIiwiYXVkIjoiTXlUZXN0QXBpIn0.beMAnbautEc7ch_9SSu0Amn-cgMWOit6Po0qB5FBEfk

Llamado Authorization y como value tenga Bearer + token. Si todo esta bien deberíamos visualizar la siguiente información:

["value1","value2"]

 

En otras publicaciones veremos como obtener la información del usuario logeado mediante el token y proteger nuestros controladores por roles.

¡Adquiera ya!

  • Software de Venta e Inventario hecho en PHP y Codeigniter

    Software de Venta e Inventario hecho en PHP y Codeigniter
  • Código de fuente de Red Social desarrollada en ASP.NET MVC

    Código de fuente de Red Social desarrollada en ASP.NET MVC
  • Software de Portafolio Profesional hecho en ASP.NET MVC 5 C#

    Software de Portafolio Profesional hecho en ASP.NET MVC 5 C#

Últimas publicaciones

Encuesta

¿Sobre qué te gustaría que se hable más?

Síguenos

Estudia con nosotros y crece profesionalmente

Nuestros cursos han sido hecho en base a lo que demanda el mercado hoy en día.
La experiencia obtenida es la de un ambiente laboral.

Anexsoft
© 2018 Anexsoft, blog y cursos online de TI.