Как войти в Facebook в Xamarin.Forms

Я хочу создать проект Xamarin.Forms, ориентированный на iOS, Android и Windows Phone.

Мое приложение должно аутентифицировать пользователей с помощью Facebook.

Должен ли я осуществлять вход в систему для каждой платформы независимо или использовать ручную обработку? https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.0

Я предпочитаю иметь единую реализацию потока входа в систему и использовать ее на всех платформах.

Как я могу получить единую реализацию потока входа в Facebook?

Ответ 1

Вы можете использовать Xamarin.Social или Xamarin.Auth для этого. Он позволяет использовать один и тот же api независимо от платформы.

На данный момент эти libs еще не PCL, но вы все равно можете их использовать из Project Shared Assets или абстрагировать API, который вам нужен в интерфейсе, и вставлять его с помощью DependencyService или любого другого контейнера DI.

Ответ 2

ОБНОВЛЕНИЕ (24.10.17): Хотя несколько лет назад такой подход был нормальным, сейчас я настоятельно рекомендую использовать собственный пользовательский интерфейс для аутентификации, а не метод веб-просмотра, показанный здесь. Auth0 - отличный способ выполнить вход в пользовательский интерфейс для ваших приложений, используя широкий спектр провайдеров идентификации: https://auth0.com/docs/quickstart/native/xamarin

РЕДАКТИРОВАТЬ: я наконец-то положил образец для этого на Gihub

Я разместил ответ на форумах Xamarin. Я повторю это здесь.

Начнем с ядра приложения, проекта Xamarin.Forms PCL. Ваш класс App будет выглядеть примерно так:

namespace OAuth2Demo.XForms
{
    public class App
    {
        static NavigationPage _NavPage;

        public static Page GetMainPage ()
        {
            var profilePage = new ProfilePage();

            _NavPage = new NavigationPage(profilePage);

            return _NavPage;
        }

        public static bool IsLoggedIn {
            get { return !string.IsNullOrWhiteSpace(_Token); }
        }

        static string _Token;
        public static string Token {
            get { return _Token; }
        }

        public static void SaveToken(string token)
        {
            _Token = token;
        }

        public static Action SuccessfulLoginAction
        {
            get {
                return new Action (() => {
                    _NavPage.Navigation.PopModalAsync();
                });
            }
        }
    }
}

Первое, на что нужно обратить внимание - это GetMainPage(). Это сообщает приложению, какой экран должен быть загружен первым при запуске.

У нас также есть простое свойство и метод для хранения Token который возвращается из службы аутентификации, а также простое свойство IsLoggedIn.

Также есть свойство Action; что-то, что я застрял здесь, чтобы у реализаций платформы был способ выполнить навигационное действие Xamarin.Forms. Подробнее об этом позже.

Вы также заметите красный цвет в вашей IDE, потому что мы еще не создали класс ProfilePage. Итак, давайте сделаем это.

Создайте очень простой класс ProfilePage в проекте Xamarin.Forms PCL. Мы даже не собираемся делать с этим ничего особенного, потому что это будет зависеть от вашей конкретной потребности. Для простоты в этом примере он будет содержать одну метку:

namespace OAuth2Demo.XForms
{
    public class ProfilePage : BaseContentPage
    {
        public ProfilePage ()
        {
            Content = new Label () {
                Text = "Profile Page", 
                VerticalOptions = LayoutOptions.CenterAndExpand,
                HorizontalOptions = LayoutOptions.CenterAndExpand, 
            };
        }
    }
}

Опять же, у вас, вероятно, будет немного красного цвета в вашей IDE, потому что мы, кажется, пропускаем класс BaseContentPage. Единственная цель класса BaseContentPage - обеспечить, чтобы ни один из экранов приложения не отображался до тех пор, пока пользователь не вошел в систему. (В этой упрощенной демонстрации мы просто сохраняем информацию о пользователе в памяти, поэтому вам нужно -login каждый раз, когда приложение запускается. В реальном приложении вы сохраняете информацию аутентифицированного пользователя в цепочке ключей устройства, что устраняет необходимость входа при каждом запуске приложения.)

Создайте класс BaseContentPage в проекте Xamarin.Forms PCL:

namespace OAuth2Demo.XForms
{
    public class BaseContentPage : ContentPage
    {
        protected override void OnAppearing ()
        {
            base.OnAppearing ();

            if (!App.IsLoggedIn) {
                Navigation.PushModalAsync(new LoginPage());
            }
        }
    }
}

