public async Task <Response> EnableAuthenticator(EnableAuthenticatorRequest request, string userId)
        {
            _logger.LogInformation("Enabled authenticator for {userId}", userId);
            var user = await _userManager.FindByIdAsync(userId);

            if (user.TwoFactorEnabled)
            {
                return(Response.ForError("2FA already enabled."));
            }
            if (request.Code.IsNullOrEmpty())
            {
                return(Response.ForError("Code is missing."));
            }
            var verificationCode = CleanCode(request.Code);

            var is2FaTokenValid = await _userManager.VerifyTwoFactorTokenAsync(user,
                                                                               _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);

            if (!is2FaTokenValid)
            {
                return(Response.ForError("2FA token is not valid."));
            }

            var result = await _userManager.SetTwoFactorEnabledAsync(user, true);

            if (!result.Succeeded)
            {
                return(Response.ForErrors(result.Errors.Select(c => $"{c.Code} - {c.Description}")));
            }

            return(Response.ForSuccess());
        }
        public async Task <Response <TIdentity> > CheckCredentials(string username, string password)
        {
            _logger.LogInformation("Check credentials for {username}", username);
            var user = await _userManager.FindByNameAsync(username);

            if (user == null)
            {
                return(Response.ForError("Invalid credentials."));
            }

            Response lockout;

            if ((lockout = await GetLockedStatus(user)) != null)
            {
                return(lockout);
            }

            if (await _userManager.CheckPasswordAsync(user, password))
            {
                await _userManager.ResetAccessFailedCountAsync(user);

                _logger.LogInformation("Credentials valid for {username}, access failed count is reset.", username);
                return(Response <TIdentity> .ForSuccess(user));
            }

            await _userManager.AccessFailedAsync(user);

            if ((lockout = await GetLockedStatus(user)) != null)
            {
                return(lockout);
            }

            return(Response.ForError("Invalid credentials."));
        }
        public async Task <Response <AuthenticatorKey> > GetAuthenticatorKey(string userId)
        {
            _logger.LogInformation("Get authentication key {userId}", userId);

            var user = await _userManager.FindByIdAsync(userId);

            if (user == null)
            {
                _logger.LogInformation("User {userId} not found", userId);
                return(Response.ForError("Credentials are not valid."));
            }
            if (user.TwoFactorEnabled)
            {
                return(Response.ForError("2FA already enabled."));
            }

            var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);

            if (string.IsNullOrEmpty(unformattedKey))
            {
                await _userManager.ResetAuthenticatorKeyAsync(user);

                unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
            }

            return(Response <AuthenticatorKey> .ForSuccess(new AuthenticatorKey
            {
                SharedKey = FormatKey(unformattedKey),
                AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey)
            }));
        }
        public async Task <Response <TIdentity> > Verify(string code, string username, string password)
        {
            _logger.LogInformation("Verify {code}", code);
            if (code.IsNullOrEmpty())
            {
                return(Response.ForError("Code is missing."));
            }

            var user = await _userManager.FindByNameAsync(username);

            if (user == null)
            {
                _logger.LogInformation("User {username} not found", username);
                return(Response.ForError("Credentials are not valid."));
            }

            if (!await _userManager.CheckPasswordAsync(user, password))
            {
                return(Response.ForError("Credentials are not valid."));
            }

            var authenticatorCode = CleanCode(code);

            _logger.LogInformation("Cleaned {authenticatorCode}", authenticatorCode); // it's ok to log this, as this info is transient

            var is2FaTokenValid = await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, authenticatorCode);

            if (!is2FaTokenValid)
            {
                return(Response.ForError("2FA token is not valid."));
            }

            return(Response <TIdentity> .ForSuccess());
        }
        public async Task <Response <AuthorizationCodeTokenResponse> > GetAccessToken(Requests.AuthorizationCodeTokenRequest request)
        {
            var authority = _idpOptions.Enable ? _idpOptions.AuthorityUrl : _partyDetailsOptions.BaseUri;

            using (var requestBody = new FormUrlEncodedContent(new Dictionary <string, string>
            {
                { "grant_type", "authorization_code" },
                { "scope", "iSHARE" },
                { "client_id", _spaOptions.SpaClientId },
                { "client_secret", _spaOptions.SpaClientSecret },
                { "redirect_uri", Path.Combine(_spaOptions.BaseUri, "callback") },
                { "code", request.Code }
            }))
            {
                var tokenResponse = await authority.AppendPathSegment("connect/token")
                                    .PostAsync(requestBody)

                ;

                if (tokenResponse.StatusCode == HttpStatusCode.OK)
                {
                    var content = await Task.FromResult(tokenResponse).ReceiveJson();

                    return(Response <AuthorizationCodeTokenResponse> .ForSuccess(
                               new AuthorizationCodeTokenResponse
                    {
                        AccessToken = (string)content.access_token,
                        ExpiresIn = (int)content.expires_in
                    }));
                }
            }
            return(Response.ForError("Token retrieval failed"));
        }
        private async Task <Response> GetLockedStatus(TIdentity user)
        {
            if (!await _userManager.IsLockedOutAsync(user))
            {
                return(null);
            }

            _logger.LogInformation("Username {username} is locked out", user.UserName);
            return(Response.ForError("Your account has been locked out due to multiple failed login attempts."));
        }
        public async Task <Response> Logout()
        {
            string token     = _httpContext.HttpContext.Request.Headers["Authorization"];
            var    authority = _idpOptions.Enable ? _idpOptions.AuthorityUrl : _partyDetailsOptions.BaseUri;

            if (!string.IsNullOrEmpty(token) && token.StartsWith("Bearer "))
            {
                var result = await _httpClient.RevokeTokenAsync(new TokenRevocationRequest
                {
                    Address      = Path.Combine(authority, "connect/revocation"),
                    ClientId     = _spaOptions.SpaClientId,
                    ClientSecret = _spaOptions.SpaClientSecret,
                    Token        = token.Substring("Bearer ".Length).Trim()
                });
            }

            return(Response.ForSuccess());
        }
        public async Task <Response <TIdentity> > Login(LoginRequest request)
        {
            _logger.LogInformation("Login for {username}", request?.Username);
            if (request == null)
            {
                return(Response.ForError(LoginErrorMessages.InvalidCredentials));
            }

            var result = await CheckCredentials(request.Username, request.Password);

            if (!result.Success)
            {
                return(Response.ForError(result.Errors.FirstOrDefault()));
            }

            if (!_spaOptions.TwoFactorEnabled.HasValue || !_spaOptions.TwoFactorEnabled.Value)
            {
                return(Response <TIdentity> .ForSuccess(result.Model));
            }

            if (result.Model.TwoFactorEnabled)
            {
                if (request.TwoFactorCode.IsNullOrEmpty())
                {
                    return(Response.ForError(LoginErrorMessages.TwoFactorCodeRequired));
                }
                var result2Fa = await Verify(request.TwoFactorCode, request.Username, request.Password);

                if (!result2Fa.Success)
                {
                    return(Response.ForError(LoginErrorMessages.InvalidCredentials));
                }
            }
            else
            {
                return(Response.ForError(LoginErrorMessages.TwoFactorSetupRequired));
            }

            return(Response <TIdentity> .ForSuccess(result.Model));
        }