Now, ctrl + . on IAuthRepository and select Implement interface
public class AuthService : IAuthService
{
private readonly EFConnectContext _context;
public AuthService(EFConnectContext context)
{
_context = context;
}
public Task<User> Login(string username, string password)
{
throw new System.NotImplementedException();
}
public Task<User> Register(User user, string password)
{
throw new System.NotImplementedException();
}
public Task<bool> UserExists(string username)
{
throw new System.NotImplementedException();
}
}
Add CreatePasswordHash Method
We'll add a private method below the public methods to create a password hash.
It will take three parameters: one will be passed by value: password, and two will be passed by reference: passwordHash and passwordSalt - this will allow our method to directly alter the values held by the passwordHash and passwordSalt variables.
private void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512()) // hmac will generate salt key
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
}
}
Implement Register Method
Now that we have a method to create our salted hash, we can implement our register method.
public async Task<User> Register(User user, string password)
{
byte[] passwordHash, passwordSalt;
CreatePasswordHash(password, out passwordHash, out passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
return user;
}
Add VerifyPasswordHash Method
Next, we need another private helper method to check the password a user enters against the hashed password saved to the database in order to implement our Login() method.
This method will work in the opposite direction of our CreatePasswordHash() method.
We'll loop through the computed hash and compare each byte against what's stored in our database. If anything doesn't match, we'll return false - otherwise, we'll return true.
private bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512(passwordSalt))
{
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
for (int i = 0; i < computedHash.Length; i++)
{
if (computedHash[i] != passwordHash[i]) return false;
}
return true;
}
}
Implement Login Method
In this method, we need to compare what's stored in our database to what is entered by the user using our VerifyPasswordHash() method.
public async Task<User> Login(string username, string password)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == username);
if (user == null)
return null;
if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
return null;
return user;
}
Implement UserExists() method
public async Task<bool> UserExists(string username)
{
if (await _context.Users.AnyAsync(x => x.Username == username))
return true;
return false;
}