Adding Auth to your Xamarin.Forms App - Part 2

Implementation and Sample Code for Auth in Xamarin


Using the steps outlined in part 1 of the series , we learned that we can log in users with just a browser. If you just want the code, checkout the github

The most basic implementation of OAuth in Xam.Forms utilizes an embedded web browser and reading events from it. The gist is to send the user to the login screen of the 3rd party auth provider, and then listen when the browser URL changes.

Note: First make sure that you register your app with the auth provider you would like to use. Use example.com as the callback url. We’ll use Spotify in this example as our auth provider.

UI Set-Up

We’ll first create a XAML page for the web browser, and set it up so that the code behind listens to the onNavigated event of the browser.

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="EasyFormAuth.LoginPage">
	<ContentPage.Content>
        <StackLayout>
            <StackLayout Orientation="Horizontal" Padding="20,10">
                <Button Text="Cancel" HorizontalOptions="StartAndExpand" />
            </StackLayout>
            <WebView x:Name="Browser" WidthRequest="1000" HeightRequest="1000" />
        </StackLayout>
	</ContentPage.Content>
</ContentPage>

Code Behind

In our code behind, we can begin implementing the steps we outlined in [Part 1]. It really only takes one ContentPage’s worth of work. Jump to the final code.

1. Send the user to the Auth Login Screen

In the codebehind the Xaml, we’ll send the user to the login screen of our Auth Provider on page load. We’ll set that to the webview’s homepage. We’ll use the Spotify API as the auth provider in this example.

Note: I keep all of the secret variables in a static file called Credentials.CS. That way updating those values is easy, and are reusable everywhere.

string URL = String.Format("https://accounts.spotify.com/authorize/?client_id={0}" +
                                   "&response_type=code&state=34fFs29kd0" +
                                   "&redirect_uri={1}" +
                                   "&scope={2}",
                                   Credentials.ClientID,
                                   Credentials.RedirectURI,
                                   Credentials.Scopes);

        public WebAuthView()
        {
            InitializeComponent();
            Browser.Source = URL;
            Browser.Navigated += Browser_Navigated;
        }
2. Listen to the browsers Navigated event

When Spotify redirects to our callback URL, we can change the behavior of our app accordingly.

 async void Browser_Navigated(object sender, WebNavigatedEventArgs e)
 {
       //verify callback URL
       Debug.WriteLine(e.Url);
       //TO-DO: Add code to handle callback
 }
3. Parse the code the from the callback URL

When the URL changes to the correct callback URL, we need to parse the “code” parameter. In our case, we’re listening for a redirect to example.com.

 async void Browser_Navigated(object sender, WebNavigatedEventArgs e)
 {
       	    //verify callback URL
            Debug.WriteLine(e.Url);

            Uri url = new Uri(e.Url);

            if (url.Host.Contains("example.com"))
            {
                //parse the response
                var code = HttpUtility.ParseQueryString(url.Query).Get("code");
                var error = HttpUtility.ParseQueryString(url.Query).Get("error");
                //exchange this for a token
                Debug.WriteLine("Got Code: " + code);

                if (error != null)
                {
                    Debug.WriteLine("Error with logging user in");
                    await DisplayAlert("Uh-oh", "Can't log you in", "Ok");
                    Browser.Source = URL;
                    await Navigation.PopAsync();
                }

		//To-do: Exchange the code for an access token if no error
	}
 }
4. Make a POST request and exchange our Code for an API Access Token

Send a POST request back to the server with the information we got from the browser. I wrote a nice helper class to do this for me, but it really just makes a web request.

I’m using Newtonsoft.JSON to parse the JSON responses from the server.

I wrote a reusable class that you can use in your applications called OAuthHelper that exchanges a code for an access token for you. It should work for any Oauth2 Provider. Save it in a separate file:

public static class OAuth2Helper
    {
        private static readonly HttpClient client = new HttpClient();


        public async static Task<OAuthModel> GetAccessTokenFromCode(string url, string redirect_url, string client_id, string client_secret, string scope_list, string code ){

            var values = new Dictionary<string, string>
            {
               { "client_id", client_id },
               { "client_secret", client_secret },
                {"grant_type","authorization_code"},
                {"scope",scope_list},
                {"redirect_uri", redirect_url},
                {"code",code}
            };

            var content = new FormUrlEncodedContent(values);

            var response = await client.PostAsync(url, content);

            var responseString = await response.Content.ReadAsStringAsync();

            Debug.WriteLine("Reponse: ", responseString);

            var model = JsonConvert.DeserializeObject<OAuthModel>(responseString);

            return model;
        }


    }

    public class OAuthModel{
        [JsonProperty("token_type")]
        public string TokenType { get; set; }

        [JsonProperty("scope")]
        public string Scope { get; set; }

        [JsonProperty("expires_in")]
        public long ExpiresIn { get; set; }

        [JsonProperty("access_token")]
        public string AccessToken { get; set; }

        [JsonProperty("refresh_token")]
        public string RefreshToken { get; set; }

    }

Add this to call to the OAuth Helper class from Browser_Navigated event :

Device.BeginInvokeOnMainThread(async () =>
{
	//Save the RefreshToken and set App AccessToken and LastRefreshedTime
	var postbackURL = "https://accounts.spotify.com/api/token";
	var tokens = await OAuth2Helper.GetAccessTokenFromCode(postbackURL,
                           Credentials.RedirectURI,
                           Credentials.ClientID,
                           Credentials.ClientSecret,
                           Credentials.Scopes,
                           code);

// TO-DO: Redirect to the correct page after logging in

 });
5. Redirect your user to the correct page that shows all of the information

You can add a static property to app.cs and save that your user is logged in and his tokens

//Add this in the MainThread after the call to Oauth Helper.

//Save the Refresh Token for future use securely
Debug.WriteLine(tokens.RefreshToken);

//In App.cs, add these variables to maintain context
App.HasLoggedIn = true;
App.AuthModel = tokens;

await Navigation.PushModalAsync(new ProfilePage());

You can make requests on behalf of the user with the access_token to call the 3rd party provider APIs.

Here is the final code. I used Spotify Auth in this example. Make sure to update credentials.cs with your own OAuth Information.

Note: On iOS, add this to your appdelegate.cs so login cookies don’t persist on a new login page.

 	NSHttpCookieStorage CookieStorage = NSHttpCookieStorage.SharedStorage;

        foreach (var cookie in CookieStorage.Cookies)
        {
             CookieStorage.DeleteCookie(cookie);
        }

	LoadApplication(new App());

And That’s it! You can do all of your login within one solid page. Here is the final code.

However, there are some limitations to this approach. Read on to Part 3 to understand some limitations of this approach.

comments powered by Disqus

Thanks for stopping by! Subscribe to my blog to get my latest posts