October 18, 2018

The Xamarin.Auth Android CustomTabs Debacle

The back-story

So, you want to implement OAuth in your app and you decide to use a well-known library for obvious reasons. You do a bit of Googling, and come across Xamarin.Auth. Great! So we open our solution and add the nuget package, follow the recommended guidelines and away we go… Right? Yes, well, until you realise that the CustomTabs don’t close after authenticating. So then you do a deep-dive into the various posts on Stack Overflow and come up empty. Until, you reach the Github issues board. As of the time of this post, the repo has seen no action in the last 8 months! With a clear regression issue in the latest version around the closing of Custom Tabs, users were forced to revert back to v1.5.0.3

This brings a new set of minor issues with it – especially if you are using .NET Standard (as you should be at this point in 2018). The last working version has no .NET Standard target, and will automatically target .NET 4.6.1. That’s fine. We can live with this.

We will also need to install PCLCrypto, as this is a dependency in this version. I am guessing they needed cryptographic features that weren’t available in PCL out of the box.

*It should be noted that the current latest (broken) version is 1.6.0.2 at the time of writing this post

tl;dr

  • Install Xamarin.Auth v1.5.0.3 (Yes, this is the last known working version at the time of this post)
  • Install PCLCrypto (latest version is fine)
  • Do not update until the Xamarin.Auth repo shows signs of life again (I am trying to find the time to make a PR into the repo)

Now, there are a lot of conflicting code snippets floating around as to how the Android implementation should be. Here’s mine.

Firstly, we want to get rid of the pesky CustomTabs toast message. Insert this into your LoginActivity’s OnCreate (or your Service if you’re using DI)

CustomTabsConfiguration.CustomTabsClosingMessage = null;

Next, we’ll want to instantiate the OAuth2Authenticator, like so: (take note that Authenticator is a property on my Singleton AuthService. This is because it will need to be accessed by various parts of the application, and I want to be sure I’m acting on the one and only instance of it)

Authenticator = new OAuth2Authenticator(
                Config.OAuthGmailClientId,
                null,
                Config.OAuthGmailScope,
                new Uri(Config.OAuthGmailAuthUrl),
                new Uri($"{Config.AndroidFilterDataScheme}:/oauth2redirect"),
                new Uri(Config.OAuthGmailAccessTokenUrl),
                null,
                true);

Subscribe to the OAuth2Authenticator’s events

Authenticator.Completed += OnAuthCompleted;
Authenticator.Error += OnAuthError;
private void OnAuthCompleted(object sender, AuthenticatorCompletedEventArgs e)
        {
            if (e.IsAuthenticated)
            {
                // Save the account
                AccountStore.Create().Save(e.Account, Config.AppName);

                // Grab the token
                var token = e.Account.Properties["access_token"];

                // Navigate to the secure part of your app - code redacted as I am using MvvmCross
            }
        }

Add your Authentication action (This will actually kick off the OAuth process)

public void Authenticate()
        {
            var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
            presenter.Login(Authenticator);
        }

Finally, you will need to add a custom Url interceptor, so add a new activity, and make sure it looks like this:

[Activity(Label = "CustomUrlSchemeInterceptorActivity", NoHistory = true, LaunchMode = LaunchMode.SingleTask)]
    [IntentFilter(
    new[] { Intent.ActionView },
    Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
    DataSchemes = new[] { Config.AndroidFilterDataScheme },
    DataPath = "/oauth2redirect")]
    public class CustomUrlSchemeInterceptorActivity : Activity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Here, I access my Singleton instace of the Authenticator - you can modify as you see fit.
            // We call OnPageLoading with the .NET  converted URI
            Mvx.IoCProvider.Resolve<IAuthService>().Authenticator.OnPageLoading(new Uri(Intent.Data.ToString()));

            var intent = new Intent(this, typeof(LoginView));
            intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
            StartActivity(intent);

            Finish();
            return;
        }
    }

And that’s it! Let me know if I missed something, or if you see that the Xamarin.Auth repo has come back from the dead.

-KG

You may also like...

Leave a Reply