Introduction
This is the third article of a series of articles on ASP.NET Core Identity:
In the previous steps, we created an ASP.NET Core MVC web site with Login/Logout functionalities using ASP.NET Core Identity. The MVC web site that we built used cookie based authentication which works fine for that scenario. But for many other scenarios (like consuming a secure API from a mobile app), cookie based approach is not a good option. One popular approach for those scenarios is token based authentication. In this step, we are going to add jwt token generation capability for authentication to our project.
I am not going to explain the details of what is a JWT token and how it is different from cookie based approach as there are tons of really good articles on this topic. If you are interested, you can have a look at these articles:
- https://auth0.com/blog/angularjs-authentication-with-cookies-vs-token/
- https://auth0.com/blog/ten-things-you-should-know-about-tokens-and-cookies/
- https://medium.com/vandium-software/5-easy-steps-to-understanding-json-web-tokens-jwt-1164c0adfcec
The complete code for this article is available in the Demo 3 folder in this repo https://github.com/ra1han/aspnet-core-identity.
Preparing the Services in Startup
To enable JWT token generation, we have to configure the services for identity middleware. But if we look at the current implementation of ConfigureServices
method in Startup
class, we will see that there is no configuration for cookies.
Then how is the cookie based authentication working? That's working because if we don't manually configure it, the default behaviour is Cookie based authentication.
We will use services.AddAuthentication
in ConfigureServices
method to add additional authentication scheme.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<applicationdbcontext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<applicationuser, identityrole="">()
.AddEntityFrameworkStores<applicationdbcontext>()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddCookie()
.AddJwtBearer(jwtBearerOptions =>
{
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters()
{
ValidateActor = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Token:Issuer"],
ValidAudience = Configuration["Token:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes
(Configuration["Token:Key"]))
};
});
services.AddMvc();
}
Preparing the Controllers
Now, we will add two new web API controllers:
TokenController
- Anonymously accessible, this is used by the user to retrieve the JWT token GreetingController
- Secured and only accessible using JWT Token authentication scheme
The token generation code in TokenController
:
[HttpPost]
public async Task<iactionresult> Get(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync
(user, model.Password, lockoutOnFailure: false);
if (!result.Succeeded)
{
return Unauthorized();
}
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, model.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken
(
issuer: _configuration["Token:Issuer"],
audience: _configuration["Token:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(60),
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(_configuration["Token:Key"])),
SecurityAlgorithms.HmacSha256)
);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
}
return BadRequest();
}
Using the API
Now to test our implementation, we shall use Postman.
First, we shall try to access Greeting
API (http://localhost:36946/api/greeting) from postman. We will see that we get Unauthorized error.
Now let's create a token from Token
API (http://localhost:36946/api/token). We will provide the username
and password
and the API will return the bearer token.
Now let's call the Greeting
API again with the bearer token.
It worked! In the next article, we shall create an Angular 4 project with user authentication functionalities developed in this step.