//Called every time the MFA Dropdown is changed by the user.
    protected void MFAChallenge_SelectedIndexChanged(object sender, EventArgs e)
    {
        //Show text answer UI elements if selected MFA mechanism requires text input from the user.
        Dictionary <string, string> mech = MFAChallenge_DropDownList.SelectedItem.Value.TrimEnd(';').Split(';').ToDictionary(item => item.Split('=')[0], item => item.Split('=')[1]);

        if (mech["AnswerType"] == "Text")
        {
            MFAAnswer_Label.Text      = UiDrivenLogin.MechToPrompt(mech);
            MFAAnswer_Label.Visible   = true;
            MFAAnswer_Textbox.Visible = true;
        }
        else
        {
            MFAAnswer_Label.Visible   = false;
            MFAAnswer_Textbox.Visible = false;
        }

        //make sure that the MFA Label is modified to match selected mech.
        if (mech["AnswerType"] != "StartOob")
        {
            //Set the Loading text for polling
            MFALoad_Label.Text = "";
        }
        else
        {
            MFALoad_Label.Text = MFAAnswer_Label.Text = UiDrivenLogin.MechToPrompt(mech);
        }
    }
    protected void ProcessChallenges(dynamic challengeCollection, bool doSecondChallenge = false)
    {
        //We should clear our dropdown list every time to ensure we are starting with a clean list.
        MFAChallenge_DropDownList.Items.Clear();

        // We need to satisfy one of each challenge collection:
        SortedList <string, string> items = new SortedList <string, string>();

        //If doSecondChallenge is true we will change the challenge set array to the second set. Otherwise we will use the first Challenge set
        int challengeSet = 0;

        if (doSecondChallenge)
        {
            challengeSet = 1;
        }

        //The first step for the drop down list is to populate a SortedList with our MFA mechanisms
        for (int mechIdx = 0; mechIdx < challengeCollection[challengeSet]["Mechanisms"].Count; mechIdx++)
        {
            string mechValue = ConvertDicToString(challengeCollection[challengeSet]["Mechanisms"][mechIdx]);
            items.Add(UiDrivenLogin.MechToDescription(challengeCollection[challengeSet]["Mechanisms"][mechIdx]), mechValue);
        }

        //The second step for the drop down list is to bind the SortedList to our dropdown element
        try
        {
            MFAChallenge_DropDownList.DataTextField  = "Key";
            MFAChallenge_DropDownList.DataValueField = "Value";
            MFAChallenge_DropDownList.DataSource     = items;
            MFAChallenge_DropDownList.DataBind();
        }
        catch (Exception ex)
        {
            //There was an error
            Login_Div.Visible    = false;
            FailureText.Text     = ex.InnerException.ToString();
            ErrorMessage.Visible = true;
        }

        StartAuthentication_Next_Button.Visible   = false;
        AdvanceAuthentication_Next_Button.Visible = true;
        Form.DefaultButton = AdvanceAuthentication_Next_Button.UniqueID;
        MFAChallenge_DropDownList.Visible       = true;
        MFAChallenge_DropDownList_Label.Visible = true;

        Dictionary <string, string> mech = MFAChallenge_DropDownList.SelectedItem.Value.TrimEnd(';').Split(';').ToDictionary(item => item.Split('=')[0], item => item.Split('=')[1]);

        //If our first mechanism in our dropdown has a text answer type we should show our MFA answer text box.
        if (mech["AnswerType"] == "Text")
        {
            MFAAnswer_Label.Text      = UiDrivenLogin.MechToPrompt(mech);
            MFAAnswer_Label.Visible   = true;
            MFAAnswer_Textbox.Visible = true;
        }
        else if (mech["AnswerType"] != "Text" && mech["AnswerType"] != "StartTextOob")
        {
            //Set the Loading text for polling
            MFALoad_Label.Text = MFAAnswer_Label.Text = UiDrivenLogin.MechToPrompt(mech);
        }
    }
    //Called when the user selects an MFA type from the MFA DropDown and presses next.
    protected void AdvanceAuthentication(object sender, EventArgs e)
    {
        Dictionary <string, string> mech = MFAChallenge_DropDownList.SelectedItem.Value.TrimEnd(';').Split(';').ToDictionary(item => item.Split('=')[0], item => item.Split('=')[1]);
        RestClient authenticationClient  = (RestClient)Session["AuthenticaitonClient"];

        Dictionary <string, dynamic> resultsDictionary = new Dictionary <string, dynamic>();

        if (sender.Equals(ForgotPass_Button))
        {
            resultsDictionary             = UiDrivenLogin.AdvanceForMech(authenticationClient, authenticationClient.TenantId, authenticationClient.SessionId, RememberMe.Checked, null, null, null, false, true);
            MFAAnswer_Label.Visible       = false;
            MFAAnswer_Textbox.Visible     = false;
            RememberMe_Div.Visible        = false;
            ForgotPass_Button_Div.Visible = false;
        }
        else
        {
            //If the mechanism is StartTextOob we need to allow the user to answer via text and poll at the same time.
            if (mech["AnswerType"] == "StartTextOob")
            {
                if (StartTextOob_Timer.Enabled) //We have already called AdvanceAuthentication once and are calling it again using the MFA Answer Text Box submit button.
                {
                    resultsDictionary = UiDrivenLogin.AdvanceForMech(authenticationClient, authenticationClient.TenantId, authenticationClient.SessionId, RememberMe.Checked, null, MFAChallenge_DropDownList.SelectedItem.Value, MFAAnswer_Textbox.Text, true);
                }
                else //This is the first time calling AdvanceAuthenticaiton and we need to start polling and turn on our TimerTick while also displaying the MFA Answer Text Box for user input.
                {
                    resultsDictionary = UiDrivenLogin.AdvanceForMech(authenticationClient, authenticationClient.TenantId, authenticationClient.SessionId, RememberMe.Checked, null, MFAChallenge_DropDownList.SelectedItem.Value);

                    MFAAnswer_Label.Text       = UiDrivenLogin.MechToPrompt(mech);
                    MFAAnswer_Label.Visible    = true;
                    MFAAnswer_Textbox.Visible  = true;
                    StartTextOob_Timer.Enabled = true; //Used to poll again every tick while waiting for user input in the form.
                }
            }
            else
            {
                //User input is required for standard text mechanisms
                if (mech["AnswerType"] == "Text")
                {
                    resultsDictionary = UiDrivenLogin.AdvanceForMech(authenticationClient, authenticationClient.TenantId, authenticationClient.SessionId, RememberMe.Checked, null, MFAChallenge_DropDownList.SelectedItem.Value, MFAAnswer_Textbox.Text);
                }
                //No user input required for polling mechanisms
                else
                {
                    resultsDictionary  = UiDrivenLogin.AdvanceForMech(authenticationClient, authenticationClient.TenantId, authenticationClient.SessionId, RememberMe.Checked, null, MFAChallenge_DropDownList.SelectedItem.Value);
                    MFALoad_Label.Text = ""; //Change the loading text back to default
                }

                MFAAnswer_Label.Visible   = false;
                MFAAnswer_Textbox.Visible = false;
            }
        }

        //Check results
        if (resultsDictionary["success"])
        {
            MFAChallenge_DropDownList.Visible       = false;
            MFAChallenge_DropDownList_Label.Visible = false;

            //MFA was successful. We need to process the results to determine what should happen next.
            if (resultsDictionary["Result"]["Summary"] == "StartNextChallenge" || resultsDictionary["Result"]["Summary"] == "LoginSuccess" || resultsDictionary["Result"]["Summary"] == "NewPackage")
            {
                StartTextOob_Timer.Enabled = false; //Turn off timer so that we are not polling in the background for StartTextOob
                ProcessResults(resultsDictionary);
            }
            //If the summary is OobPending this means that StartTextOob has returned pending and we need to stop and wait. If any other results are present there is an error.
            else if (resultsDictionary["Result"]["Summary"] != "OobPending")
            {
                //There was an error
                Login_Div.Visible    = false;
                FailureText.Text     = "There was an unexpected error. Please contact your system administrator. Click here to <a href=\"Login.aspx\">start over.</a>";
                ErrorMessage.Visible = true;
            }
        }
        else
        {
            //There was an error
            Login_Div.Visible    = false;
            FailureText.Text     = resultsDictionary["Message"] + " Click here to <a href=\"Login.aspx\">start over.</a>";
            ErrorMessage.Visible = true;
        }
    }