Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
90 views
in Technique[技术] by (71.8m points)

How to log in to Facebook in Xamarin.Forms

I want to make a Xamarin.Forms project, targeting iOS, Android and Windows Phone.

My app needs to authenticate users using Facebook.

Should I implement login for each platform independently, or use a manual flow? https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.0

I prefer to have a single implementation of the login flow, and use it on all platforms.

How can I get a single implementaion of the Facebook login flow?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

UPDATE (10/24/17): While this way of doing things was okay a few years ago, I now strongly advocate for using native UI for doing authentication, as opposed to the webview method shown here. Auth0 is a great way to accomplish native UI login for your apps, using a wide variety of identity providers: https://auth0.com/docs/quickstart/native/xamarin

EDIT: I finally put a sample for this on Gihub

I posted an answer on the Xamarin Forums. I'll repeat it here.

Let's start with the core of the app, the Xamarin.Forms PCL project. Your App class will look something like this:

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();
                });
            }
        }
    }
}

The first thing to notice is the GetMainPage() method. This tells the app which screen it should load first upon launching.

We also have a simple property and method for storing the Token that is returned from the auth service, as well as a simple IsLoggedIn property.

There's an Action property as well; something I stuck in here in order to have a way for the platform implementations to perform a Xamarin.Forms navigation action. More on this later.

You'll also notice some red in your IDE because we haven't created the ProfilePage class yet. So, let's do that.

Create a very simple ProfilePage class in the Xamarin.Forms PCL project. We're not even going to do anything fancy with it because that will depend on your particular need. For the sake of simplicity in this sample, it will contain a single label:

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

Again, you'll probably have some red in your IDE because we seem to be missing the BaseContentPage class. The sole purpose of the BaseContentPage class is to ensure that none of the app's screens can be displayed until the user has logged in. (In this simplified demo, we're just persisting the user info to memory, so you'll need to re-login every time the app is run. In a real-world app, you'd be storing the authenticated user info to the device's keychain, which would eliminate the need to login at each app start.)

Create a BaseContentPage class in the Xamarin.Forms PCL project:

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

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

There's a few interesting things going on here:

  1. We're overriding the OnAppearing() method, which is similar to the ViewWillAppear method in an iOS UIViewController. You can execute any code here that you'd like to have run immediately before the screen appears.

  2. The only thing we're doing in this method is checking to see if the user is logged in. If they're not, then we perform a modal push to a class called LoginPage. If you're unfamiliar with the concept of a modal, it's simply a view that takes the user out of the normal application flow in order to perform some special task; in our case, to perform a login.

So, let's create the LoginPage class in the Xamarin.Forms PCL project:

namespace OAuth2Demo.XForms
{
    public class LoginPage : ContentPage
    {

    }
}

Wait...why doesn't this class have a body???

Since we're using the Xamatin.Auth component (which does the job of building and presenting a web view that works with the provided OAuth2 info), we actually don't want any kind of implementation in our LoginPage class. I know that seems weird, but bear with me.

The LoginPageRenderer for iOS

Up until this point, we've been working solely in the Xamarin.Forms PCL project. But now we need to provide the platform-specific implementation of our LoginPage in the iOS project. This is where the concept of a Renderer comes in.

In Xamarin.Forms, when you want to provide platform-specific screens and controls (i.e. screens that do not derive their content from the abstract pages in the Xamarin.Forms PCL project), you do so with Renderers.

Create a LoginPageRenderer class in your iOS platform project:

[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's 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);
            }
        }
    }
}

There are important things to note:

  1. The [assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))] line at the top (and importantly before the namespace declaration) is using the Xamarin.Forms DependencyService. It's not the most beautiful thing in the world because it's not IoC/DI, but whatever...it works. This is the mechanism that "maps" our LoginPageRenderer to the LoginPage.

  2. This is the class in which we're actually using the Xamarin.Auth component. That's where the OAuth2Authenticator reference comes from.

  3. Once the login is successful, we fire off a Xamarin.Forms navigation via App.SuccessfulLoginAction.Invoke();. This gets us back to the ProfilePage.

  4. Since we're on iOS, we're doing all of our logic sinde of the ViewDidAppear() method.

The LoginPageRenderer for Android

Create a LoginPageRenderer class in your Android platform project. (Note that class name you're creating is identical to the one in the iOS project, but here in the Android project the PageRenderer inherits from Android classes instead of iOS classes.)

[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));
        }
    }
}

Again, let's take a look at some interesting things:

  1. The [assembly: ExportRenderer (typeof (LoginPage), typeof (LoginPageRenderer))] line at the top (and importantly before the namespace declaration) is using the Xamarin.Forms DependencyService. No difference here from the iOS version of LoginPageRenderer.

  2. Again, this is where we're actually using the Xamarin.Auth component. That's where the OAuth2Authenticator reference comes from.

  3. Just as with the iOS version, once the login is successful, we fire off a Xamarin.Forms navigation via App.SuccessfulLoginAction.Invoke();. This gets us back to the ProfilePage.

  4. Unlike the iOS version, we're doing all of the logic inside of the OnModelChanged() method instead of the ViewDidAppear().

Here it is on iOS:

Xamarin.Auth with Xamarin.Forms iOS example

...and Android:

<img src="https://www.joesauve.com/content/images/2014/Jun/Xamarin-Auth_Xamarin-Forms_example_Android.gif" alt="Xamarin.Auth with Xam


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...