/// <summary>
        /// GetUserCodeWithExternalSystem method implementation for Azure MFA
        /// </summary>
        public int GetUserCodeWithExternalSystem(string upn, string phonenumber, string smstext, ExternalOTPProvider externalsys, CultureInfo culture)
        {
            ResourcesLocale Resources      = new ResourcesLocale(culture.LCID);
            String          NumberStr      = phonenumber;
            int             CountryCode    = 0;
            ulong           NationalNumber = 0;
            string          extension      = string.Empty;

            PhoneNumberUtil phoneUtil   = PhoneNumberUtil.GetInstance();
            PhoneNumber     NumberProto = phoneUtil.Parse(NumberStr, culture.TwoLetterISOLanguageName.ToUpper());

            CountryCode    = NumberProto.CountryCode;
            NationalNumber = NumberProto.NationalNumber;
            if (NumberProto.HasExtension)
            {
                extension = NumberProto.Extension;
            }

            PhoneFactor.Initialize(externalsys);
            PhoneFactorParams Params = new PhoneFactorParams();

            Params.Username = upn;

            Params.CountryCode     = CountryCode.ToString();
            Params.Phone           = NationalNumber.ToString();
            Params.Extension       = extension;
            Params.ApplicationName = "IdentityServer";
            Params.Sha1Salt        = externalsys.Sha1Salt;

            if (externalsys.IsTwoWay)
            {
                Params.SmsText = string.Format(Resources.GetString(ResourcesLocaleKind.SMSAzure, "SMSTwoWayMessage"), externalsys.Company);
                Params.Mode    = PhoneFactor.MODE_SMS_TWO_WAY_OTP;
            }
            else
            {
                Params.SmsText = string.Format(Resources.GetString(ResourcesLocaleKind.SMSAzure, "SMSMessage"), externalsys.Company);
                Params.Mode    = PhoneFactor.MODE_SMS_ONE_WAY_OTP;
            }

            int    callStatus;
            int    errorId;
            string otp = string.Empty;

            if (PhoneFactor.Authenticate(Params, out otp, out callStatus, out errorId, externalsys.Timeout))
            {
                if (externalsys.IsTwoWay)
                {
                    return((int)AuthenticationResponseKind.SmsTwoWayOTP);
                }
                else
                {
                    return(Convert.ToInt32(otp));
                }
            }
            else
            {
                return((int)AuthenticationResponseKind.Error);
            }
        }
        /// <summary>
        /// CreateMessage method implmentation
        /// </summary>
        private static string CreateMessage(PhoneFactorParams pfAuthParams, bool asynchronous)
        {
            bool sms           = pfAuthParams.Mode == MODE_SMS_TWO_WAY_OTP || pfAuthParams.Mode == MODE_SMS_TWO_WAY_OTP_PLUS_PIN || pfAuthParams.Mode == MODE_SMS_ONE_WAY_OTP || pfAuthParams.Mode == MODE_SMS_ONE_WAY_OTP_PLUS_PIN;
            bool two_way       = pfAuthParams.Mode == MODE_SMS_TWO_WAY_OTP || pfAuthParams.Mode == MODE_SMS_TWO_WAY_OTP_PLUS_PIN;
            bool otp           = pfAuthParams.Mode == MODE_SMS_TWO_WAY_OTP || pfAuthParams.Mode == MODE_SMS_ONE_WAY_OTP;
            bool phone_app     = pfAuthParams.Mode == MODE_PHONE_APP_PIN || pfAuthParams.Mode == MODE_PHONE_APP_STANDARD;
            bool phone_app_pin = pfAuthParams.Mode == MODE_PHONE_APP_PIN;

            XmlDocument doc = new XmlDocument();

            // start message
            // <pfpMessage></pfpMessage>
            XmlElement root = doc.CreateElement("pfpMessage");

            root.SetAttribute("version", "1.5");
            doc.AppendChild(root);

            // message header
            // <header></header>
            XmlElement header = doc.CreateElement("header");

            root.AppendChild(header);
            XmlElement source = doc.CreateElement("source");

            header.AppendChild(source);
            XmlElement component = doc.CreateElement("component");

            component.SetAttribute("type", "pfsdk");
            source.AppendChild(component);
            XmlElement element = doc.CreateElement("host");

            element.SetAttribute("ip", pfAuthParams.IpAddress);
            element.SetAttribute("hostname", pfAuthParams.Hostname);
            component.AppendChild(element);

            // request
            // <request></request>
            XmlElement request = doc.CreateElement("request");
            Random     random  = new Random();

            if (pfAuthParams.RequestId == null || pfAuthParams.RequestId.Length == 0)
            {
                pfAuthParams.RequestId = random.Next(10000).ToString();
            }
            request.SetAttribute("request-id", pfAuthParams.RequestId);
            request.SetAttribute("language", pfAuthParams.Language);
            request.SetAttribute("async", asynchronous ? "1" : "0");
            request.SetAttribute("response-url", pfAuthParams.ResponseUrl);
            root.AppendChild(request);
            XmlElement auth_request = doc.CreateElement("authenticationRequest");

            if (sms)
            {
                auth_request.SetAttribute("mode", "sms");
            }

            request.AppendChild(auth_request);
            XmlElement customer = doc.CreateElement("customer");

            auth_request.AppendChild(customer);
            element           = doc.CreateElement("licenseKey");
            element.InnerText = LICENSE_KEY;
            customer.AppendChild(element);
            element           = doc.CreateElement("groupKey");
            element.InnerText = GROUP_KEY;
            customer.AppendChild(element);
            element           = doc.CreateElement("countryCode");
            element.InnerText = pfAuthParams.CountryCode;
            auth_request.AppendChild(element);
            element           = doc.CreateElement("authenticationType");
            element.InnerText = "pfsdk";
            auth_request.AppendChild(element);
            element           = doc.CreateElement("username");
            element.InnerText = pfAuthParams.Username;
            auth_request.AppendChild(element);
            element = doc.CreateElement("phonenumber");
            element.SetAttribute("userCanChangePhone", "no");
            element.InnerText = pfAuthParams.Phone;
            if (pfAuthParams.Extension != null && pfAuthParams.Extension.Length != 0)
            {
                element.SetAttribute("extension", pfAuthParams.Extension);
            }
            auth_request.AppendChild(element);
            element           = doc.CreateElement("allowInternationalCalls");
            element.InnerText = pfAuthParams.AllowInternationalCalls ? "yes" : "no";
            auth_request.AppendChild(element);
            element           = doc.CreateElement("applicationName");
            element.InnerText = pfAuthParams.ApplicationName;
            auth_request.AppendChild(element);

            XmlElement pin_info    = doc.CreateElement("pinInfo");
            XmlElement pin_element = doc.CreateElement("pin");

            if (pfAuthParams.Mode == MODE_SMS_TWO_WAY_OTP_PLUS_PIN)
            {
                string pinFormat;
                string pinFormatted;
                if (pfAuthParams.Sha1PinHash.Length == 0)
                {
                    pinFormat    = "plainText";
                    pinFormatted = pfAuthParams.Pin;
                }
                else
                {
                    pinFormat    = "sha1";
                    pinFormatted = pfAuthParams.Sha1PinHash;
                }

                pin_element.SetAttribute("pinFormat", pinFormat);
                if (pfAuthParams.Sha1PinHash.Length != 0)
                {
                    pin_element.SetAttribute("sha1Salt", pfAuthParams.Sha1Salt);
                }
                pin_element.SetAttribute("pinChangeRequired", "no");
                pin_element.InnerText = pinFormatted;
            }

            if (sms)
            {
                XmlElement sms_info = doc.CreateElement("smsInfo");
                sms_info.SetAttribute("direction", two_way ? "two-way" : "one-way");
                sms_info.SetAttribute("mode", otp ? "otp" : "otp-pin");
                element           = doc.CreateElement("message");
                element.InnerText = pfAuthParams.SmsText;
                sms_info.AppendChild(element);
                if (pfAuthParams.Mode == MODE_SMS_TWO_WAY_OTP_PLUS_PIN)
                {
                    sms_info.AppendChild(pin_element);
                }
                auth_request.AppendChild(sms_info);
                pin_info.SetAttribute("pinMode", MODE_STANDARD);
            }

            else if (phone_app)
            {
                XmlElement phone_app_auth_info = doc.CreateElement("phoneAppAuthInfo");
                phone_app_auth_info.SetAttribute("mode", "standard");
                XmlElement device_tokens = doc.CreateElement("deviceTokens");
                element = doc.CreateElement("deviceToken");
                if (pfAuthParams.NotificationType.Length > 0)
                {
                    element.SetAttribute("notificationType", pfAuthParams.NotificationType);
                }
                element.InnerText = pfAuthParams.DeviceToken;
                device_tokens.AppendChild(element);
                phone_app_auth_info.AppendChild(device_tokens);
                element           = doc.CreateElement("phoneAppAccountName");
                element.InnerText = pfAuthParams.AccountName;
                phone_app_auth_info.AppendChild(element);

                if (phone_app_pin)
                {
                    element = doc.CreateElement("pin");
                    element.SetAttribute("pinChangeRequired", "0");
                    string pinFormat;
                    string pinFormatted;
                    if (pfAuthParams.Sha1PinHash.Length == 0)
                    {
                        pinFormat    = "plainText";
                        pinFormatted = pfAuthParams.Pin;
                    }
                    else
                    {
                        pinFormat    = "sha1";
                        pinFormatted = pfAuthParams.Sha1PinHash;
                    }
                    element.SetAttribute("pinFormat", pinFormat);
                    if (pfAuthParams.Sha1PinHash.Length != 0)
                    {
                        element.SetAttribute("sha1Salt", pfAuthParams.Sha1Salt);
                    }
                    element.InnerText = pinFormatted;
                    phone_app_auth_info.AppendChild(element);
                }

                XmlElement phone_app_messages = doc.CreateElement("phoneAppMessages");
                element = doc.CreateElement("message");
                element.SetAttribute("type", "authenticateButton");
                element.InnerText = "Authenticate";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "authenticationDenied");
                element.InnerText = "PhoneFactor authentication denied.";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "authenticationSuccessful");
                element.InnerText = "You have successfully authenticated using PhoneFactor.";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "cancelButton");
                element.InnerText = "Cancel";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "closeButton");
                element.InnerText = "Close";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "denyAndReportFraudButton");
                element.InnerText = "Deny and Report Fraud";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "denyButton");
                element.InnerText = "Deny";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "fraudConfirmationNoBlock");
                element.InnerText = "Your company's fraud response team will be notified.";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "fraudConfirmationWithBlock");
                element.InnerText = "Your account will be blocked preventing further authentications and the company's fraud response team will be notified.";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "fraudReportedNoBlock");
                element.InnerText = "Fraud reported.";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "fraudReportedWithBlock");
                element.InnerText = "Fraud reported and account blocked.";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "notification");
                element.InnerText = "You have received a PhoneFactor authentication request.";
                phone_app_messages.AppendChild(element);
                element = doc.CreateElement("message");
                element.SetAttribute("type", "reportFraudButton");
                element.InnerText = "Report Fraud";
                phone_app_messages.AppendChild(element);

                if (pfAuthParams.Mode == MODE_PHONE_APP_STANDARD)
                {
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "standard");
                    element.InnerText = "Tap Authenticate to complete your authentication.";
                }
                else
                {
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "confirmPinField");
                    element.InnerText = "Confirm PIN";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "newPinField");
                    element.InnerText = "New PIN";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pin");
                    element.InnerText = "Enter your PIN and tap Authenticate to complete your authentication.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinAllSameDigits");
                    element.InnerText = "Your PIN cannot contain 3 or more repeating digits.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinExpired");
                    element.InnerText = "Your PIN has expired. Please enter a new PIN to complete your authentication.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinField");
                    element.InnerText = "PIN";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinHistoryDuplicate");
                    element.InnerText = "Your PIN cannot be the same as one of your recently used PINs. Please choose a different PIN.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinLength");
                    element.InnerText = "Your PIN must be a minimum of 4 digits.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinMismatch");
                    element.InnerText = "New PIN and Confirm PIN must match.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinRetry");
                    element.InnerText = "Incorrect PIN. Please try again.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinSequentialDigits");
                    element.InnerText = "Your PIN cannot contain 3 or more sequential digits ascending or descending.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "pinSubsetOfPhone");
                    element.InnerText = "Your PIN cannot contain a 4 digit subset of your phone number or backup phone number.";
                    phone_app_messages.AppendChild(element);
                    element = doc.CreateElement("message");
                    element.SetAttribute("type", "saveButton");
                    element.InnerText = "Save";
                }
                phone_app_auth_info.AppendChild(phone_app_messages);
                auth_request.AppendChild(phone_app_auth_info);
            }
            auth_request.AppendChild(pin_info);
            return(doc.InnerXml);
        }
        /// <summary>
        /// InternalAuthenticate method implementation
        /// </summary>
        private static bool InternalAuthenticate(PhoneFactorParams pfAuthParams, bool asynchronous, out string otp, out int call_status, out int error_id, int timeout = 300)
        {
            if (pfAuthParams.CountryCode.Length == 0)
            {
                pfAuthParams.CountryCode = "1";
            }
            if (pfAuthParams.Hostname.Length == 0)
            {
                pfAuthParams.Hostname = "adfsmfa-hostname";
            }
            if (pfAuthParams.IpAddress.Length == 0)
            {
                pfAuthParams.IpAddress = "255.255.255.255";
            }
            pfAuthParams.Mode = pfAuthParams.Mode.ToLower();
            if (pfAuthParams.Mode != MODE_SMS_TWO_WAY_OTP &&
                pfAuthParams.Mode != MODE_SMS_TWO_WAY_OTP_PLUS_PIN &&
                pfAuthParams.Mode != MODE_SMS_ONE_WAY_OTP &&
                pfAuthParams.Mode != MODE_SMS_ONE_WAY_OTP_PLUS_PIN &&
                pfAuthParams.Mode != MODE_PHONE_APP_STANDARD &&
                pfAuthParams.Mode != MODE_PHONE_APP_PIN)
            {
                pfAuthParams.Mode = MODE_SMS_ONE_WAY_OTP; //  pfAuthParams.Mode = MODE_STANDARD;
            }
            otp         = "";
            call_status = 0;
            error_id    = 0;

            //  pfAuthParams.Pin = "7812";

            string auth_message = CreateMessage(pfAuthParams, asynchronous);

            int tries = 1;

            if (mMutex.WaitOne(timeout * 1000))
            {
                try
                {
                    tries = mTargets.Count + 1;
                }
                finally
                {
                    mMutex.ReleaseMutex();
                }
            }
            for (int i = 0; i < tries; i++)
            {
                string response;
                if (SendMessage(mCurrentTarget, auth_message, out response))
                {
                    string request_id_out = "";
                    bool   authenticated  = ReceiveResponse(response, out request_id_out, out otp, out call_status, out error_id);
                    return(authenticated);
                }
                else
                {
                    if (mMutex.WaitOne(timeout * 1000))
                    {
                        try
                        {
                            mTargets.Enqueue(mCurrentTarget);
                            mCurrentTarget = mTargets.Dequeue().ToString();
                        }
                        finally
                        {
                            mMutex.ReleaseMutex();
                        }
                    }
                }
            }
            return(false);
        }
        /// <summary>
        /// InternalAuthenticate method implementation
        /// </summary>
        private static bool InternalAuthenticate(PhoneFactorParams pfAuthParams, bool asynchronous, out int call_status, out int error_id, int timeout = 300)
        {
            string otp = "";

            return(InternalAuthenticate(pfAuthParams, asynchronous, out otp, out call_status, out error_id, timeout));
        }
 /// <summary>
 /// Authenticate method implementation overload
 /// </summary>
 public static bool Authenticate(PhoneFactorParams pfAuthParams, out int callStatus, out int errorId, int timeout = 300)
 {
     return(InternalAuthenticate(pfAuthParams, false, out callStatus, out errorId, timeout));
 }