2.9: Adding Login Functionality
Add UserForLogin DTO
In your Models/User
folder - add another class called UserForLogin.cs
.
This will be the same as our the register DTO we created earlier. We could use that here - but, we will be making changes later, so it's better to go ahead and have two.
public class UserForLogin
{
public string Username { get; set; }
public string Password { get; set; }
}
Adding Login Method to Controller
Now we're finally ready to write our Login()
method.
In this method, we don't need to validate the user's input. If their username and/or passwords don't match - we don't want to give them too much information about what was wrong. We'll just return Unauthorized()
.
[HttpPost("Login")]
public async Task<IActionResult> Login([FromBody] UserForLogin userForLogin)
{
var userFromDb = await _authService.Login(userForLogin.Username.ToLower(), userForLogin.Password);
if (userFromDb == null)
return Unauthorized();
// GENERATE TOKEN
var tokenHandler = new JwtSecurityTokenHandler();
var key = new byte[0]; // we'll add this after adding DI later
var tokenDescriptor = new SecurityTokenDescriptor // Describes information we want to include in our token
{
Subject = new ClaimsIdentity(new Claim[] // Payload
{
new Claim(ClaimTypes.NameIdentifier, userFromDb.Id.ToString()),
new Claim(ClaimTypes.Name, userFromDb.Username)
}),
Expires = DateTime.Now.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha512Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor); // Create token
var tokenString = tokenHandler.WriteToken(token); // to string (from byte[])
return Ok( new { tokenString }); // Return 200, passing along tokenString
}
There is a lot going on here. Some comments have been added to help make it more clear what's going on.
We're getting the user from the database and checking if it's null -- if so, we return Unauthorized()
.
We're using a SecurityTokenDescriptor
to format our token - right now we're giving our payload two pieces of information: the Id and username.
We're using JwtSecurityTokenHandler
to actually create the token with the descriptor we made and also to write it to a string.
Register Authentication and Add Middleware in Startup Class
Now we need to register our key in our DI container and configure our middleware to use authentication.
In your Startup.cs class add the following to the ConfigureServices()
method:
public void ConfigureServices(IServiceCollection services)
{
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value); // <---- Added
services.AddDbContext<EFConnectContext>(x => x.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
services.AddScoped<IAuthService, AuthService>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // <---- Added
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
Next, to add authentication to the middleware - add this above the call to UseMvc()
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication(); // <---- Added
app.UseMvc();
}
Adding Key to AuthController and Login() Method
Back in our AuthController
, we can now access our key.
First, let's inject it into our constructor (remember ctrl + .
to create a field from parameter):
private readonly IConfiguration _config;
public AuthController(IAuthService authService, IConfiguration config)
{
_config = config;
_authService = authService;
}
Again - we're only injecting interfaces. This is easily swapped out in our Startup class.
Now, we can use this in our Login()
method:
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_config.GetSection("AppSettings:Token").Value); // <---- Added
var tokenDescriptor = new SecurityTokenDescriptor
Last updated