示例#1
0
        // Returns the transaction id
        internal static string SubmitFactor(string sid, DuoChoice choice, string passcode, RestClient rest)
        {
            var parameters = new Dictionary <string, object>
            {
                { "sid", sid },
                { "device", choice.Device.Id },
                { "factor", GetFactorParameterValue(choice.Factor) },
            };

            if (!passcode.IsNullOrEmpty())
            {
                parameters["passcode"] = passcode;
            }

            var response = PostForm <R.SubmitFactor>("frame/prompt", parameters, rest);

            var id = response.TransactionId;

            if (id.IsNullOrEmpty())
            {
                throw MakeInvalidResponseError("Duo: transaction ID (txid) is expected but wasn't found");
            }

            return(id);
        }
示例#2
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));
            }
        }
示例#3
0
        // Returns null when a recoverable flow error (like incorrect code or time out) happened
        // TODO: Don't return null, use something more obvious
        internal static string SubmitFactorAndWaitForToken(string sid,
                                                           DuoChoice choice,
                                                           string passcode,
                                                           IDuoUi ui,
                                                           RestClient rest)
        {
            var txid = SubmitFactor(sid, choice, passcode, rest);

            var url = PollForResultUrl(sid, txid, ui, rest);

            if (url.IsNullOrEmpty())
            {
                return(null);
            }

            return(FetchToken(sid, url, ui, rest));
        }