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 <Response <CheckOneTimeCodeResult, CheckOneTimeCodeStatus> > CheckOneTimeCodeAsync(string sentTo, string shortCode, string clientNonce)
        {
            _logger.LogTrace("Checking short code");

            var response = await _oneTimeCodeStore.GetOneTimeCodeAsync(sentTo);

            if (response.HasError)
            {
                return(new Response <CheckOneTimeCodeResult, CheckOneTimeCodeStatus>(
                           CheckOneTimeCodeStatus.Error(_localizer["One time code not found."], CheckOneTimeCodeStatusCode.NotFound)));
            }
            var otc = response.Result;

            if (otc.ExpiresUTC < DateTime.UtcNow)
            {
                _logger.LogDebug("The one time code has expired.");
                return(new Response <CheckOneTimeCodeResult, CheckOneTimeCodeStatus>(
                           CheckOneTimeCodeStatus.Error(_localizer["The one time code has expired."], CheckOneTimeCodeStatusCode.Expired)));
            }

            if (!string.IsNullOrEmpty(shortCode) && shortCode.Length == PasswordlessLoginConstants.OneTimeCode.ShortCodeLength)
            {
                if (otc.FailedAttemptCount >= PasswordlessLoginConstants.OneTimeCode.MaxFailedAttemptCount)
                {
                    // maximum of 3 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)
                    _logger.LogDebug("The one time code is locked (too many failed attempts to use it).");
                    return(new Response <CheckOneTimeCodeResult, CheckOneTimeCodeStatus>(
                               CheckOneTimeCodeStatus.Error(_localizer["The one time code is locked."], CheckOneTimeCodeStatusCode.ShortCodeLocked)));
                }
                if (shortCode == otc.ShortCode)
                {
                    _logger.LogDebug("The one time code matches");
                    return(await ExpireTokenAndValidateNonceAsync(otc, clientNonce));
                }
            }
            else
            {
                _logger.LogDebug("The one time code was missing or was the wrong length");
            }

            _logger.LogDebug("Updating failure count for one time code");
            await _oneTimeCodeStore.UpdateOneTimeCodeFailureAsync(sentTo, otc.FailedAttemptCount + 1);

            return(new Response <CheckOneTimeCodeResult, CheckOneTimeCodeStatus>(
                       CheckOneTimeCodeStatus.Error(_localizer["The one time code was not correct."], CheckOneTimeCodeStatusCode.CodeIncorrect)));
        }