Este post debe ser usado como guía, como sabrás, en ningún momento es buena idea dejar poner en tu código cualquier tipo de llave que no quieres que sea pública (como los AppSecrets o los Tokens). Te recomiendo visitar estos elnaces: How to hide your key y The Ultimate Guide to Mobile API Security para mayor información.
Es un hecho que las redes sociales están dominando el mundo, y que tu app tenga conexión con alguna (o varias) de ellas es una característica que tal vez tus usuarios apreciarían en ella. Sin embargo, para acceder a ellas es necesario contar con el permiso de los usuarios, ¿cierto?

Para nuestra suerte, muchos servicios proporcionan una forma rápida y estandarizada de solicitar dicho permiso de ellos y es través de OAuth. Sin embargo, el proceso que ocurre despues de que los usuarios nos han dado permiso no es tan sencillo como parece, para nuestra suerte, Xamarin.Auth nos ofrece una forma de ejecutar este proceso de manera sencilla y en unas cuantas líneas de código.

En este post, trataré de explicar cómo usar Xamarin.Auth para conectarse con GitHub, Facebook y Twitter.

Registrando aplicaciones en servicios

Primero que nada, tenemos que registrar nuestras aplicaciones en los servicios de terceros:

GitHub

Lo primero que tienes que hacer es darte de alta en el programa de desarrolladores de GitHub en este enlace y una vez que lo hayas hecho, debes dirigirte a este otro enlace en donde podrás registrar tu nueva app, en la pantalla de registro debes poner los datos de tu app, pero debes colocar https://github.com en el campo "Authorization callback URL":

Una vez que has completado estos pasos, tu aplicación estará registrada y lista para usarse, una vez creada te aparecerán dos valores que son muy importantes y que debes guardar con mucho cuidado:

Facebook

Para Facebook la cosa no es tan diferente, debes acceder a https://developers.facebook.com y registrarte como desarrollador para después registrar tu aplicación en el mismo sitio. Ahora, después de elegir un nombre para nuestra aplicación, de proporcionar un correo electrónico para contacto y resolver un captcha, nos aparecerá una pantalla con un menú como el siguiente y en el cual debemos dar click en "Add product" y seleccionar "Facebook Login":

Una vez que hayamos hecho esto, del lado izquierdo, aparecerá un nuevo ítem en el menú llamado, justamente, "Facebook Login" con dos opciones debajo, damos click en "Settings", después de esto aparecerá un formulario, en donde lo que tenemos que modificar es el campo "Valid OAuth redirect URIs", en donde debes colocar lo siguiente: https://www.facebook.com/connect/login_success.html:

Guardamos los cambios y ahora, en el menú de la izquierda damos click en "Dashboard", que nos llevará a la pantalla inicial de nuestra app, de donde debemos copiar el valor de "App ID" y listo, hemos terminado de registrar nuestra app en Facebook.

Twitter

Básicamente, ahora que has registrado tu app en GitHub y Facebook, sabrás más o menos qué esperar con Twitter. Primero, tienes que iniciar el registro de tu app en https://apps.twitter.com, de nuevo, debemos ingresar detalles sobre la app y, muy importante, en el campo "Callback URL" debemos colocar https://mobile.twitter.com/home. Una vez dada de alta, tendremos acceso a una "Consumer Key" y a un "Consumer Secret", este último debe permanecer siempre secreto:

Bien, hemos terminado el papeleo, ahora, a escribir código:

En el proyecto de Forms

Comenzaremos por crear una interfaz que especifique algunas de las propiedades comunes a la hora de realizar la autenticación:

public interface IKeys
{
    string ClientId { get; }
    string ClientSecret { get; }
    string Scope { get; }
    string AuthorizeUrl { get; }
    string RedirectUrl { get; }
    string RequestTokenUrl { get; }
    string AccessTokenUrl { get; }
    string ConsumerSecret { get; }
    string ConsumerKey { get; }
    string CallbackUrl { get; }
}

