コード例 #1
0
        internal static string Login(string username,
                                     byte[] passwordHash,
                                     string deviceId,
                                     IUi ui,
                                     ISecureStorage storage,
                                     RestClient rest)
        {
            // Try simple password login, potentially with a stored second factor token if
            // "remember me" was used before.
            var rememberMeOptions = GetRememberMeOptions(storage);
            var response          = RequestAuthToken(username, passwordHash, deviceId, rememberMeOptions, rest);

            // Simple password login (no 2FA) succeeded
            if (response.AuthToken != null)
            {
                return(response.AuthToken);
            }

            var secondFactor = response.SecondFactor;

            if (secondFactor.Methods == null || secondFactor.Methods.Count == 0)
            {
                throw new InternalErrorException("Expected a non empty list of available 2FA methods");
            }

            // We had a "remember me" token saved, but the login failed anyway. This token is not valid anymore.
            if (rememberMeOptions != null)
            {
                EraseRememberMeToken(storage);
            }

            var      method   = ChooseSecondFactorMethod(secondFactor, ui);
            var      extra    = secondFactor.Methods[method];
            Passcode passcode = null;

            switch (method)
            {
            case Response.SecondFactorMethod.GoogleAuth:
                passcode = ui.ProvideGoogleAuthPasscode();
                break;

            case Response.SecondFactorMethod.Email:
                // When only the email 2FA present, the email is sent by the server right away.
                // Trigger only when other methods are present.
                if (secondFactor.Methods.Count != 1)
                {
                    TriggerEmailMfaPasscode(username, passwordHash, rest);
                }

                passcode = ui.ProvideEmailPasscode((string)extra["Email"] ?? "");
                break;

            case Response.SecondFactorMethod.Duo:
            case Response.SecondFactorMethod.DuoOrg:
            {
                var duo = Duo.Authenticate((string)extra["Host"] ?? "",
                                           (string)extra["Signature"] ?? "",
                                           ui,
                                           rest.Transport);

                if (duo != null)
                {
                    passcode = new Passcode(duo.Passcode, duo.RememberMe);
                }

                break;
            }

            case Response.SecondFactorMethod.YubiKey:
                passcode = ui.ProvideYubiKeyPasscode();
                break;

            case Response.SecondFactorMethod.U2f:
                passcode = AskU2fPasscode(JObject.Parse((string)extra["Challenge"]), ui);
                break;

            default:
                throw new UnsupportedFeatureException($"2FA method {method} is not supported");
            }

            // We're done interacting with the UI
            ui.Close();

            if (passcode == null)
            {
                throw MakeCancelledMfaError();
            }

            var secondFactorResponse = RequestAuthToken(username,
                                                        passwordHash,
                                                        deviceId,
                                                        new SecondFactorOptions(method,
                                                                                passcode.Code,
                                                                                passcode.RememberMe),
                                                        rest);

            // Password + 2FA is successful
            if (secondFactorResponse.AuthToken != null)
            {
                SaveRememberMeToken(secondFactorResponse, storage);
                return(secondFactorResponse.AuthToken);
            }

            throw new BadMultiFactorException("Second factor code is not correct");
        }