public async Task <ActionResult <ApiResult <SessionPreview> > > LoginMfa([FromBody] MfaLoginModel data, CancellationToken cancellationToken = default)
        {
            if (data.ActionToken == null || data.MfaCode == null)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "MFA state validation failed."))));
            }

            var(stateId, clientTk) = this.UnpackState(data.ActionToken);
            var serverTk = await this.MfaStateRepository.ValidateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), stateId, cancellationToken);

            var tokenPair = new ActionTokenPair(clientTk, serverTk);

            if (!this.ActionTokenPairHandler.ValidateTokenPair(tokenPair, TokenActionMFA))
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "MFA state validation failed."))));
            }

            var userId = BinaryPrimitives.ReadInt64BigEndian(clientTk.State);
            var mfa    = await this.MfaRepository.GetMfaSettingsAsync(userId, cancellationToken);

            if (mfa == null || !mfa.IsConfirmed)
            {
                return(this.StatusCode(400, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "MFA not configured."))));
            }

            if (data.MfaCode.Length == 6)
            {
                if (!this.MfaValidator.ValidateCode(data.MfaCode, mfa))
                {
                    return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
                }
            }
            else if (data.MfaCode.Length == 8)
            {
                if (!this.MfaValidator.ValidateRecoveryCode(data.MfaCode, mfa))
                {
                    return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
                }

                await this.MfaRepository.TripRecoveryCodeAsync(userId, cancellationToken);
            }
            else
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
            }

            var user = await this.UserRepository.GetUserAsync(userId, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
        public async Task <ActionResult <ApiResult <SessionPreview> > > MfaEnable([FromBody] MfaLoginModel data, CancellationToken cancellationToken = default)
        {
            var(stateId, clientTk) = this.UnpackState(data.ActionToken);
            var serverTk = await this.MfaStateRepository.ValidateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), stateId, cancellationToken);

            var tokenPair = new ActionTokenPair(clientTk, serverTk);

            if (!this.ActionTokenPairHandler.ValidateTokenPair(tokenPair, TokenActionMFAConfigure))
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "MFA state validation failed."))));
            }

            var user = this.RosettaUser;
            var pwd  = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            var mfa = await this.MfaRepository.GetMfaSettingsAsync(user.Id, cancellationToken);

            if (mfa == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "MFA not configured."))));
            }

            if (mfa.IsConfirmed)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.AlreadyConfigured, "MFA is already configured."))));
            }

            if (!this.MfaValidator.ValidateCode(data.MfaCode, mfa))
            {
                await this.MfaRepository.RemoveMfaAsync(user.Id, cancellationToken);

                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
            }

            await this.MfaRepository.ConfirmMfaAsync(user.Id, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
        public async Task <ActionResult <ApiResult <SessionPreview> > > Login([FromBody] OAuthAuthenticationModel data, CancellationToken cancellationToken = default)
        {
            if (data.State == null)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "OAuth state validation failed."))));
            }

            var(stateId, clientTk) = this.UnpackState(data.State);
            var serverTk = await this.OAuthStateRepository.ValidateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), stateId, cancellationToken);

            var tokenPair = new ActionTokenPair(clientTk, serverTk);

            if (!this.ActionTokenPairHandler.ValidateTokenPair(tokenPair, TokenActionOAuth))
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "OAuth state validation failed."))));
            }

            var provider = this.OAuthSelector.IdFromReferrer(new Uri(data.Referrer));
            var oauth    = this.OAuthSelector.GetById(provider);

            if (oauth == null)
            {
                return(this.NotFound(ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidProvider, "Specified provider does not exist."))));
            }

            var ctx    = this.CreateContext(provider, data.State);
            var tokens = await oauth.CompleteLoginAsync(ctx, data.Code, cancellationToken);

            if (tokens == null)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.ExternalAuthenticationError, "Failed to authenticate with the OAuth provider."))));
            }

            var expires = DateTimeOffset.UtcNow.AddSeconds(tokens.ExpiresIn - 20);
            var ouser   = await oauth.GetUserAsync(ctx, tokens.AccessToken, cancellationToken);

            if (ouser == null || !ouser.IsAuthorized)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "You are not authorized to participate."))));
            }

            var oid   = ouser.Id;
            var euser = await this.UserRepository.GetExternalAccountAsync(oid, provider, cancellationToken);

            var user = euser?.User;

            if (euser == null)
            {
                if (!AbstractionUtilities.NameRegex.IsMatch(ouser.Username))
                {
                    return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidName, "Specified username contained invalid characters."))));
                }

                var uid = this.GetUserId();
                if (uid == null)
                {
                    user = await this.UserRepository.CreateUserAsync(ouser.Username, ouser.IsAuthorized, cancellationToken);
                }
                else
                {
                    user = await this.UserRepository.GetUserAsync(uid.Value, cancellationToken);
                }

                euser = await this.UserRepository.ConnectExternalAccountAsync(user.Id, ouser.Id, ouser.Username, provider, cancellationToken);
            }

            await this.UserRepository.UpdateTokensAsync(user.Id, euser.ProviderId, tokens.AccessToken, tokens.RefreshToken, expires, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }