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.
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.
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 iOS client using the Mobile Services SDK for iOS. All of the source code for this iOS 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 won’t have to login each time, and dealing with expired tokens now that we’re caching them.
Giving the user choice
This is one of the easiest things to do from a front end perspective because all I need to do is give the user the ability to select their authentication provider:
All I’ve done is put one button for each provider and then the custom auth at the bottom. Each of these buttons (except for the last) will call the same method and pass in the provider’s name:
[self loginWithProvider:@”MyProvider”]
Depending on which button is clicked, one of the following is sent in: facebook, google, microsoftaccount, twitter. I do want to highlight here that all of these are exactly as you’d expect, except the Microsoft one. I’m not exactly sure why it’s like that. Next we have the loginWithProvider method:
Here the first thing we do is record which provider the user selected in a variable inside the authService (which is a class I have wrapped all of my Mobile Service functionality in). We then create a MSLoginController using our authService’s client property. That get’s presented to the user. When that method’s callback is called, we log the error if there was one and, if not, we save the auth info (which we’ll talk about later) and then trigger the loggedInSegue. I’m going to highlight how I created that segue because it was something I didn’t know about before this sample. If you open up your storyboard (I’m using storyboards for everything here) you can control + click and drag from the View Controller icon under your view to another View Controller and create a segue which you can then give an Identifer so you can call it from the code:
So even though there’s nothing in my UI triggering this segue (like there would be if I’d done the same thing from a button instead of the icon) I’m able to use this segue from my code behind. So here, once the user has logged in successfully, we’re taking them to a page that’s “inside the authentication wall”. We’ll talk about what happens in there a bit later, but first, let’s talk about what happens when you hit the Login with Email button.
Custom auth
When you tap the Login with Email button, you’re taken (via segue) to a login page where the user can enter their username and password:
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:
Here we’re building a NSDictionary with the username and password and then passing that to the loginAccount method of the authService. In the callback, we’re checking to see if the NSString that is returned is equal to SUCCESS, and if so, we’re dismissing this view and then calling the customAuthSegue segue. This segue is defined just like the one above but on our CustomLoginViewController. If we didn’t have a success, then we’re setting a label below the buttons to whatever message was returned. Inside my AuthService, I have the loginAccount method:
This method creates a NSDictionary to store parameters for the query string and adds one named login. If you recall from the previous article about the server side scripts, when we do an insert on the Accounts table and login is a query string parameter, it knows we’re trying to log the user in as opposed to register a new account (since we’re using the same script for both things). We then call the insert method on the accountsTable. If there was an error we call the completion handler with the error text (which sets it to the label on the screen), and if not, we create a new MSUser using the userId that is returned and then set it’s mobileServiceAuthenticationToken using the token that was returned from the insert call. We then set the client’s currentUser to be equal to that and call the saveAuthInfo method before finally returning SUCCESS. One thing I’ll point out is that I didn’t put any validation in to check that they entered a username or password. This is definitely something you’ll want to do in your own apps to make them user-friendly.
Registering accounts
If the user taps Register for Account, we’ll take them to this view:
Here the user enters all of their information and then taps Register. This calls the tappedRegister method:
The first thing we do here is check to make sure they’ve entered data and that it’s valid. After that, we build our NSDictionary with the necessary information and pass that to the registerAccount method on AuthService. If this comes back with a SUCCESS message, we dismiss the view and call the customAuthSegue which will take us to the logged in view. If not, we display the error on the screen. Let’s look at registerAccount next:
As you can see, this is the exact same as the loginAccount method earlier, we’re just not passing the login parameter in. Now let’s take a look at how we save the account info.
Saving account info
To store our account info, we’re using something in iOS called the Keychain. You can read a lot about the keychain if you want or you can skip that for now and just know that we use it to securely store data on our device. The KeychainWrapper’s provided in Apple’s docs don’t actually account for Automatic Reference Counting (ARC) so instead of using their files, we’re going to take the version from Chris Lowe’s iOS Security tutorial here. This code is available under the MIT license so we can use it (as long as we know it’s our responsibility if anything crazy happens). Once you’ve added those files to a project you can then use the methods in your code. Here we’re using it to save our data in the saveAuthInfo method:
We’re simply storing the userId and token properties. Now that we’ve seen how we can cache the user token and ID, let’s see how we can load that when the app starts.
Loading the auth info
When our application starts, in the first View Controller, we’re initializing the AuthService which calls this init method:
The important call here is the call to loadAuthInfo:
Inside that method, we’re first fetching the userid from our Keychain. If that value isn’t null, we create a new MSUser for the client.currentUser and then set it’s mobileServiceAuthenticationToken. Now we finally see where this is called and set from our initial View Controller:
After the AuthService is set up, we check if it’s client.currentUser.userId property isn’t NIL and if so, we call the loggedInSegue. This segue (just like the earlier ones that aren’t connect to a UI element) takes the user into the logged in section of the app and by passes the login screen. So now we’ve seen how to cache the current user’s token and user ID and how we can load it when the application starts. Next let’s talk about how we can trigger logouts.
Logging the client out
To log out of this application, there is a logout button on our LoggedInViewController:
This logout button is connected to a segue, but not just any kind of segue, an unwind segue. Unwind segues are a way for us to trigger returning back to a certain point in the application flow. So if I want to return back to the view with all of the login options, this is perfect for it. To create an unwind segue, we first have to add a method to a View Controller. This method needs to have a return type of IBAction and a UIStoryboardSegue parameter. Here’s the method we have in the initial View Controller:
The only thing we’re doing is calling the killAuthInfo method on authService. From a code perspective, none of this is saying “roll back all the view controllers until you get here”. iOS actually handles that for us. All we have to do is connect the Logout button to that segue. In order to do that, we can control + click and drag from the button down to the green Exit indicator beneath the View Controller:
When you release, any method that matches the IBOutlet / UIStoryboardSegue method format will show up for us to select. So when a user taps that button, iOS says “I’m going to roll back to the first View Controller that implements the matching method (so watch out if you have multiple unwind methods named the same thing) as well as calling that method. So now that we’re back to the initial View Controller, let’s look at the killAuthInfo method that is called:
The first thing this method does is delete the userid and token items from the Keychain. After that we go through the cookies and remove them. The reason we do this is that logging in through any of the built in providers (Facebook, Google, Microsoft, and Twitter) sets cookies on the UIWebView that is used to display the login pages. If these cookies were left there and we tried to log back in using the same provider, then that provider would say we were already authenticated. One caveat to this is that here we’re removing ALL of the cookies from our app. If we were using UIWebView’s for more than just that, this could cause a problem if we remove cookies we don’t want to. Lastly we call logout on the client. This currently just sets the currentUser to NIL. Now the last thing we need to look at is how to handle expired tokens.
Handling expired tokens
The motivation for handling this came from another post by Josh Twist which you can check out here. The idea is that if your app makes a request to your Mobile Service that SHOULD get through because the 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 could handle this. In the completion 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 MSFilter’s handleRequest method. Basically, when you say that your service class (AuthService here) implements the MSFilter protocol, you’re saying you’ll also be implementing the handleRequest method which will be called for each request you make to your Mobile Service. Here’s the handleRequest method in our AuthService:
The only thing we’re doing in this method is calling onNext and specifying a method to call when we get a response. That method is filterResponse:
In this method we first check to see if the response was a 401, if not we just call onResponse which continues to deliver the response to the completion handler the call to the Mobile Service had. If it was a 401, we first call killAuthInfo to remove all the authentication info. We then check to see if the shouldRetryAuth boolean has been set to true (we’ll get to where that is set soon). If it isn’t true, or the auth used was the custom auth, then we call a triggerLogout method (we’ll look at that in a second). We’re checking for custom auth because we haven’t implemented a popup to let the user log back in with custom auth. We could of course create something like that and show it, I just haven’t done it for this sample. Provided the boolean is true and we used a non-custom auth, then we proceed to call loginWithProvider on the client to pop up the login window again. When we get our response back from the login window, if they didn’t log in successfully, then we call triggerLogout (they had a chance to log back in and missed it). If they did login successfully, then we save the new auth info with saveAuthInfo and then we recreate the original request. We first take a mutableCopy of the original request. We then set that NSMutableURLRequest’s X-ZUMO-AUTH header to the new auth token. Finally we add a new query string parameter and tell our filter to process the request again. Now if you remember how we implemented the BadAuth service so it would trigger a 401, you’ll remember that if the request contains a query string parameter named bypass, then we return a normal 200 response. We fix this with the addQueryStringParamToRequest method:
Here we’re assuming there isn’t already any query string parameters but that’s a safe thing to do because we know exactly what the original request was. Plus, if we really had an expired token and fixed the issue, we wouldn’t need to worry about a bypass parameter. The triggerLogout method is called whenever we want to force our user back to the login screen from our filter:
Here we make sure the auth info was killed, then we get the top View Controller and tell it to perform the logoutSegue segue. This segue is defined like the others to be an untrigger segue, this one on the root View Controller. Finally, let’s look at the method we use to trigger that call to the BadAuth method:
Here we’re taking in the retry parameter which will dictate whether or not we try the re-login or if we just force the user out.
Conclusion
Today we looked at how to implement an iOS 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 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.
22 Comments
roxor
Hi chris, I've already read a few of your tutorials and they are just great!! Really want to thank you :) also brilliant assortment of topics - really useful
Chris
Happy to hear that you've enjoyed them!
Jay Mayu
OMG I have no idea why Microsoft has to complicate custom authentication like this :'( We decided to move to azure but now the custom authentication killing us. Take a look at parse.com custom login is seamless. Not everyone on earth has fb and twitter.
Chris
Thanks for the feedback J. Making custom user auth easier is something we'd like to see, though we'd also like to make sure we understand how people want to use it and get it right.
Jay Mayu
Thanks chris for the reply. Sure I love to see how it evolves in future. We already going live before christmas with custom authentication. Made use of some snippets from your site too. Thanks again.
Magnus
Hi, awesome stuff. However I get exceptions from the MobileService client saying that the token is invalid:
Error: The authentication token has an invalid signature.
It is much shorter than the token I get if I authenticate with Google. Is it supposed to work still? Has Microsoft changed something on their side perhaps? I would really love to get this working. Any ideas? Thanks /Magnus
Chris
Hi Magnus, are you doing custom auth or auth with one of the providers? Can you give me some more information on what you're doing?
wil
hi Chris, is there a way to get user profile photo as well?
Chris
Yes, you can get their provider identity information using the user.getIdentities method in a server side script. You can then talk to the API provider (whether it's facebook, twitter, etc). You'll have to perform the web request against the provider yourself though.
wil
I am trying to get profile image from facebook. I get a URL but that URL is not actual image file. I have to do a http request on that URL to get back actually link to a jpg file. Is there a better way to do this because this is slow. Thanks Chris
Chris
According to this (http://bit.ly/IKmvET) the URL returned is a redirect to the current actual URL of the profile image. It doesn't sound like there is a way around using that to get the redirect URL. If you're getting the username, you could also use this URL: https://graph.facebook.com/... but that will still need to do a redirect. It seems like since the URL of the actual picture can change, to be safe you have to go through the redirect.
Dmitri
Hi Chris, love your straight forward tutorials with great explanations and copy paste code to go with it. I am currently delving into Xamarin development for iOS and Android. I was wondering if you can provide a C# translation for iOS primarily and Android Auth. tutorials that you've done? If not, can you direct someone who can help me here from Windows? Thanks, Much appreciated.
I need to get the login and authentication system down and secure before moving on with anything else, I'm sure you know how crucial that is.
Chris
Happy they've been helpful. If you're doing Xamarin, it will be pretty easy to move from this sample to that. Take a look at the tutorial found here (http://azure.microsoft.com/... which is for Xamarin.iOS but there is a link at the top to Xamarin.Android. That will cover how to do all of the individual provider auths. For custom auth, you can look at the same tutorials to see how to make calls against tables (or use the more recent custom APIs to handle the registration and login part). If you're still using JS backend, the backend won't change at all. Let me know if you have other questions.
Dmitri
Are you saying that I didn't need to implement the JS back-end you had in the JS Auth back-end tutorial? From my understanding, I DO need the JS back-end in order to get additional user profile information from the Facebook user, such as Friends List, their profile pic, etc..
If it simply authenticating based on Facebook for example, then yes I can use the individual provider auths, but that won't come with the additional information pertaining to the Facebook user. Am I correct with this?
Chris
No you definitely still need to duplicate the backend. I was just sending the client side information. This post above doesn't show how to get any additional information about the user once they've authenticated. If you're looking to get more info (i.e. username, actual name, profile pic, etc), take a look at this post: http://blogs.msdn.com/b/car...
Mike
Hi Chris,
In regards to expired authentication tokens, do you perhaps have an alternative method of handling those? As a user, I would get pretty frustrated logging in all the time. Seems like there should be a way to keep that authentication token valid while the user is still active within the expiration period, and then maybe force a logout/login once there hasn't been any activity within that expiration period. Thanks!
Chris
With the Node.js backend, you're kind of stuck at the whims of the runtime right now which is a 30 day expiration for tokens from any providers (if you do custom auth, you specify your own expiration term). If you use the .NET backend, I believe you can alter this expiration behavior. Alternatively, if you do client side authentication (not possible today with all providers) you should be able to get a refresh token from the provider which will allow you to reauth from the client without popping up a login window.
swaminathan m
Chris, could you kindly share the authentication for IOS using swift language? thanks
Rohan Singhal
Long shot after so long, but did you ever find a swift implementation for this?
Chris
Alas, I did not but it shouldn't be difficult to convert the Objective-C into Swift. The Azure Mobile Apps SDK should be close to the same (as far as method calls) regardless of if you're using swift or Objective-C
ShivaChowdary Boddapati
i was download and run the source code.but it wont works.I am unable to login facebook account as well as remaining can you help me chris this is the error.
AuthenticationDemo[98973:660987] Authentication Error: Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={NSErrorFailingURLStringKey=https://mymobileservicename..., _kCFStreamErrorCodeKey=8, NSErrorFailingURLKey=https://mymobileservicename..., NSLocalizedDescription=A server with the specified hostname could not be found., _kCFStreamErrorDomainKey=12, NSUnderlyingError=0x7d235e10 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "A server with the specified hostname could not be found." UserInfo={_kCFStreamErrorCodeKey=8, NSErrorFailingURLStringKey=https://mymobileservicename..., NSErrorFailingURLKey=https://mymobileservicename..., NSLocalizedDescription=A server with the specified hostname could not be found., _kCFStreamErrorDomainKey=12}}}
ShivaChowdary Boddapati
I think url doesn't works!