A Very Simple User Registration and Login in EF

Posted by Gabs on Sunday, August 25, 2019

A very simple membership class library for your .NET/C# application with Entity Framework 6 Code-First.

User.cs

Properties

Property Type Description
Id int A database-generated unique number which identifies the user
Email string Unique username or email
Password Hash/Salt byte[] Encrypted password data [read only]
LastLogin Nullable DateTime The last successful login of the user
Roles string Comma-separated role(s) of a user
UserProfiles any You can add more user profile data e.g. FirstName, LastName, BirthDate, Gender, Phone, Address, etc

Public Static Methods

Method Type Description
Create(User, strUserPassword, optional requiresActivation) User Creates new User. Return null if registration fails. requiresActivation set to true needs account be activated by the Administrator
Authenticate(userEmail, userPassword) bool Returns true if login is valid
Update(User, optionaluserPassword, optionalnewPassword) User Updates the user. This also serves as ChangePassword if parameter userPass and newPass are not empty
GetAll() List<User> Returns all users
GetAllUsersInRole(rolename) List<User> Returns all users belong to a role
GetUserById(id) User Returns User by Id
GetUserByEmail(email) User Returns User by email or username
GetCurrentUser() User Returns currently logged-in user
GetUserRoles(id or email) string[] Returns the roles of user as string array
Deactivate() User Disables user from logging in
Activate() User Re-enables user

Setup

Copy paste User.cs in your project and set the username and password for the auto-generated default administrator. The code below automatically creates user with username, password, and role set to "admin".


private const string DEFAULT_ADMIN_LOGIN = "admin";

Then change the type of _db instance to your app DbContext.


private static MyDbContext _db = new MyDbContext();

In your DbContext class, add database mapping to User class


public DbSet<User> Users { get; set; }

Execute update-database

Usage


Sign up


public void Register()
{
    if (txtPassword.Text == txtVerifyPass.Text)
    {
        User newUser = new User();
        newUser.Email = txtEmail.Text;
        newUser.FirstName = txtFirstName.Text;
        newUser.LastName = txtLastName.Text;
        newUser.Phone = txtPhone.Text;
        var user = User.Create(newUser, txtPassword.Text);
        if (user != null)
        {
            MessageBox.Show("You have successfully registered. \nUserId: " + user.Id);
            // Logic for successful registration...
        }
        else
            MessageBox.Show("Registration failed.");
    }
    else
        MessageBox.Show("Password is not the same.");

}

Sign in


public void Login()
{
    var valid = User.Authenticate(txtLoginEmail.Text, txtLoginPassword.Text);
    if(valid)
    {
        var currentUser = User.GetCurrentUser();
        MessageBox.Show("Hello " + currentUser.FirstName + ", you have successfully logged in to the system.");
        // Do what you want to do as login is successful...
    }
    else
        MessageBox.Show("Invalid email and/or password.");
}

User.cs Complete Code


using MyProject.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

[Table("Users")]
public partial class User
{
    // Change this to your desired default admin login and password
    private const string DEFAULT_ADMIN_LOGIN = "admin";
    // Change this to your DbContext class
    private static YourDbContext _db = new YourDbContext();


    #region UserAccountRepository
    public int Id { get; set; }
    //Login info
    [Required]
    [StringLength(254)]
    public string Email { get; set; }
    public byte[] PasswordHash { get; set; }
    public byte[] PasswordSalt { get; set; }
    public DateTime CreatedOn { get; set; }
    public DateTime? LastLogin { get; set; }
    public bool IsActive { get; set; }
    public string Roles { get; set; } // comma-separated


    private static User CurrentUser = null;

    public static bool Authenticate(string userEmail, string userPassword)
    {
        CreateAdmin(); // Comment out this line if you already have admin account

        if (string.IsNullOrWhiteSpace(userEmail) || string.IsNullOrWhiteSpace(userPassword))
            return false;

        var user = _db.Users.Where(x => x.Email == userEmail.Trim().ToLower()).FirstOrDefault();
        if (user == null)
            return false;
        if (!user.IsActive)
            return false;

        bool valid = VerifyPasswordHash(userPassword, user.PasswordSalt, user.PasswordHash);
        if (valid)
        {
            user.LastLogin = DateTime.Now;
            _db.Entry(user).State = System.Data.Entity.EntityState.Modified;
            _db.SaveChanges();
            CurrentUser = user; // Set current user
            return true;
        }

        return false;
    }

