Xamarin Diaries – OAuth2 with Active Directory

When one of the requirements of our application is to use the OAuth2 protocol in Xamarin, we quickly think of the Xamarin.Auth library. This library comes with an OAuth2Authenticator class that works fine for identity providers such as Google, Facebook and Twitter (among others), but surprisingly it is not flexible enough to adapt to a service like Microsoft’s Active Directory (AD FS). This will probably be solved in the future, but for now, the solution is to overwrite the OAuth2Authenticator class, allowing to include extra parameters required by ADFS.

The solution:

In order to display the AD FS authentication page on the iOS and Android platforms, it will be necessary to create a Renderer and Provider for each specific project. The differences between the two platforms are minimal, so we will show the code for Android and indicate the changes necessary for the solution in 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

With these lines we are creating an instance of the extended class of OAuth2Authenticator and an activity to show the url of the page that will allow us to authenticate in the AD FS.
Another important part of this class is to subscribe to the complete event, which will be thrown when the user authenticates successfully or cancels.

On the iOS platform, we will have to make some changes on this class, as we must change the use of Activity to the iOS view controller (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

In this case there are no differences between platforms, so we can create the class in the Android project and then define a link to this class from the iOS project to avoid having to make changes in duplicate.

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 
}

In this extended class we add the extra parameters needed in AD FS and define the Uri, which we will open from the renderer in order to display the authentication page.

 

Artículos Relacionados

Cross-platform development with Xamarin

Xamarin Diaries – Offline by default

1 thought on “Xamarin Diaries – OAuth2 with Active Directory

  1. I recommend you check out SimpleAuth (https://github.com/Clancey/SimpleAuth). All of that code boils down to 2 lines of code:
    var azureApi = new ADFSApi(“Azure”, azureClientId,
    $”https://login.microsoftonline.com/{azureTennant}/oauth2/authorize”,
    $”https://login.microsoftonline.com/{azureTennant}/oauth2/token”, “”);
    await azureApi.Authenticate();

Leave a Comment

Responsable » Solidgear.
Finalidad » Gestionar los comentarios.
Legitimación » Tu consentimiento.
Destinatarios » Los datos que me facilitas estarán ubicados en los servidores SolidgearGroup dentro de la UE.
Derechos » Podrás ejercer tus derechos, entre otros, a acceder, rectificar, limitar y suprimir tus datos.

By completing the form you agree to the Privacy Policy

This site uses Akismet to reduce spam. Learn how your comment data is processed.