static void

Demo User repository (sample!)

Code for demos or training, not production. This is intended to be simple, but it shows some practical security features.

ASP has had several user systems for Forms Authentication since .net 2, from the overcomplicated Membership Provider to the underpowered Identity. This repository is just a simple sample with no backing store. It demonstrates very simple security including hashing passwords, and counting login attempts, without being too complicated. Have a look at Membership Reboot for a better membership system.

User class

public class User
{
    public User()
    {
        RolesList = new List<string>();
    }
    public string Name { get; set; }
    public IList<string> RolesList { get; set; }
    public string PasswordHash { get; set; }
    public string PasswordSalt { get; set; }
    public int PasswordFailuresSinceLastSuccess { get; set; }
    public DateTime? LastPasswordFailureDate { get; set; }
}

UserRepository

    public class UserRepository
    {
 
        public User FindUser(string userName, string password)
        {
            var user = LoadUser(userName);
#if DEBUG
            //for testing, null passwords are the actual password
            if (user.PasswordHash == null)
            {
                ResetPassword(user, password);
            }
#endif
            //allow one try per hour
            if (user.PasswordFailuresSinceLastSuccess >= 5 &&
                ((DateTime.UtcNow - user.LastPasswordFailureDate) < TimeSpan.FromHours(1)))
            {
                Trace.TraceWarning("Locked out: " + userName);
                return null;
            }
            var hashedPass = EncodePassword(password, user.PasswordSalt);
            if (user.PasswordHash != hashedPass)
            {
                Trace.TraceWarning("Password failure " + userName);
                user.LastPasswordFailureDate = DateTime.UtcNow;
                user.PasswordFailuresSinceLastSuccess++;
                //save it!
                return null;
            }
            if (user.PasswordFailuresSinceLastSuccess > 0)
            {
                user.PasswordFailuresSinceLastSuccess = 0;
                //save it!
            }
            return user;
        }
 
        private User LoadUser(string userName)
        {
            //should hit database
            var user = new User { Name = userName };
            user.RolesList.Add("Admin");
            user.PasswordSalt = GenerateSalt();
            return user;
        }
 
        public void ResetPassword(User user, string password)
        {
            if (string.IsNullOrEmpty(user.PasswordSalt))
                user.PasswordSalt = GenerateSalt();
            user.PasswordFailuresSinceLastSuccess = 0;
            user.LastPasswordFailureDate = null;
            user.PasswordHash = EncodePassword(password, user.PasswordSalt);
        }
 
        private static string EncodePassword(string pass, string salt)
        {
            var bytes = Encoding.Unicode.GetBytes(pass);
            var src = Convert.FromBase64String(salt);
            var dst = new byte[src.Length + bytes.Length];
            Buffer.BlockCopy(src, 0, dst, 0, src.Length);
            Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
            var algorithm = System.Security.Cryptography.
                HashAlgorithm.Create("SHA256");//"SHA" or "SHA256"
            var inArray = algorithm.ComputeHash(dst);
            return Convert.ToBase64String(inArray);
        }
 
        private static string GenerateSalt()
        {
            var buf = new byte[16];
            (new System.Security.Cryptography.
                RNGCryptoServiceProvider()).GetBytes(buf);
            return Convert.ToBase64String(buf);
        }
    }