.NET Zone is brought to you in partnership with:

Sasha Goldshtein is a Senior Consultant for Sela Group, an Israeli company specializing in training, consulting and outsourcing to local and international customers.Sasha's work is divided across these three primary disciplines. He consults for clients on architecture, development, debugging and performance issues; he actively develops code using the latest bits of technology from Microsoft; and he conducts training classes on a variety of topics, from Windows Internals to .NET Performance. You can read more about Sasha's work and his latest ventures at his blog: http://blogs.microsoft.co.il/blogs/sasha. Sasha writes from Herzliya, Israel. Sasha is a DZone MVB and is not an employee of DZone and has posted 206 posts at DZone. You can read more from them at their website. View Full User Profile

Windows Azure Mobile Services "Rent a Home" Sample, Part 3: Authentication

04.18.2013
| 3360 views |
  • submit to reddit

Last time around, we explored the user interface and the server script for our apartment listings application. Today we'll see how to add authentication to the mix, and limit certain operations only to authenticated users. This is particularly important in the Rent a Home application, because you don't want anonymous users deleting and updating apartment listings! In fact, you'd probably want only the user that created an apartment listing to have the right to update or delete it.

NOTE: Windows Azure Mobile Services is configured by default to enable any user with the application URL and application key to perform create, retrieve, update, and delete operations on any tables belonging to that service. Because the application key is distributed to client devices, this is obviously a very bad idea for a production app -- anyone can extract the application key from your application and perform arbitrary modifications of your data. Therefore, in a production configuration, it is extremely important that you limit access to your data only to authenticated users. You might find it reasonable to allow read data to anyone with the application key, but create/update/delete operations should require authentication.

Windows Azure Mobile Services currently supports authentication with four authentication providers: Microsoft Account (Live Connect), Google, Twitter, and Facebook. (The Rent a Home application supports only Twitter authentication, but it would be trivial to add support for the other providers as well.)

Before you can authenticate with Twitter, you have to create a Twitter application (do so by visitingdev.twitter.com) and specify its consumer key and consumer secret in the Windows Azure portal. Then, you can immediately start making authentication requests from the supported clients:

Android

mobileService.login(MobileServiceAuthenticationProvider.Twitter,
  new UserAuthenticationCallback() {
    public void onCompleted(MobileServiceUser user,
                            Exception exception,
                            ServiceFilterResponse response) {
      if (exception != null) {
        displayError(exception);
      } else {
        setTitle("Welcome! User id: " + user.getUserId());
      }
    }
  });

NOTE: The user id is not the user name; it is an opaque number that users will be surprised to see. Later in this post, we'll see how to obtain the user's name from the user id.

iOS

UIViewController *vc =
  [self.client loginViewControllerWithProvider:@"twitter"
               completion:^(MSUser *user, NSError *error) {
    if (error) {
      NSLog(@"Authentication Error: %@", error);
      //error.code == -1503 means user cancelled the dialog
    } else {
      //Authentication was successful, use self.client.currentUser
      //to read information about the user
    }
    [self dismissViewControllerAnimated:YES completion:nil];
  }];
[self presentViewController:vc animated:YES completion:nil];

Windows Phone and Windows 8

var user = await App.MobileService.LoginAsync(
                MobileServiceAuthenticationProvider.Twitter);
btnLogin.Visibility = Visibility.Collapsed;
txtLogin.Visibility = Visibility.Visible;
txtLogin.Text = "Logged in using Twitter";

Server-Side Authentication
With this client-side infrastructure in place, every subsequent request performed by an authenticated user will be associated with that user, and available to table scripts as the user parameter of the respective script function. Having it available is not enough, however -- we need to explicitly associate our apartment listings with users. The following part of the insert script on the apartment illustrates how easy this is:

function insert(item, user, request) {
  if (user) {
    item.user = user.userId;
  }
  //The rest of the code omitted for brevity
}

Thanks to dynamic schema, there's no need to pre-declare the user property on apartment listings. This property is actually enough to protect apartment listings from modification by the "wrong" user. For example, this could be our delete script on the apartment table:

function delete(item, user, request) {
  if (!user || user.userId !== item.userId) {
    request.respond(403, 'Only the user that created the ' +
                         'apartment listing may delete it.');
  }
  request.execute();
}

As you saw in the application UI, it would also be valuable to display owner information to other users (this would form the basis of helping users contact the apartment owner to broker a deal). Because the user id is not a human-readable username, we have to use a service-specific API (in this case, Twitter's) to retrieve the username. This is done in the insert script of the apartment table, like so:

item.username = '<Unknown User>';
var identities = user ? user.getIdentities() : null;
if (identities && identities.twitter) {
  var id = user.userId.substring(userId.indexOf(':') + 1);
  var url = 'https://api.twitter.com/1/users/show.json?user_id='
            + id;
  reqModule(url, function (err, resp, body) {
    if (err || resp.statusCode !== 200) {
      request.respond(500, body);
    } else {
      try {
        item.username = JSON.parse(body).name;
        //Continue processing as usual
      } catch (ex) {
        request.respond(500, ex);
      }
    }
  });
}

Reading the specific user's data depends on the identity provider -- the API endpoint for Twitter users is obviously different from the API endpoint for Facebook users. Carlos Figuera has taken the time to provide a set of code snippets you can integrate in your backend to obtain user information from any of the four identity providers that are currently supported.

Summary
At this point, we have added authentication support to our application. Users can sign in with Twitter and their apartment listings are associated with their Twitter identity. We also restricted access so that only authenticated users can create new apartment listings, and only the creating user is allowed to update or delete an apartment listing. One additional thing that would be "nice to have" is support for a custom identity, i.e. custom username and password credentials for your service. Josh Twist of the Mobile Services team explains what would be necessary to provide such support on your application's side.

Finally, there's one fairly annoying detail about the current implementation: it requires the user to sign in every time they open the application. Although the backend doesn't require re-authentication, the client SDK does not currently have an automatic way to persist the authentication token in some secure storage. This shouldn't stop you, however, from persisting the authentication token yourself, and passing it to the appropriate login method of the Mobile Service client. For a complete example, see Josh Twist's post.

In the next installment, we'll take a look at how to configure push notifications on all four platforms, and how to deliver push notifications to registered clients.

I am posting short links and updates on Twitter as well as on this blog. You can follow me: @goldshtn
Published at DZone with permission of Sasha Goldshtein, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)