protected bool AccountIsLocked(PasswordLockMode lockMode, int failedAttemptCount, DateTime?tempLockUntilUTC)
        {
            var canLock = lockMode != PasswordLockMode.DoNotLock;
            var lockoutUntrustedClient =
                lockMode == PasswordLockMode.UntrustedClient &&
                failedAttemptCount >= _passwordlessLoginOptions.MaxUntrustedPasswordFailedAttempts;
            var tempLockout = tempLockUntilUTC > DateTime.UtcNow;

            return(canLock && (lockoutUntrustedClient || tempLockout));
        }
        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));
            }
        }
        protected async Task <CheckPasswordStatus> ProcessDoesNotMatchAndReturnAsync(string uniqueIdentifier, PasswordLockMode lockMode, int failedAttemptCount)
        {
            _logger.LogDebug("Password does not match.");
            if (lockMode == PasswordLockMode.DoNotLock)
            {
                return(CheckPasswordStatus.Error(_localizer["Password was not correct."], CheckPasswordStatusCode.PasswordIncorrect));
            }

            var currentFailedAttemptCount = failedAttemptCount + 1;

            if (currentFailedAttemptCount >= _passwordlessLoginOptions.TempLockPasswordFailedAttemptCount)
            {
                var lockUntil = DateTime.UtcNow.AddMinutes(_passwordlessLoginOptions.TempLockPasswordMinutes);
                _logger.LogDebug("Locking password until {0} (UTC)", lockUntil);
                if (lockMode == PasswordLockMode.TrustedClient)
                {
                    // for sign in attempts from a trusted client, we reset the failure account when doing a temp
                    // lock so that the next failure after a lockout will not initiate another lockout period
                    currentFailedAttemptCount = 0;
                }
                await _passwordHashStore.UpdatePasswordHashTempLockAsync(uniqueIdentifier, lockUntil, currentFailedAttemptCount);

                return(CheckPasswordStatus.Error(_localizer["Password is temporarily locked."], CheckPasswordStatusCode.TemporarilyLocked));
            }
            else
            {
                _logger.LogDebug("Updating failed attempt count");
                await _passwordHashStore.UpdatePasswordHashFailureCountAsync(uniqueIdentifier, failedAttemptCount + 1);

                return(CheckPasswordStatus.Error(_localizer["Password was not correct."], CheckPasswordStatusCode.PasswordIncorrect));
            }
        }