    private static bool VerifyPasswordHash(string userPassword, byte[] passwordSalt, byte[] passwordHash)
    {
        // Verify PasswordHash
        using (var hmac = new System.Security.Cryptography.HMACSHA512(passwordSalt))
        {
            var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(userPassword));
            for (int i = 0; i < computedHash.Length; i++)
            {
                if (computedHash[i] != passwordHash[i])
                    return false;
            }
        }

        return true;
    }

    public static int? Create(string userEmail, string userPassword, string userRoles = "", bool requiresActivation = false)
    {
        if (string.IsNullOrWhiteSpace(userPassword))
            return null;
        if (string.IsNullOrWhiteSpace(userEmail))
            return null;

        var user = new UserAccount();
        user.Email = userEmail.Trim().ToLower();

        var userExists = _db.Users.Where(x => x.Email == user.Email).Count() > 0;
        if (userExists)
            return null;

        // Create PasswordHash
        using (var hmac = new System.Security.Cryptography.HMACSHA512())
        {
            user.PasswordSalt = hmac.Key;
            user.PasswordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(userPassword));
        }

        user.Roles = userRoles;
        user.CreatedOn = DateTime.Now;
        user.IsActive = !requiresActivation;

        _db.Users.Add(user);
        _db.SaveChanges();

        return user.Id;
    }

    private static void CreateAdmin()
    {
        var hasAdmin = _db.Users.Where(x => x.Roles == DEFAULT_ADMIN_LOGIN).Any();
        if (!hasAdmin)
        {
            Create(DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_LOGIN);
        }
    }

    public static bool ChangePassword(string email, string userPassword = "", string newPassword = "", bool forceChange = false)
    {
        if (string.IsNullOrWhiteSpace(newPassword))
            return false;

        if (forceChange == false && string.IsNullOrWhiteSpace(userPassword))
            return false;

        var user = _db.Users.Where(x => x.Email == email.Trim()).FirstOrDefault();
        if (user == null)
            return false;


        var validPassword = !forceChange ? VerifyPasswordHash(userPassword, user.PasswordSalt, user.PasswordHash) : true;
        if (validPassword)
        {
            // Overwrite with new PasswordHash
            using (var hmac = new System.Security.Cryptography.HMACSHA512())
            {
                user.PasswordSalt = hmac.Key;
                user.PasswordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(newPassword));
            }
        }

        _db.Entry(user).State = System.Data.Entity.EntityState.Modified;
        _db.SaveChanges();
        return true;
    }

    public static List<UserAccount> GetAll()
    {
        var users = _db.Users.ToList();
        return users;
    }

    public static List<UserAccount> GetAllUsersInRole(string role)
    {
        var users = _db.Users.ToList().Where(x => x.Roles.Split(',').Contains(role)).ToList();
        return users;
    }

    public static UserAccount GetUserById(int userId)
    {
        var user = _db.Users.Find(userId);
        return user;
    }

    public static UserAccount GetUserByEmail(string userEmail)
    {
        var user = _db.Users.Where(x => x.Email == userEmail).FirstOrDefault();
        return user;
    }

    public static UserAccount GetCurrentUser()
    {
       	return CurrentUser;
    }

    public static string[] GetUserRoles(int userId)
    {
        var user = GetUserById(userId);
        if (user != null)
            return user.Roles.Split(',');
        else
            return new string[] { string.Empty };
    }

    public static string[] GetUserRoles(string userEmail)
    {
        var user = GetUserByEmail(userEmail);
        return GetUserRoles(user.Id);
    }

    public static UserAccount Deactivate(string userEmail)
    {
        var user = _db.Users.Where(x => x.Email == userEmail).FirstOrDefault();
        if (user != null)
        {
            user.IsActive = false;
            _db.Entry(user).State = System.Data.Entity.EntityState.Modified;
            _db.SaveChanges();
            return user;
        }
        else
            return null;

    }

    public static UserAccount Activate(string userEmail)
    {
        var user = _db.Users.Where(x => x.Email == userEmail).FirstOrDefault();
        if (user != null)
        {
            user.IsActive = true;
            _db.Entry(user).State = System.Data.Entity.EntityState.Modified;
            _db.SaveChanges();
            return user;
        }
        else
            return null;

    }
    #endregion

}