One common feature that most of the applications utilize is to allow you to log in with your favorite login provider. We will close this chapter learning how you can leverage the Xamarin.Auth
plugin to connect and authenticate two of the most famous login providers, Facebook and Google, to save your users from registering and using custom login steps, and to save your application from extra work in the backend and frontend tiers.
In Xamarin Studio or Visual Studio, create a new cross-platform project; if you don't remember how to, check the Creating a cross-platform solution recipe in this chapter. Name it
XamFormsAuthenticateProviders
.Add the plugin in the iOS and Android projects, but don't bother with Windows Phone for now.
Right-click the
XamFormsAuthenticateProviders.iOS
components folder, Edit Components, search forXamarin.Auth
, and add it to the project. Do the same for theXamFormsAuthenticateProviders.Droid
project.To be able to authenticate your users with Facebook and Google, you will need to get your own client ID and secret from the corresponding developer consoles. For Facebook, go to https://developers.facebook.com, and for Google, got to https://console.developers.google.com.
The process in every platform to get your credentials is almost the same for each OAuth 2.0 provider: you create a new project and retrieve the keys. Refer to the related platform documentation for more information. The tricky part is to not forget to add the same redirect URLs to allow the application to end somewhere after a successful login.
In the
XamFormsAuthenticateProviders
core project, right-click and choose Add | New Folder; name itModels
.In the newly created folder, add two helper classes:
OAuthSettings
andProviderManager
.Add the following code:
public class OAuthSettings { public string ClientId { get; set; } public string ClientSecret { get; set; } public string AuthorizeUrl { get; set; } public string RedirectUrl { get; set; } public string AccessTokenUrl { get; set; } public List < string > Scopes { get; set; } public string ScopesString { get { return Scopes.Aggregate((current, next) => string.Format("{0}+{1}", current, next)); } } public OAuthSettings() { Scopes = new List < string > (); } } public enum Provider { Unknown = 0, Facebook, Google } public static class ProviderManager { private static OAuthSettings FacebookOAuthSettings { get { return new OAuthSettings { ClientId = "YOUR_CLIENT_ID", ClientSecret = "YOUR_CLIENT_SECRET", AuthorizeUrl = "https://m.facebook.com/dialog/oauth/", RedirectUrl = "http://www.facebook.com/connect/login_success.html", AccessTokenUrl = "https://graph.facebook.com/v2.3/oauth/access_token", Scopes = new List < string > { "" } }; } } private static OAuthSettings GoogleOAuthSettings { get { return new OAuthSettings { ClientId = " YOUR_CLIENT_ID ", ClientSecret = " YOUR_CLIENT_ID ", AuthorizeUrl = "https://accounts.google.com/o/oauth2/auth", RedirectUrl = "https://www.googleapis.com/plus/v1/people/me", AccessTokenUrl = "https://accounts.google.com/o/oauth2/token", Scopes = new List < string > { "openid" } }; } } public static OAuthSettings GetProviderOAuthSettings(Provider provider) { switch (provider) { case Provider.Facebook: { return FacebookOAuthSettings; } case Provider.Google: { return GoogleOAuthSettings; } } return new OAuthSettings(); } }
Right-click on the core XamFormsAuthenticate project and Add | New Folder; name it Custom Pages.
Right-click on the newly created folder and add two classes,
LoginPage
andProvidersAuthPage
, and then write or paste-replace the file's contents with the following code:public class LoginPage : ContentPage { public OAuthSettings ProviderOAuthSettings { get; set; } public LoginPage (Provider provider) { ProviderOAuthSettings = ProviderManager.GetProviderOAuthSettings (provider); } } public class ProvidersAuthPage : ContentPage { StackLayout stackLayout; Button facebookButton; Button googleButton; public ProvidersAuthPage () { facebookButton = new Button { Text = "Facebook" }; facebookButton.Clicked += async (sender, e) => await Navigation.PushModalAsync(new LoginPage(Provider.Facebook)); googleButton = new Button { Text = "Google" }; googleButton.Clicked += async (sender, e) => await Navigation.PushModalAsync(new LoginPage(Provider.Google)); stackLayout = new StackLayout { VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center, Orientation = StackOrientation.Vertical, Spacing = 10, Children = { facebookButton, googleButton } }; this.Content = stackLayout; } }
In the App constructor of the
App.cs XamFormsAuthenticate
project file, add the following to set the start page:MainPage = new ProvidersAuthPage();
Now we need to add the authentication code flow, but if you noticed when we added the Xamarin.Auth
components in step 3, there are two different implementations: one for each platform. To accomplish our goal and run specific platform code for each project, we will use the PageRenderer
class and an attribute that under the covers uses the Xamarin.Forms DependencyService
. You can find more in-depth recipes in this book regarding these topics in Chapter 2, Declare Once, Visualize Everywhere and Chapter 4, Different Cars, Same Engine.
Create a new folder, name it
Platform Specific
, and add a new class file nameLoginPageRenderer
. Make it aPageRenderer
subclass and add the following code:LoginPage page; bool loginInProgress; protected override void OnElementChanged(ElementChangedEventArgs < Page > e) { base.OnElementChanged(e); if (e.OldElement != null || Element == null) return; page = e.NewElement as LoginPage; if (page == null || loginInProgress) return; loginInProgress = true; try { // your OAuth2 client id OAuth2Authenticator auth = new OAuth2Authenticator(page.ProviderOAuthSettings.ClientId, // your OAuth2 client secret page.ProviderOAuthSettings.ClientSecret, // scopes page.ProviderOAuthSettings.ScopesString, //the scopes, delimited by the "+" symbol new Uri(page.ProviderOAuthSettings.AuthorizeUrl), // the redirect URL for the service new Uri(page.ProviderOAuthSettings.RedirectUrl), new Uri(page.ProviderOAuthSettings.AccessTokenUrl)); auth.AllowCancel = true; auth.Completed += async(sender, args) => { // Do something… await page.Navigation.PopAsync(); loginInProgress = false; }; auth.Error += (sender, args) => { Console.WriteLine("Authentication Error: {0}", args.Exception); }; var activity = Xamarin.Forms.Forms.Context as Activity; activity.StartActivity(auth.GetUI(Xamarin.Forms.Forms.Context)); } catch (Exception ex) { Console.WriteLine(ex); } }
We need some using statements to make all this and also a decoration attribute on the namespace.
using XamFormsAuthenticateProviders; using XamFormsAuthenticateProviders.Droid; using Xamarin.Forms.Platform.Android; using Xamarin.Auth; using Android.App; [assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))] namespace XamFormsAuthenticateProviders.Droid
The same applies here as with the Android platform: create a folder and a new class file named
LoginPageRenderer
, and make it a subclass ofPageRenderer
.This time, we need iOS-related code; you can find it in the following excerpt:
LoginPage page; bool loginInProgress; protected override void OnElementChanged(VisualElementChangedEventArgs e) { base.OnElementChanged(e); if (e.OldElement != null || Element == null) return; page = e.NewElement as LoginPage; } public override async void ViewDidAppear(bool animated) { base.ViewDidAppear(animated); if (page == null || loginInProgress) return; loginInProgress = true; try { // your OAuth2 client id OAuth2Authenticator auth = new OAuth2Authenticator(page.ProviderOAuthSettings.ClientId, // your OAuth2 client secret page.ProviderOAuthSettings.ClientSecret, // scopes page.ProviderOAuthSettings.ScopesString, // the scopes, delimited by the "+" symbol new Uri(page.ProviderOAuthSettings.AuthorizeUrl), // the redirect URL for the service new Uri(page.ProviderOAuthSettings.RedirectUrl), new Uri(page.ProviderOAuthSettings.AccessTokenUrl)); auth.AllowCancel = true; auth.Completed += async(sender, args) => { // Do something… await DismissViewControllerAsync(true); await page.Navigation.PopModalAsync(); loginInProgress = false; }; auth.Error += (sender, args) => { Console.WriteLine("Authentication Error: {0}", args.Exception); }; await PresentViewControllerAsync(auth.GetUI(), true); }catch (Exception ex) { Console.WriteLine(ex); } }
Add the using statements and the
ExportRenderer
attribute on the namespace.using System; using Xamarin.Forms.Platform.iOS; using Xamarin.Forms; using XamFormsAuthenticateProviders; using XamFormsAuthenticateProviders.iOS; using Xamarin.Auth; [assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))] namespace XamFormsAuthenticateProviders.iOS
The Windows Phone platform! Yes, the forgotten one. You must have noticed that there is no Windows Phone platform component in the Xamarin Component Store. Fear not, you have options. The magic answer is relying on the custom platform-specific PageRenderer
that we add for each platform; you can create a platform-specific following the same steps and add your favorite Windows Phone OAuth library logic or you can follow the next steps to try out the experimental alpha channel Xamarin.Auth
NuGet package.
Right-click in the Windows Phone project and add the
Xamarin.Auth
NuGet package. You have to change the channel to Alpha in order to find it.Create a folder named
Platform Specific
and add a class file namedLoginPageRenderer
; subclass it toPageRenderer
.Override the
OnElementChanged
method and add your code orXamarin.Auth
-related code.
Don't forget to check the code available with this book for the complete picture.
That was a big milestone. Of course, it scratches the surface of access tokens, but we leave that to your preferences. Normally, you would get the access token, capture the expiration date, and persist it somewhere.
You just had a taste of the power that Xamarin.Forms can provide you to create not only a unified application with shared code but also customize per platform when necessary. With the use of PageRenderer
, you can completely provide your native views and pages.
The first thing we did after we created our solution is to go to the developer console of Facebook and Google, set up a new project, enable the OAuth2 system, retrieve our keys, and add the redirect URL we need to navigate the users after a successful login. In a few words, OAuth and OAuth is an authentication system to verify that this user is actually real and exists in the provider's records. It enables the user to use the same secure credentials from a provider he trusts to give access to the application he is about to use. You can also ask for various scopes and retrieve personal information that he has provided to his trusted provider.
Then we added two helper classes: one to provide us with all the common properties, OAuthSettings
, that every provider needs in order to provide you with the desired access token; and a static class, ProviderManager
, with a static method accepting a provider enumeration and returning an OAuthSettings
instance for the required provider.
Our cross-platform UI consists of two pages: ProvidersAuthPage
, which is our main page and has two buttons where in the Clicked event of each we navigate modally to the LoginPage
passing the desired provider we need to authenticate our user.
In the LoginPage
constructor, we use the provider value to retrieve an instance of OAuthSettings
and set to a public property. That's it, everything else is handled in our platform-specific renderers.
Every PageRenderer
we added has platform-specific functionality and is essentially our Xamarin.Forms page; so essentially in iOS, a UIViewController
; in Android is almost like an Activity, but the lifecycle is a little bit different; and for Windows Phone, the same applies as a PhoneApplicationPage
under the hood. There are more renderers that correspond to the type of page you want.
We also decorate our renderers namespace with the ExportRenderer
attribute. With this, Xamarin.Forms knows which implementation to load in runtime.
For every PageRenderer
, we override OnElementChanged
and get the actual Xamarin.Forms page. For iOS, we use the ViewDidAppear
iOS-specific method, but for Android we don't have any specific lifecycle methods so we write all our authentication code in the OnElementChanged
method.
We capture if the login is in progress in case we dismiss the modal page and OnElementChanged
is invoked again to avoid showing again the authentication page.
The Xamarin.Auth
plugin has an OAuth2Authenticator
class that we instantiate with the information from our OAuthSettings
instance that we have in our Xamarin.Forms page ProviderOAuthSettings
property that we have access in our renderer. The best of both worlds!
Register the completed event that will be invoked in success and in cancel cases and the error event that will notify us if any error is occurred. With the Auth.GetUI()
method, we get a platform-specific object that we can use to show our authentication view. Last but not least, in the Completed event we close the authentication view that we presented on each platform.
Chapter 2, Declare Once, Visualize Everywhere
Chapter 4, Different Cars, Same Engine
https://developer.xamarin.com/guides/cross-platform/xamarin-forms/custom-renderer/