Example #1
0
        // Returns the second factor token from Duo or null when canceled by the user.
        public static Result Authenticate(string host, string signature, IDuoUi ui, IRestTransport transport)
        {
            var rest = new RestClient(transport, $"https://{host}");

            var(tx, app) = ParseSignature(signature);
            var html = DownloadFrame(tx, rest);

            var(sid, devices) = ParseFrame(html);

            while (true)
            {
                // Ask the user to choose what to do
                var choice = ui.ChooseDuoFactor(devices);
                if (choice == null)
                {
                    return(null); // Canceled by user
                }
                // SMS is a special case: it doesn't submit any codes, it rather tells the server to send
                // a new batch of passcodes to the phone via SMS.
                if (choice.Factor == DuoFactor.SendPasscodesBySms)
                {
                    SubmitFactor(sid, choice, "", rest);
                    choice = new DuoChoice(choice.Device, DuoFactor.Passcode, choice.RememberMe);
                }

                // Ask for the passcode
                var passcode = "";
                if (choice.Factor == DuoFactor.Passcode)
                {
                    passcode = ui.ProvideDuoPasscode(choice.Device);
                    if (passcode.IsNullOrEmpty())
                    {
                        return(null); // Canceled by user
                    }
                }

                var token = SubmitFactorAndWaitForToken(sid, choice, passcode, ui, rest);

                // Flow error like an incorrect passcode. The UI has been updated with the error. Keep going.
                if (token.IsNullOrEmpty())
                {
                    continue;
                }

                // All good
                return(new Result($"{token}:{app}", choice.RememberMe));
            }
        }