READ THIS FIRST

Update: 3-4-2016: If you're finding this blog and looking for information related to Azure Mobile Services, I'd strongly recommend checking out Azure Mobile Apps. Azure Mobile Apps is a new version (consider it a v2) of Azure's mobile backend support. All of the same features of Azure Mobile Services are there, with a lot of other very cool features to go along. You can read more about Azure Mobile Apps, and how to transition from Azure Mobile Services, here.

Mobile Services with Android

Update 4/22/2014: Version 1 JWTs have been deprecated. Make sure you check out this post to see how the script for JWT creation needs to be changed.

Update 5-13-2013: Since writing this I found an easier way to do the retry request that does not require the changes to the SDK. Please read more about this at this post on CountDownLatchs.

This article is the companion to one I just posted about handling different types of authentication with Windows Azure Mobile Services.  Prior to taking a look through this article and the mentioned code, I would go through the original article on Mobile Services and auth.  This article will cover how to connect the Mobile Service we set up there with an Android client using the Mobile Services SDK for Android.  All of the source code for this Android app is available here in GitHub.  I’m going to cover a few different areas in the app in this post: giving users the choice of how to login, creating and logging in with custom accounts, logging users out and returning to the root view controller, caching user tokens so we don’t have to login each time we run the app, and dealing with expired tokens now that we’re caching them.

An updated Android SDK

For a couple of different reasons, I needed to update the Android SDK to complete this sample.  I’m in the process of working on a pull request against the official SDK to get these changes into what you pull down from the portal, but for now you can download the updated SDK from my forked repo in GitHub here.  The two additions to the SDK in this fork are the ability to pass query string parameters to insert, update, and delete calls against your Mobile Service tables as well as some code to make it easier / possible to retry requests.  We’ll talk more about this when we get into handling expired tokens.  If you don’t want to provide retry request support you DO NOT need to use the forked SDK.  It’s only required to support retrys.

Giving the user choice

This is one of the easiest things to do from a front end perspective because all you need to do, is give the user the ability to select their authentication provider:

Provider selection

All I’ve done is put one button for each provider and then the custom auth at the bottom.  Each of these buttons, except the last, calls the same OnClickListener:

We’re checking to see which button was clicked and setting a provider variable to match the type, then calling mAuthService’s login method.  AuthService is a class that wraps most of the Mobile Service functionality which we’ll be talking about throughout this article.  This method takes in the provider and a UserAuthenticationCallback.  This callback is called when the user finishes logging in, or cancels out (or if there is an error).  If there aren’t any issues, we save the user data (which we’ll cover later), and then open the LoggedInActivity with an Intent.  The login method presents the login view of the provider web view, all wrapped up for us.  Let’s talk about what happens when you hit the Login with Email button.

Custom auth

When you tap the Login with Email button we fire another intent that takes you to CustomLoginActivity where the user can enter their username and password:

custom auth login

Let’s take a look at logging the user in here before we look at registering their account.  When the user taps Login we call the following code:

First we check to see if the username and password fields aren’t empty.  We’re only logging them if they aren’t but we should probably show some indicator to the user.  If there aren’t any issues, we call authService’s login method but here we’re passing over a username and password instead of the provider.  This override means we’re trying to do custom auth.  In the callback handler we’re doing the same thing as before: saving the user data and loading the LoggedInActivity.  Let’ move on to the Register for Account button.

Registering accounts

When you tap the Register for Account button, we launch the RegisterAccountActivity:

Registering an account

Let’s look at what happens when the user taps Register:

The first thing we do is check that all of the fields are filled out.  If not, we’re just logging some information to say they aren’t.  If this was a real app, we’d probably want to give the user a visual cue to know that they need to enter the info.  After that, we check to make sure the password and confirm password are the same.  Finally, we call the mAuthService’s registerUser method which has a callback handler that saves the user info, finishes the current activity, and loads the LoggedInActivity with an intent.  The registerUser method is very simple:

Here we’re just creating a JsonObject which we fill with the username, password, and email address.  We then pass that into the mTableAccounts insert method.  mTableAccounts is a MobileServiceJsonTable which maps up to our Mobile Service’s Accounts table.  Let’s next look at how we’re saving account info.

