Cuando uno de los requisitos de nuestra aplicación es usar el protocolo OAuth2 en Xamarin, pensamos rápidamente en la librería Xamarin.Auth. Esta librería viene con una clase OAuth2Authenticator que funciona correctamente en proveedores de identidad como Google, Facebook y Twitter (entre otros), pero sorprendentemente no es suficientemente flexible como para adaptarse a un servicio como el directorio activo de Microsoft (AD FS). Probablemente esto será resuelto en un futuro, pero de momento, la solución consiste en sobrescribir la clase OAuth2Authenticator, permitiendo incluir parámetros extra requeridos por AD FS.

La solución:

Con el objetivo de mostrar la página de autenticación contra AD FS en las plataformas iOS y Android, será necesario crear un Renderer y un Provider en cada uno de los proyectos específicos. Las diferencias entre ambas plataformas son mínimas, por lo que mostraremos el código para Android e indicaremos los cambios necesarios para que la solución funcione también en iOS.

Renderer

LoginPageRenderer.cs

using System; using System.Collections.Generic; using System.Linq; using Android.App; using Xamarin.Forms.Platform.Android; using Xamarin.Forms; using OAuthApp; using Xamarin.Auth; using OAuthApp.Droid; using System.Threading.Tasks; [assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))] namespace OAuthApp.Droid { public class LoginPageRenderer : PageRenderer { IEnumerable accounts; protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); var page = e.NewElement as Page; var activity = this.Context as Activity; var auth = new OAuth2AuthenticatorExtended4ADFS( App.CLIENT_ID, App.CLIENT_SECRET, App.SCOPE, // Scope - not need for ADFS authentication new System.Uri(App.AUTHORIZE_URL), new System.Uri(App.REDIRECT_URL), new System.Uri(App.ACCESSTOKEN_URL), new System.Uri(App.RESOURCE_URL) // ResourceUrl ---> ADFS relying party ); auth.Completed += (sender, eventArgs) => { if (eventArgs.IsAuthenticated) { UpdateAppAccessProperties(); } page.Navigation.PopModalAsync(); }; auth.ShowErrors = false; activity.StartActivity(auth.GetUI(activity)); } private async Task UpdateAppAccessProperties() { await AuthManager.Instance.RecoverUserAsync(); } } // class LoginPageRenderer } // namespace

Con estas líneas estamos creando una instancia de la clase extendida de OAuth2Authenticator y un activity para mostrar la url de la pagina que nos permitirá autenticarnos en el AD FS.

Otra parte importante de esta clase es subscribirnos al evento complete, el cual será lanzado cuando el usuario se autentique correctamente o cancele.

En la plataforma iOS, deberemos realizar unos ligeros cambios sobre esta clase, ya que debemos cambiar el uso de Activity por el controlador de vistas propio de iOS (UIViewController).

using Xamarin.Auth; using Xamarin.Forms.Platform.iOS; using Xamarin.Forms; using OAuthApp; using OAuthApp.iOS.Renderers; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; [assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))] namespace OAuthApp.iOS { public class LoginPageRenderer : PageRenderer { IEnumerable accounts; Page page; public override void ViewDidAppear(bool animated) { base.ViewDidAppear(animated); var auth = new OAuth2AuthenticatorExtended4ADFS( App.CLIENT_ID, App.CLIENT_SECRET, App.SCOPE, // Scope - not need for ADFS authentication new System.Uri(App.AUTHORIZE_URL), new System.Uri(App.REDIRECT_URL), new System.Uri(App.ACCESSTOKEN_URL), new System.Uri(App.RESOURCE_URL) // ResourceUrl ---> ADFS relying party ); auth.Completed += (sender, eventArgs) => { if (eventArgs.IsAuthenticated) { UpdateAppAccessProperties(); } App.Current.MainPage = new NavigationPage(new AuthenticationPage()); }; auth.ShowErrors = false; PresentViewController(auth.GetUI(), true, null); } protected override void OnElementChanged(VisualElementChangedEventArgs e) { base.OnElementChanged(e); page = e.NewElement as Page; } private async Task UpdateAppAccessProperties() { await AuthManager.Instance.RecoverUserAsync(); } } }

Provider

OAuth2AuthenticatorExtended4ADFS.cs

En este caso no hay diferencias entre plataformas, por lo que podemos crear la clase en el proyecto de Android y después definir un link a esta clase desde el proyecto de iOS para evitar tener que hacer cambios por duplicado.

using System; using System.Threading.Tasks; using Xamarin.Auth; // Implementation for OAuth2AuthenticatorExtended4ADFS namespace OAuthApp.Droid { public class OAuth2AuthenticatorExtended4ADFS : OAuth2Authenticator { public Uri Resource { get; private set; } public string ClientId { get; private set; } public Uri AuthorizeUrl { get; private set; } public Uri RedirectUrl { get; private set; } public OAuth2AuthenticatorExtended4ADFS(string clientId, string clientSecret, string scope, Uri authorizeUrl, Uri redirectUrl, Uri accessTokenUrl, Uri resource, GetUsernameAsyncFunc getUsernameAsync = null) : base(clientId, clientSecret, scope, authorizeUrl, redirectUrl, accessTokenUrl, getUsernameAsync) { ClientId = clientId; AuthorizeUrl = authorizeUrl; RedirectUrl = redirectUrl; Resource = resource; } // Constructor public override Task GetInitialUrlAsync() { var url = new Uri(string.Format( "{0}?client_id={1}&resource={2}&redirect_uri={3}&response_type={4}", AuthorizeUrl.AbsoluteUri, Uri.EscapeDataString(ClientId), Uri.EscapeDataString(Resource.AbsoluteUri), Uri.EscapeDataString(RedirectUrl.AbsoluteUri), "code")); var tcs = new TaskCompletionSource(); tcs.SetResult(url); return tcs.Task; } // GetInitialUrlAsync } // class OAuth2AuthenticatorExtended4ADFS }

En esta clase extendida añadimos los parámetros extra necesarios en AD FS y definimos la Uri, la cual abriremos desde el renderer con el objetivo de mostrar la página de autenticación.

Artículos Relacionados