public async Task <ActionResult <User> > Create(User user) { if (await _userService.GetWithEmail(user.Email) != null) { return(Unauthorized("Email already exists.")); } var salt = HashProvider.GetSalt(); user.IV = salt; user.Password = HashProvider.GetHash(user.Password, salt); user.CreatedAt = DateTime.Now; var claims = new[] { new Claim(ClaimTypes.Email, user.Email) }; user.RefreshToken = TokenProvider.GetToken ( user, _configuration["SecurityKey"], DateTime.Now.AddYears(100), claims ); await _userService.Create(user); return(CreatedAtRoute("GetUser", new { id = user.Id.ToString() }, user)); }
/// <summary> /// Registers a new user. The PasswordHash property should be the actual password. /// </summary> /// <param name="user">A user with a raw password which is turned into a password hash as part of registration.</param> /// <param name="duration">The amount of time that the initial session will be valid.</param> /// <param name="ipAddress">The internet address where the user is connecting from.</param> /// <param name="result">A ExecutionResults instance to add applicable /// warning and error messages to.</param> /// <returns>A boolean indicating success (true) or failure (false).</returns> public override UserIdentity RegisterUser(User user, UserSessionDurationType duration, String ipAddress, ExecutionResults result) { string password = user.PasswordHash; if (!ValidateName(user.Name, result) || !ValidatePassword(password, result)) { return(new cs.UserIdentity()); } var existing = GetUserByName(user.Name); if (existing != null) { //seed user table with deleted users with names you don't want users to have result.AppendError("The name you specified cannot be used."); return(new cs.UserIdentity()); } if (user.UserID.Equals(Guid.Empty)) { user.UserID = Guid.NewGuid(); } HashProvider hasher = HashManager.SelectProvider(); var salt = new UserSalt { PasswordSalt = hasher.GetSalt(), UserID = user.UserID, HashGroup = new Random(DateTime.Now.Second).Next(HashGroupMinimum, HashGroupMaximum), HashName = hasher.Name }; user.PasswordHash = hasher.Hash(salt.PasswordSalt, password, salt.HashGroup + BaseHashIterations); using (var scope = new System.Transactions.TransactionScope()) { //starts as a lightweight transaction SaveUser(user); //enlists in a full distributed transaction if users and salts have different connection strings SaveUserSalt(salt); } return(AuthenticateUser(name: user.Name, password: password, duration: duration, ipAddress: ipAddress, checkHistory: false, allowUpdateHash: false, result: result)); }
/// <summary> /// Generates a new password reset code for a user and stores that as the current code valid /// for the next hour. /// </summary> /// <param name="name">The user name / email address.</param> /// <returns>If the user exists, then a reset code string; otherwise null.</returns> public override String GenerateUserResetCode(String name) { var user = GetUserByName(name); if (user == null) { return(null); } var salt = GetUserSalt(user.UserID); if (!String.IsNullOrWhiteSpace(salt.ResetCode) && salt.ResetCodeExpiration > DateTime.UtcNow.AddMinutes(5)) { return(salt.ResetCode); //if submits form to request a code multiple times during window, then use the same code unless about to expire. } HashProvider hasher = !string.IsNullOrEmpty(salt.HashName) ? HashManager.Providers[salt.HashName] : HashManager.DefaultProvider; salt.ResetCode = hasher.GetSalt(16); salt.ResetCodeExpiration = DateTime.UtcNow.AddHours(1); SaveUserSalt(salt); return(salt.ResetCode); }
/// <summary> /// updates a user's name and/or password. /// </summary> /// <param name="item">The user details to be saved. If Password is empty is it not changed. If specified it should be the new raw password (not a hash).</param> /// <param name="currentPassword">The current raw password for the user used to authenticate that the change can be made, or the current resetcode last sent to this user.</param> /// <param name="ipAddress">The internet address where the user is connecting from.</param> /// <param name="result">A ExecutionResults instance to add applicable /// warning and error messages to.</param> /// <returns>A boolean indicating success (true) or failure (false).</returns> public override bool UpdateUser(User item, String currentPassword, String ipAddress, ExecutionResults result) { if (item.UserID.Equals(Guid.Empty)) { throw new ArgumentException("The user identity must be specified."); } var user = GetUserByID(item.UserID); var salt = GetUserSalt(item.UserID); if (user == null || salt == null) { result.AppendError("The specified user identity does not exist."); return(false); } if (salt.ResetCode == currentPassword) { if (salt.ResetCodeExpiration < DateTime.UtcNow) { result.AppendError( "Your password reset code has expired. Request a new one to be sent to you, and then use it immediately."); return(false); } salt.ResetCode = null; salt.ResetCodeExpiration = DateTime.UtcNow; } else { var rememberMe = !cs.SecurityContextManager.IsAnonymous && cs.SecurityContextManager.CurrentUser.Identity.Ticket.UserSession.ExpirationDate > DateTime.UtcNow.AddMinutes(PublicSessionDuration); if (!AuthenticateUser(name: item.Name, password: currentPassword, ipAddress: ipAddress, duration: rememberMe ? UserSessionDurationType.Extended : UserSessionDurationType.PublicComputer, allowUpdateHash: false, checkHistory: false, result: result).IsAuthenticated) { result.AppendError("Cannot change password due to authentication error with current password."); return(false); } } if (user.Name != item.Name) { //user is changing their sign in name. Make sure the new name is available. var nameExisting = GetUserByName(item.Name); if (nameExisting != null) { result.AppendError("The name you specified cannot be used."); return(false); } user.Name = item.Name; } if (!String.IsNullOrEmpty(item.PasswordHash)) { var password = item.PasswordHash; //update hashes on regular basis, keeps the iterations in latest range for current users, and with a 'current' hash provider. HashProvider hasher = HashManager.SelectProvider(); salt.PasswordSalt = hasher.GetSalt(); salt.HashGroup = new Random(DateTime.Now.Second).Next(HashGroupMinimum, HashGroupMaximum); salt.HashName = hasher.Name; user.PasswordHash = hasher.Hash(salt.PasswordSalt, password, salt.HashGroup + BaseHashIterations); user.PasswordUpdatedDate = DateTime.UtcNow; } using (var scope = new System.Transactions.TransactionScope()) { //starts as a lightweight transaction SaveUser(user); //enlists in a full distributed transaction if users and salts have different connection strings SaveUserSalt(salt); } return(true); }
private cs.UserIdentity AuthenticateUser(string name, string password, UserSessionDurationType duration, string ipAddress, bool checkHistory, bool allowUpdateHash, ExecutionResults result) { if (checkHistory) { var recentFailures = GetRecentFailedUserNameAuthenticationCount(name); if (recentFailures > AllowedFailuresPerPeriod) { return(FailAuthenticateUser(name, ipAddress, result)); } } User user = GetUserByName(name); if (user == null) { return(FailAuthenticateUser(name, ipAddress, result)); } UserSalt salt = GetUserSalt(user.UserID); if (salt == null) { return(FailAuthenticateUser(name, ipAddress, result)); } //this should get a named hashProvider used to originally hash the password... // fallback to 'default' provider in legacy case when we didn't store the name. HashProvider hasher = !string.IsNullOrEmpty(salt.HashName) ? HashManager.Providers[salt.HashName] : HashManager.DefaultProvider; var passwordHash = hasher.Hash(salt.PasswordSalt, password, salt.HashGroup + BaseHashIterations); if (user.PasswordHash != passwordHash) { return(FailAuthenticateUser(name, ipAddress, result)); } var session = new UserSession { CreatedDate = DateTime.UtcNow, ExpirationDate = DateTime.UtcNow.AddMinutes(duration == UserSessionDurationType.PublicComputer ? PublicSessionDuration : ExtendedSessionDuration), UserID = user.UserID, RenewalToken = Guid.NewGuid() }; var history = new AuthenticationHistory { IPAddress = ipAddress, IsAuthenticated = true, UserName = name, UserSession = session }; using (var scope = new System.Transactions.TransactionScope()) { if (allowUpdateHash && (hasher.IsObsolete || user.PasswordHashUpdatedDate < DateTime.UtcNow.AddMonths(-1))) { //update hashes on regular basis, keeps the iterations in latest range for current users, and with a 'current' hash provider. hasher = HashManager.SelectProvider(); salt.PasswordSalt = hasher.GetSalt(); salt.HashGroup = new Random(DateTime.Now.Second).Next(HashGroupMinimum, HashGroupMaximum); salt.HashName = hasher.Name; user.PasswordHash = hasher.Hash(salt.PasswordSalt, password, salt.HashGroup + BaseHashIterations); user.PasswordHashUpdatedDate = DateTime.UtcNow; //starts as a lightweight transaction SaveUser(user); //enlists in a full distributed transaction if users and salts have different connection strings SaveUserSalt(salt); } //either continues distributed transaction if applicable, // or creates a new lightweight transaction for these two commands SaveUserSession(session); InsertUserHistory(history); } return(new cs.UserIdentity(history, this.Name)); }