Saving account info

There are actually a few different methods on our AuthService that we use for saving and setting user data.  Let’s look at the method called from our custom auth login and registration screens, setUserAndSaveData:

Here, we pull the userId and token out of the JsonObject that is sent into the method.  We then pass that information into the setUserData method and call the saveUserData method.

The setUserData method creates a new MobileServiceUser using the userId and then sets the authentication token.  It then set’s our mClient’s current user property to be that user.  Afterwards, we check which provider was being used and save that for later. 

Finally, in the saveUserData method, which is also called from the AuthenticationActivity after a user logins with one of the built in providers, we are writing the userId and token to SharedPreferences.  SharedPreferences are a great way to easily store small amounts of data for your applications.  However, the data is NOT encrypted and if the device has been rooted, it’s possible for other applications to access the data.  Remember that with access to a user Id and token, you can make requests as that user so this should be considered sensitive information.  You’ll probably want to pick a more secure method to store this information in your production apps.  Now that we’ve seen how to save the data, let’s take a look at loading it when the app starts.

Loading account info

In the AuthenticationActivity’s onCreate method, we call the AuthService’s isUserAuthenticated method.  If the user is already authenticated, then we go directly to the LoggedInActivity and skip any of the login options.  That method looks like this:

Here we check to see if we’ve saved UserData before.  If we have, then we pull out the userid and token and then call the setUserData method with those values.  Finally, we return a boolean which says if the user is authenticated or not.  Let’s look at the LoggedInActivity and talk about logging out.

Logging the user out

Once we’ve logged into our app we get to the LoggedInActivity:

Logged in activity

When we tap the Logout button we are just calling the logout method on the mAuthService:

There’s a lot going on here.  First we use CookieManager to remove all of the cookies in our application.  This is done because when you login using one of the built-in providers, they place cookies on your web view.  So, if you were to go back and login again, the provider would see those cookies and say “you’re already logged in”.  One important thing to note is that we’re removing ALL of the cookies.  If you’re using web views for other things, you may want to look at the specific cookies for the login providers and remove those.  Next, we’re deleting the data stored in shared preferences.  So, if we’ve stored the userid and token there, this will remove them.  Then we call the MobileServiceClient’s logout method which removes the local user property.  Finally, if we passed in a value saying we should redirect to the login page, we’ll load the AuthenticationActivity with an intent.  Importantly, we’re passing the FLAG_ACTIVITY_CLEAR_TOP and FLAG_ACTIVITY_NEW_TASK to our intent.  This is basically saying remove all the other activities from our navigation stack and take me back to the authentication activity.  The last thing we’ll talk about today is dealing with expired tokens.

Handling expired tokens

Update 5-13-2013: Since writing this I found an easier way to do the retry request that does not require the changes to the SDK. Please read more about this at this post on CountDownLatchs.

This is the most complicated part to explain because it required a few changes to the Android SDK (as mentioned near the top).  The idea is that if your app makes a request to your Mobile Service that SHOULD get through because their user is authenticated and you receive a 401 (unauthorized error) it means the user token you’re passing over has expired.  There are a couple of ways we can handle this.  In the callback handler for every method that we have that interacts with our Mobile Service, we could check for a 401 response, or we can handle things in one place: the ServiceFilter’s handleRequest method.  Basically, when you instantiate your MobileServiceClient and call withFilter on it, you can specify a filter that will be called for each request you make to your Mobile Service.  This is a pretty complex method so we’ll go through it slowly:

Inside of the handleRequest method we’re just calling the NextServiceFilterCallback’s onNext method.  Inside the ServiceFilterResponseCallback’s onResponse method is where the magic happens.  First we check to see if the status code on the response was a 401, if it wasn’t we skip the rest of the code and call responseCallback.onResponse at the bottom of the method and proceed on as normal.  If it was a 401 though, we do some more advanced stuff.

