public async Task <CheckOneTimeCodeResponse> CheckOneTimeCodeAsync(string sentTo, string shortCode) { var otc = await _oneTimeCodeStore.GetOneTimeCodeAsync(sentTo); if (otc == null) { return(new CheckOneTimeCodeResponse(CheckOneTimeCodeResult.NotFound)); } if (otc.ExpiresUTC < DateTime.UtcNow) { return(new CheckOneTimeCodeResponse(CheckOneTimeCodeResult.Expired)); } if (!string.IsNullOrEmpty(shortCode) && shortCode.Length <= 8) { if (otc.FailedAttemptCount > 1) { // maximum of 2 attempts during code validity period to prevent guessing attacks // long code remains valid, preventing account lockout attacks (and giving a fumbling but valid user another way in) return(new CheckOneTimeCodeResponse(CheckOneTimeCodeResult.ShortCodeLocked)); } var checkResult = _passwordHashService.CheckPasswordHash(otc.ShortCodeHash, shortCode); if (checkResult == CheckPaswordHashResult.Matches || checkResult == CheckPaswordHashResult.MatchesNeedsRehash) { await _oneTimeCodeStore.ExpireOneTimeCodeAsync(sentTo); return(new CheckOneTimeCodeResponse(CheckOneTimeCodeResult.Verified, sentTo, otc.RedirectUrl)); } } await _oneTimeCodeStore.UpdateOneTimeCodeFailureAsync(sentTo, otc.FailedAttemptCount + 1); return(new CheckOneTimeCodeResponse(CheckOneTimeCodeResult.CodeIncorrect)); }
public async Task <CheckPasswordResult> CheckPasswordAsync(string uniqueIdentifier, string password) { var hashInfo = await _passwordHashStore.GetPasswordHashAsync(uniqueIdentifier); if (hashInfo == null) { return(CheckPasswordResult.NotFound); } if (hashInfo.TempLockUntilUTC > DateTime.UtcNow) { return(CheckPasswordResult.TemporarilyLocked); } var checkHashResult = _passwordHashService.CheckPasswordHash(hashInfo.Hash, password); switch (checkHashResult) { case CheckPaswordHashResult.DoesNotMatch: if (hashInfo.FailedAttemptCount > 3) // todo, get from settings { var lockUntil = DateTime.UtcNow.AddMinutes(5); // todo: get from settings //todo: consider if failure count should be reset or not. (should first subsequent failure after lockout initiate another lockout period?) await _passwordHashStore.TempLockPasswordHashAsync(uniqueIdentifier, lockUntil); return(CheckPasswordResult.TemporarilyLocked); } else { await _passwordHashStore.UpdatePasswordHashFailureAsync(uniqueIdentifier, hashInfo.FailedAttemptCount + 1); return(CheckPasswordResult.PasswordIncorrect); } case CheckPaswordHashResult.MatchesNeedsRehash: var newHash = _passwordHashService.HashPassword(password); await _passwordHashStore.UpdatePasswordHashAsync(uniqueIdentifier, newHash); return(CheckPasswordResult.Success); case CheckPaswordHashResult.Matches: return(CheckPasswordResult.Success); default: // this should never happen return(CheckPasswordResult.ServiceFailure); } }
public async Task <CheckPasswordStatus> CheckPasswordAsync(string uniqueIdentifier, string password, PasswordLockMode lockMode) { _logger.LogDebug("Checking password for {0}", uniqueIdentifier); var response = await _passwordHashStore.GetPasswordHashAsync(uniqueIdentifier); if (response.HasError) { var status = new CheckPasswordStatus(); status.Add(response.Status); status.StatusCode = CheckPasswordStatusCode.NotFound; return(status); } var hashInfo = response.Result; if (AccountIsLocked(lockMode, hashInfo.FailedAttemptCount, hashInfo.TempLockUntilUTC)) { return(CheckPasswordStatus.Error(_localizer["Password is temporarily locked."], CheckPasswordStatusCode.TemporarilyLocked)); } var checkHashResult = _passwordHashService.CheckPasswordHash(hashInfo.Hash, password); switch (checkHashResult) { case CheckPasswordHashResult.DoesNotMatch: return(await ProcessDoesNotMatchAndReturnAsync(uniqueIdentifier, lockMode, hashInfo.FailedAttemptCount)); case CheckPasswordHashResult.MatchesNeedsRehash: return(await ProcessMatchesNeedsRehashAndReturnAsync(uniqueIdentifier, password)); case CheckPasswordHashResult.Matches: return(await ProcessMatchesAndReturnAsync(uniqueIdentifier)); default: // this should never happen return(CheckPasswordStatus.Error(_localizer["An unexpected error occurred."], CheckPasswordStatusCode.ServiceFailure)); } }