2.3: Adding Auth Service

Add Authentication Interface

Add an interface to your .Contracts project by right clicking on the folder name and selecting "New C# Interface" called IAuthService.cs.

Add method signatures for register, login, and checking whether the user already exists in the database.

using System.Threading.Tasks;
using EFConnect.Data.Entities;

namespace EFConnect.Contracts
{
    public interface IAuthService
    {
         Task<User> Register(User user, string password);
         Task<User> Login(string username, string password);
         Task<bool> UserExists(string username);
    }
}

Add Concrete AuthService

In the .Services project - create a new C# class called AuthService.cs that implements IAuthService.

Start by adding a constructor to inject our DbContext:

public AuthService(EFConnectContext context)
{
}

Ctrl + . on the context parameter and choose Generate field from parameter.

private readonly EFConnectContext _context;
public AuthService(EFConnectContext context)
{
    _context = context;
}

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;
}

Last updated