First, we call the logout method that we looked at above.  In this situation, we’re passing false into that method call to indicate that we don’t want to boot the user back to the AuthenticationActivity.  We then check to see if we should retry failed requests (the mShouldRetryAuth variable).  We’re also checking to see if the user used custom auth.  We’re not facilitating re-logging in if the user did custom auth but you could easily do that by creating a dialog with custom auth (we just have a full activity right now).  If we shouldn’t retry or they did use custom auth, we go down to the bottom of the method and call logout, this time booting the user back to the first activity, and then process the response again.

Now that we’re ready to retry, let’s dig into what happens.  First, we get access to the Application object.  We’ve overridden the Application object so that it has a reference to whatever the current Activity is.  We’re then using that to set the Context of our MobileServiceClient.  We’re doing this because the client need’s to have a context for whatever is currently visible in order to pop back up the login dialog.  If you were to leave a non-visible Context as the context or pass in the Application Context, your call to login would fail.  We then call the onResponse method because the SDK will currently crash if you don’t (I filed an issue on this and they’re working on a fix).  Then we tell our client to login again and do that on the UI thread.

If the user does not login successfully, we log the user out and kick them back to AuthenticationActivity.  If they do login successfully, we first call saveUserData to store the authentication info.  Then we call the request’s getPreviousRequest method.  This is where we get into the changes I had to make to the SDK to facilitate retrying requests.  The previous request is essentially a copy of the current request.  We remove it’s X-ZUMO-AUTH header which contains the user token and then replace it with the current user’s auth token.  The idea being that we’re removing the invalid token that was set on the previous request and then setting the new valid token.  We then pull out the URL of the previous request and add a bypass query parameter to it.  This is only necessary because of how I’m testing for 401 errors.  To test 401s, I’m calling a method on my Mobile Service that will always return a 401 error UNLESS the bypass query string parameter is present.  You wouldn’t include a bypass query string parameter if you were just handling expired tokens.  Finally, we do a switch on the previous request type and perform the appropriate operation on the previousTable.  This is what retries our previous request.  Again, if you didn’t want to facilitate retrying, you could JUST force the user out on a 401 error and you wouldn’t need the forked SDK. 

Lastly, let’s look at the method we call to trigger testing a 401:

Here all we’re doing is creating a JsonObject and then sending that to mTableBadAuth’s insert method.  As we saw in the preceding article, the Insert script for the bad auth table is set to handle returning a 401 without the presence of the bypass parameter. 

Conclusion

Today we looked at how to implement an Android client with access to each of the built in authentication providers as well as how to handle custom authentication.  We reviewed how to register a user for new accounts as well as how to login with them.  We also saw how to cache the user’s auth token so that they don’t have to login each time the app runs and how to reload that auth information.  With all of this, you could pretty much just drop in whatever functionality you want only logged in users to have access to.  There are some further things you might want to handle such as sending an email to the user after they register for an account to make sure the email address they used is valid (which you can do with SendGrid) as well as offering Forgotten Password functionality.  These are things that are definitely part of a full application and should be considered.


Chris Risner


18 Comments

Chris

Happy you asked. After posting this, I worked with the SDK team and we found a way to do it with the SDK as is. It's still not going to be as nice as it is in .NET or iOS due to how Android works with AsyncTasks and UI / background threads, but it does mean the changes are unnecessary. I'll be posting a walkthrough of doing that shortly.

Chris

Hi Alexander, you can read about the updated way to do this here: http://chrisrisner.com/Coun.... The updated SDK will still be required for the sample due to the custom parameters being used when making calls to insert / update / delete but the previous request stuff is no longer needed.

Alexander

Excellent code, no doubt. I did notice that I get a crash sometimes when my response object is null (any sort of communication failure). Throwing in a resoponse!=null check fixed the problem.

Line 13:

StatusLine status = response.getStatus();

int statusCode = status.getStatusCode();

if (statusCode == 401) {

Duncan Fuller

Hi Chris, I am using the authservice successfully, thanks - I like having this grouped in one place - I'm trying a mix of typed and untyped data, I've got the untyped working but do you have any example of using the authservice with v2 SDK and typed data hooking up to the authservice, much appreciated - UPDATE - I've gone back to untyped data with an arraylist and figured it out

Leave a Comment