Здесь происходит несколько интересных вещей:

  1. Мы переопределяем метод OnAppearing(), который похож на метод ViewWillAppear в iOS UIViewController. Здесь вы можете выполнить любой код, который хотите запустить непосредственно перед появлением экрана.

  2. Единственное, что мы делаем в этом методе, это проверяем, вошел ли пользователь в систему. Если это не так, то мы выполняем модальный LoginPage в класс с именем LoginPage. Если вы не знакомы с концепцией модального режима, это просто представление, которое выводит пользователя из обычного потока приложений для выполнения какой-то специальной задачи; в нашем случае для входа в систему.

Итак, давайте создадим класс LoginPage в проекте Xamarin.Forms PCL:

namespace OAuth2Demo.XForms
{
    public class LoginPage : ContentPage
    {

    }
}

Подожди... почему у этого класса нет тела???

Поскольку мы используем компонент Xamatin.Auth (который выполняет работу по созданию и представлению веб-представления, работающего с предоставленной информацией OAuth2), мы фактически не хотим никакой реализации в нашем классе LoginPage. Я знаю, что это кажется странным, но терпите меня.

LoginPageRenderer для iOS

До этого момента мы работали исключительно в рамках проекта Xamarin.Forms PCL. Но теперь нам нужно предоставить платформо- LoginPage реализацию нашего LoginPage в проекте iOS. Вот тут и приходит понятие рендерера.

В Xamarin.Forms, когда вы хотите предоставить платформо-зависимые экраны и элементы управления (то есть экраны, которые не получают свое содержимое от абстрактных страниц в проекте Xamarin.Forms PCL), вы делаете это с помощью Renderers.

Создайте класс LoginPageRenderer в своем проекте платформы iOS:

[assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))]

namespace OAuth2Demo.XForms.iOS
{
    public class LoginPageRenderer : PageRenderer
    {
        public override void ViewDidAppear (bool animated)
        {
            base.ViewDidAppear (animated);

            var auth = new OAuth2Authenticator (
                clientId: "", // your OAuth2 client id
                scope: "", // the scopes for the particular API you're accessing, delimited by "+" symbols
                authorizeUrl: new Uri (""), // the auth URL for the service
                redirectUrl: new Uri ("")); // the redirect URL for the service

            auth.Completed += (sender, eventArgs) => {
            // We presented the UI, so it up to us to dimiss it on iOS.
            App.SuccessfulLoginAction.Invoke();

            if (eventArgs.IsAuthenticated) {
                // Use eventArgs.Account to do wonderful things
                App.SaveToken(eventArgs.Account.Properties["access_token"]);
            } else {
                // The user cancelled
            }
        };

        PresentViewController (auth.GetUI (), true, null);
            }
        }
    }
}

Есть важные вещи, на которые стоит обратить внимание:

  1. [assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))] вверху (и, что важно, до объявления пространства имен) использует Xamarin.Forms DependencyService. Это не самая красивая вещь в мире, потому что это не IoC/DI, а как угодно... это работает. Это механизм, который "отображает" наш LoginPageRenderer на LoginPage.

  2. Это класс, в котором мы на самом деле используем компонент Xamarin.Auth. OAuth2Authenticator ссылка на OAuth2Authenticator.

  3. После успешного входа в систему мы запускаем навигацию App.SuccessfulLoginAction.Invoke(); через App.SuccessfulLoginAction.Invoke(); , Это возвращает нас к ProfilePage.

  4. Поскольку мы работаем на iOS, мы выполняем все наши логические операции с ViewDidAppear().

LoginPageRenderer для Android

Создайте класс LoginPageRenderer в своем проекте платформы Android. (Обратите внимание, что создаваемое вами имя класса совпадает с именем в проекте iOS, но здесь, в проекте Android, PageRenderer наследует классы Android вместо классов iOS.)

[assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))]

namespace OAuth2Demo.XForms.Android
{
    public class LoginPageRenderer : PageRenderer
    {
        protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
        {
            base.OnModelChanged (oldModel, newModel);

            // this is a ViewGroup - so should be able to load an AXML file and FindView<>
            var activity = this.Context as Activity;

            var auth = new OAuth2Authenticator (
                clientId: "", // your OAuth2 client id
                scope: "", // the scopes for the particular API you're accessing, delimited by "+" symbols
                authorizeUrl: new Uri (""), // the auth URL for the service
                redirectUrl: new Uri ("")); // the redirect URL for the service

            auth.Completed += (sender, eventArgs) => {
            if (eventArgs.IsAuthenticated) {
                App.SuccessfulLoginAction.Invoke();
                // Use eventArgs.Account to do wonderful things
                App.SaveToken(eventArgs.Account.Properties["access_token"]);
            } else {
                // The user cancelled
            }
        };

        activity.StartActivity (auth.GetUI(activity));
        }
    }
}