Ahora es cuando tenemos que usar los valores obtenidos en la fase de "El Papeleo", creamos una clase por cada servicio conectado, fijate cómo es que las clases implementan la interfaz IKey:

public class FacebookKeys : IKeys
{
    public string ClientId { get; } = "XXXXXXXXX"; // El valor de App ID en el Dashobard
    public string AuthorizeUrl { get; } = "https://m.facebook.com/dialog/oauth/";
    public string RedirectUrl { get; } = "https://www.facebook.com/connect/login_success.html";
    // ...

public class GitHubKeys : IKeys
{
    public string ClientId => "XXXXXXXXXX"; // El valor de Client Id de GitHub
    public string ClientSecret => "XXXXXXXXXXXXXXXX"; // El valor de Client Secret de GitHub
    public string AccessTokenUrl { get; } = "https://github.com/login/oauth/access_token";
    public string AuthorizeUrl { get; } = "https://github.com/login/oauth/authorize";
    public string RedirectUrl { get; } = "https://github.com";
    // ...

public class TwitterKeys : IKeys
{
    public string ConsumerKey { get; } = "EnPK4XXXXXXXtrwXXXXXXXEm"; // Consumer Key de Twitter
    public string ConsumerSecret => "FhSHfXXXXXXXXyu0ZqXXXXXXXXXXXXhbFcr8ETG2p"; // Consumer Secret de Twitter
    public string AccessTokenUrl { get; } = "https://api.twitter.com/oauth/access_token";
    public string AuthorizeUrl { get; } = "https://api.twitter.com/oauth/authorize";
    public string RedirectUrl { get; } = "https://mobile.twitter.com/home";
    public string RequestTokenUrl => "https://api.twitter.com/oauth/request_token";
    public string CallbackUrl => "https://mobile.twitter.com/home";
    //

Además de un tipo enum que represente a los servicios que podemos usar, usamos un enum para evitar usar cadenas mágicas en el código:

public enum Services
{
    GitHub,
    Facebook,
    Twitter
}

Dado que Xamarin.Auth hace uso de APIs nativas de cada platforma, sus métodos son únicamente accesibles en los proyectos "cliente" y no desde el núcleo de nuestra aplicación, es por eso que para interactuar con ella indepentiéntemente de la plataforma, necesitamos hacer uso de una interfaz:

public interface IAccountManagerService
{
    List<Services> Accounts { get; }

    string GetPropertyFromAccount(Services service, string property);

    void EraseAll();
}

La pantalla de autorización es una nueva Activity o un nuevo UIViewController, dependiendo de la plataforma, y como desde Forms no tenemos acceso a estas clases, de nuevo creamos una solución independiente de la plataforma. En este caso lo haremos a través de un Custom renderer de una ContentPage:

public class AuthorizePage : ContentPage
{
    public Services Service { get; private set; }