Опять же, давайте посмотрим на некоторые интересные вещи:

  1. [assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))] вверху (и, что важно, до объявления пространства имен) использует Xamarin.Forms DependencyService. Здесь нет никаких отличий от версии LoginPageRenderer для LoginPageRenderer.

  2. Опять же, именно здесь мы на самом деле используем компонент Xamarin.Auth. OAuth2Authenticator ссылка на OAuth2Authenticator.

  3. Как и в версии для iOS, после успешного входа в систему мы запускаем навигацию App.SuccessfulLoginAction.Invoke(); через App.SuccessfulLoginAction.Invoke(); , Это возвращает нас к ProfilePage.

  4. В отличие от версии для iOS, мы выполняем всю логику внутри метода OnModelChanged() вместо ViewDidAppear().

Вот это на iOS:

Xamarin.Auth with Xamarin.Forms iOS example

... и Android:

Xamarin.Auth with Xamarin.Forms Android example

ОБНОВЛЕНИЕ: я также предоставил подробный образец в своем блоге: http://www.joesauve.com/using-xamarin-auth-with-xamarin-forms/

Ответ 3

Я создал образец проекта, чтобы показать, как создать вход в Facebook, используя собственный компонент Facebook, а не через веб-просмотр, как предлагаемые здесь решения. Вы можете проверить это по этому адресу:

https://github.com/IdoTene/XamarinFormsNativeFacebook

Ответ 4

IOS 8: Для тех, кто использует код @NovaJoe и застрял на виду, добавьте ниже код к обходному пути:

 bool hasShown;

    public override void ViewDidAppear(bool animated)
    {
        if (!hasShown)
        {
            hasShown = true;

            // the rest of @novaJoe code
        }

    }

Ответ 5

Здесь хороший образец проверки подлинности Xamarin.Forms. Документация в коде хорошая. Он использует веб-просмотр для отображения экрана входа в систему, но вы можете выбрать, какой тип входа вы хотите. Он также сохраняет токен пользователей, поэтому ему не нужно повторно регистрироваться.

https://github.com/rlingineni/Xamarin.Forms_Authentication

Ответ 6

Еще одно дополнение к коду @NovaJoe, на iOS8 с Facebook, вам нужно будет изменить класс Renderer, как показано ниже, чтобы закрыть представление после успешной аутентификации.

auth.Completed += (sender, eventArgs) => {
            // We presented the UI, so it up to us to dimiss it on iOS.

/* Импортировать и добавить эту строку */

            DismissViewController (true, null);

/* */

            if (eventArgs.IsAuthenticated) {
                App.Instance.SuccessfulLoginAction.Invoke ();

                // Use eventArgs.Account to do wonderful things
                App.Instance.SaveToken (eventArgs.Account.Properties ["access_token"]);


            } else {
                // The user cancelled
            }
        };

Ответ 7

Правильная реализация для Android PageRenderer:

using System;
using Android.App;
using Android.Content;
using OAuth2Demo.XForms.Android;
using Xamarin.Auth;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using XamarinAuth;

[assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))]

namespace OAuth2Demo.XForms.Android
{
    public class LoginPageRenderer : PageRenderer
    {
        public LoginPageRenderer(Context context) : base(context) { }

        protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
        {
            base.OnElementChanged(e);

            // this is a ViewGroup - so should be able to load an AXML file and FindView<>
            var activity = this.Context as Activity;

            var auth = new OAuth2Authenticator(
                clientId: "<Constants.clientId>", // your OAuth2 client id
                scope: "<Constants.scope>", // the scopes for the particular API you're accessing, delimited by "+" symbols
                authorizeUrl: new Uri("<Constants.authorizeUrl>"), // the auth URL for the service
                redirectUrl: new Uri("<Constants.redirectUrl>")); // the redirect URL for the service

            auth.Completed += (sender, eventArgs) =>
            {
                if (eventArgs.IsAuthenticated)
                {
                    App.SuccessfulLoginAction.Invoke();
                    // Use eventArgs.Account to do wonderful things
                    App.SaveToken(eventArgs.Account.Properties["access_token"]);
                }
                else
                {
                    // The user cancelled
                }
            };

            activity.StartActivity(auth.GetUI(activity));
        }
    }
}