    public AuthorizePage(Services service)
    {
        Service = service;
    }
}

Como puedes ver, tiene una propiedad llamada Service del tipo Service, a través de la cual especificaremos a cuál de los servicios nos vamos a conectar. Más adelante demostraré el uso de este tipo de páginas.

En Xamarin.iOS y Xamarin.Android

Una vez terminado el código multiplataforma, vamos a trabajar con los proyectos de iOS y Android; como las diferencias entre un código y otro son mínimas, podemos utilizar un proyecto compartido y usar directivas de preprocesador para compilar determinadas porciones de código de acuerdo al proyecto que estamos ejecutando.

Comenzaremos por implementar la interfaz IAccountManagerService a través de la que interactuaremos con el servicio de gestión de cuentas de Xamarin.Auth:

using System;
using System.Linq;
using System.Collections.Generic;
using Authy.AccountManagement;

#if __IOS/
namespace Authy.iOS.AccountManagement
#elif __ANDROID/
namespace Authy.Droid.AccountManagement
#endif
{
	public class AccountManagementImplementation : IAccountManagerService
	{
		public List<Services> Accounts
		{
			get
			{
				var accounts = new List<Services>();
				var accountStore = Xamarin.Auth.AccountStore.Create();
				foreach (var service in Enum.GetNames(typeof(Services)))
				{
					var acc = accountStore.FindAccountsForService(service).FirstOrDefault();
					if (acc != null)
					{
						accounts.Add((Services)Enum.Parse(typeof(Services), service));
					}
				}
				return accounts;
			}
		}

		public void EraseAll()
		{
			var accountStore = Xamarin.Auth.AccountStore.Create();
			foreach (var service in Enum.GetNames(typeof(Services)))
			{
				var acc = accountStore.FindAccountsForService(service).FirstOrDefault();
				if (acc != null)
				{
					accountStore.Delete(acc, service);
				}
			}
		}

		public string GetPropertyFromAccount(Services service, string property)
		{
			var accountStore = Xamarin.Auth.AccountStore.Create();
			var account = accountStore.FindAccountsForService(service.ToString()).FirstOrDefault();
			if (account != null)
			{
				return account.Properties[property];
			}
			return null;
		}
	}
}

Para esta ocasión estaremos usando otra de las herramientas de XF, el DependencyService, así que no te olvides de colocar la siguiente línea en tu archivo:

[assembly: Xamarin.Forms.Dependency(typeof(AccountsImplementation))]

Después, es momento de implementar el custom renderer de la página con la que vamos a solicitar la autorización:

using System;
using Authy.AccountManagement;
using Xamarin.Forms;
using Xamarin.Auth;
#if __IOS/
using Xamarin.Forms.Platform.iOS;
#elif __ANDROID/
using Xamarin.Forms.Platform.Android;
#endif

#if __IOS/
[assembly: ExportRenderer(typeof(AuthorizePage), typeof(Authy.iOS.AccountManagement.AuthorizePageRenderer))]
namespace Authy.iOS.AccountManagement
#elif __ANDROID/
[assembly: ExportRenderer(typeof(AuthorizePage), typeof(Authy.Droid.AccountManagement.AuthorizePageRenderer))]
namespace Authy.Droid.AccountManagement
#endif
{
	public class AuthorizePageRenderer : PageRenderer
	{

#if __IOS/
		public override void ViewDidAppear(bool animated)
		{
			base.ViewDidAppear(animated);
#elif __ANDROID/
		protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
		{
			base.OnElementChanged(e);
			var activity = this.Context as Android.App.Activity;
#endif


			OAuth2Authenticator auth2 = null;
			OAuth1Authenticator auth1 = null;
			IKeys keys = null;

			var authPage = Element as AuthorizePage;

			switch (authPage.Service)
			{
				case Services.Facebook:
					keys = new FacebookKeys();
					auth2 = new OAuth2Authenticator(
						clientId: keys.ClientId,
						scope: keys.Scope,
						authorizeUrl: new Uri(keys.AuthorizeUrl),
						redirectUrl: new Uri(keys.RedirectUrl));
					break;
				case Services.Twitter:
					keys = new TwitterKeys();
					auth1 = new OAuth1Authenticator(
							consumerKey: keys.ConsumerKey,
							consumerSecret: keys.ConsumerSecret,
							requestTokenUrl: new Uri(keys.RequestTokenUrl),
							authorizeUrl: new Uri(keys.AuthorizeUrl),
							accessTokenUrl: new Uri(keys.AccessTokenUrl),
							callbackUrl: new Uri(keys.CallbackUrl));
					break;
				case Services.GitHub:
					keys = new GitHubKeys();
					auth2 = new OAuth2Authenticator(
						clientId: keys.ClientId,
						clientSecret: keys.ClientSecret,
						scope: keys.Scope,
						authorizeUrl: new Uri(keys.AuthorizeUrl),
						redirectUrl: new Uri(keys.RedirectUrl),
						accessTokenUrl: new Uri(keys.AccessTokenUrl));
					break;
				default:
					throw new Exception("Service " + authPage.Service + " not yet supported");
			}

			if (auth2 != null)
			{
				auth2.Completed += async (sender, eventArgs) =>
				{
#if __IOS/
					DismissViewController(true, null);
#endif
					if (eventArgs.IsAuthenticated)
						AccountStore.Create().Save(eventArgs.Account, authPage.Service.ToString());
					await authPage.Navigation.PopAsync();
				};
#if __IOS/
				PresentViewController(auth2.GetUI(), true, null);
#elif __ANDROID/
				activity.StartActivity(auth2.GetUI(activity));
#endif
			}

			if (auth1 != null)
			{
				auth1.Completed += async (sender, eventArgs) =>
				{
#if __IOS/
					DismissViewController(true, null);
#endif
					if (eventArgs.IsAuthenticated)
						AccountStore.Create().Save(eventArgs.Account, authPage.Service.ToString());
					await authPage.Navigation.PopAsync();
				};
			
#if __IOS/
				PresentViewController(auth1.GetUI(), true, null);
#elif __ANDROID/
				activity.StartActivity(auth1.GetUI(activity));
#endif
			}

		}
	}

}

Lo que el código anterior hace es declarar dos formas de obtener la autorización (OAuth2Authenticator y OAuth2Authenticator), también delcara una variable del tipo IKeys.

Luego obtiene una referencia a la página de Xamarin.Forms desde la que estamos llamándola (para saber a qué servicio queremos conectarnos), con esta información comienza a crear la petición adecuada de acuerdo al servicio que solicitamos. Es importante que veas que dentro de cada una de las opciones de la instrucción switch, se instancia la clase que contiene las llaves del servicio que vamos a usar.

Por último, y después de que se ha creado correctamente una instancia de OAuth2Authenticator o de OAuth1Authenticator, asigna un manejador al evento Completed, para que si se obtuvo el permiso requerido lo almacene en el dispositivo mediante la llamada a AccountStore.Create().Save(). Luego de todo esto, podemos mostrar la pantalla al usuario. En android mediante el método StartActivity, mientras que en iOS mediante PresentViewController.

De este modo, cada vez que naveguemos hacia una página del tipo AuthorizePage, se mostrará la interfaz de autorización solicitada, de acuerdo al valor pasado en el constructor de la clase.

Ese es todo el código que necesitas para los proyectos "cliente". Ahora, de vuelta al proyecto de Forms:

De vuelta en Xamarin.Forms

Cuando solicitemos permiso para usar determinado servicio, lo único que debemos hacer es crear una nueva página en Forms y navegar hacia ella:

await Navigation.PushAsync(new AuthorizePage(Services.Facebook));

Para corroborar si el usuario ha otorgado permiso debemos obtener las cuentas que tenemos guardadas en el dispositivo usando la implementación de la clase IAccountManagerService del servicio de dependencias:

_services = DependencyService.Get<IAccountManagerService>();

Y luego revisar la propiedad Accounts buscando si existe el servicio que deseamos:

var accounts = _services.Accounts;
if (accounts.Contains(Services.Facebook))
{ // ...

Si sabemos que el usuario nos ha autorizado, podemos acceder a los tokens, claves y demás información de permisos mediante el método GetPropertyFromAccount:

var screen_name = _services.GetPropertyFromAccount(Services.Twitter, "screen_name");
var oauth_consumer_key = _services.GetPropertyFromAccount(Services.Twitter, "oauth_consumer_key");
var oauth_token_secret = _services.GetPropertyFromAccount(Services.Twitter, "oauth_token_secret");

Demo

Puedes ver el vídeo de la app que usa el código explicado en el código anterior:

Para finalizar

Como puedes ver no es tan complicado (espero me haya explicado bien), y añadir otros servicios es tan simple como conseguir las llaves, modificar unas cuantas clases y ¡listo! Tu app se puede conectar con el resto del internet.

No olvides que me puedes preguntar dudas por correo o por Twitter, ah... y descargar el código fuente.