Securing Data and Protecting the User - Learning Windows Azure Mobile Services for Windows 8 and Windows Phone 8 (2014)

Learning Windows Azure Mobile Services for Windows 8 and Windows Phone 8 (2014)

Chapter 3. Securing Data and Protecting the User

Security is extremely important for any system that is exposed to the Internet. Using Windows Azure Mobile Services with our applications is no different from any other system; we are exposing our data to the Internet on the server side and our users on the app side.

Windows Azure Mobile Services makes it easy to achieve a secure system by offering us the following features:

· Services are hosted on a highly-secure infrastructure; so we don't have to worry about hardening servers, configuring firewalls, and patching software.

· Granular permission control on individual tables and API methods means that we can tailor permissions down to method level on tables and APIs.

· Authentication is delegated to third-party authentication providers. So we don't need to store user credentials ourselves; we can let someone else take care of this for us.

· With authentication delegation, we don't need to create a full set of user admin UI in our application, which is time-consuming.

· Windows Azure Mobile Services use HTTPS, which means our data is encrypted between the client and server.

In this chapter, we'll discuss permissions, setting up an authentication provider, writing code to authenticate our users, and accessing the REST API with the master key.

Configuring permissions

By default, table and API methods allow requests that are made with the app key. This behavior can be changed to one of the following options per method:

· Everyone: This is the least secure option for a method as it allows anybody who knows your service URL to call it

· Anybody with the Application Key: This is the default option

· Only Authenticated Users: If this option is chosen, requests must be authenticated with one of the providers configured in the IDENTITY tab

· Only Scripts and Admins: If this option is chosen, only requests authenticated with the master key or from internal scripts will be allowed

Rules for choosing permissions

The following is a list of rules to help choosing permissions:

· If a user doesn't need to use a service method and we only need to perform administrative tasks, apply Only Scripts and Admins

· If a user requires the Insert, Update, and Delete methods, apply Only Authenticated Users and make sure only the user's data is available to them in the Read methods with custom scripts

· Read on public tables can have Anybody with the Application Key if we aren't tracking the user

· Don't use the Everyone option unless you want anyone with the service URL to use your service method

The leaderBoard table has INSERT PERMISSION set to Only Authenticated Users and READ PERMISSION set to Anybody with the Application Key because all users have access to this table and we're not tracking their credentials. UPDATE PERMISSIONand DELETE PERMISSION is set to Only Scripts and Admins because they're not used, and we don't want these methods being used by anybody except the administrators, as shown in the following screenshot:

Rules for choosing permissions

Authentication providers

Windows Azure Mobile Services support the following Oauth2 authentication providers:

· Microsoft: http://msdn.microsoft.com/en-us/live//default.aspx

· Twitter: https://dev.twitter.com/apps

· Facebook: https://developers.facebook.com/apps

· Google: https://code.google.com/apis/console

Any of the preceding can be implemented by creating an application with the provider that will provide you with a client ID and secret key. These details are then entered in the Identity tab of the portal.

Authentication

We should not think that if a user has been authenticated by a provider, they can be trusted with all our services; they can't. Authentication just means that the users are who they say they are, and we can use their identity to manage their data. We should only allow them to access services they need, and only allow them to read, update, and delete their own data and read public data.

Registering for Windows Live Connect Single Sign-on

Go to the Live Connect Developer Center at http://msdn.microsoft.com/en-us/live//default.aspx, click on My apps, and enter the app's details, as shown in the following screenshot:

Registering for Windows Live Connect Single Sign-on

Once you have accepted the terms and conditions, you need to configure the application details, as shown in the following screenshot:

Registering for Windows Live Connect Single Sign-on

Enter your Windows Azure Mobile Service's URL in the Redirect domain field and select Yes for Mobile client app. I selected No for Restrict JWT Issuing because I want my Windows 8 and Windows Phone 8 app to use the same authentication provider application. Restricting JSON Web Token (JWT) is a security mechanism for allowing just one application to use the Live Connect application.

Once this is done, copy Client ID and Client secret (v1) to the microsoft account settings section on the IDENTITY tab in the Windows Azure Mobile Services portal and click on SAVE, as shown in the following screenshot:

Registering for Windows Live Connect Single Sign-on

Authentication in the app

If you've created apps before that need to authenticate a user with OAuth2 in order to use services from providers such as Twitter and Facebook, you'll know that it's not straightforward. It has steps such as launching a web page for the user to log in and collecting credentials from browser redirects. Authentication with Windows Azure Mobile Services couldn't be simpler. The MobileServiceClient class has a LoginAsync method that does everything for us.

Logging in

To log in, we use the MobileServiceClient class instantiated with our app key and service URL that we saw in the previous chapter. We simply call the LoginAsync method with the auth provider type we want to use. It will log us in and return a MobileServiceUser object that contains a user ID and auth token, as shown in the following code snippet:

// Login

var user = await this._mobileService

.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);

In a Windows 8 app, the LoginAsync method launches a login page that contains the provider's login web page for the user to enter their details, as shown in the following screenshot:

Logging in

When we examine the user object that is returned, this is what we see:

Logging in

Once we've done this, the MobileServiceClient object has the CurrentUser property set to this user, and the details will be used to authenticate our requests, as shown in the following screenshot:

Logging in

If the authentication fails, an InvalidOperationException will be thrown with the Error: Unauthorized message so that we can catch it, as shown in the following code snippet:

catch (InvalidOperationException ioex) {

// Task has failed because it was unauthorized try again

if (ioex.Message == "Error: Unauthorized") {

}

}

Storing credentials

This is all very good, but we don't want our users to log in every time they open the app, or when the app resumes after suspension—it would not make for a good user experience! We can get around this by storing the user credentials when they log in, and then retrieving them and applying them to MobileServiceClient whenever required. This is achieved by the following method:

public static async Task<bool> Login() {

// First have a look and see if we have the user's token

var userId = StorageHelper.GetSetting<string>(USER_ID, null);

var userToken = StorageHelper.GetSetting<string>(USER_TOKEN, null);

bool success = true;

if (userId != null && userToken != null) {

// Create user and apply to client

var user = new MobileServiceUser(userId);

user.MobileServiceAuthenticationToken = userToken;

_mobileService.CurrentUser = user;

}

else {

try {

// Login

var user = await _mobileService.LoginAsync(_provider);

// Store credentials

StorageHelper.StoreSetting(USER_ID, user.UserId, true);

StorageHelper.StoreSetting(USER_TOKEN, user.MobileServiceAuthenticationToken, true);

}

catch (InvalidOperationException) {

// Something has gone wrong, most likely user cancelled by // backing-out

success = false;

}

}

return success;

}

StorageHelper is a helper class I wrote to read and write typed settings to storage. You can get it in the code samples.

Now, our users don't have to log in every time they run the app. But what will happen when the token expires? Some OAuth2 providers actually tell us the expiry date of the token, but it's not available to us in MobileServiceUser object. What we can do is look for a request that is failing because it is unauthorized or has expired, and then ask the user to log in. I put together this helper method which takes Task wrapped in a Func so that the task can be executed again if it fails, as shown in the following code snippet:

protected async Task<T> ExecuteAuthenticated<T>(Func<Task<T>> t, int retries = 1) {

int retry = 0;

T retVal = default(T);

while (retry <= retries) {

// If we have no current user, login

if (_mobileService.CurrentUser == null) {

// If login fails return default

if (!await Login())

return retVal;

}

// Try and execute task

try {

retVal = await t();

break;

}

catch (InvalidOperationException ioex) {

// If task has failed because it was unauthorised try again

if (ioex.Message == "Error: Unauthorized" || ioex.Message == "Error: The authentication token has expired.")

{

Logout();

}

retry++;

}

}

return retVal;

}

We can now make any request authenticated, as shown in the following code snippet:

public async Task<IEnumerable<LeaderBoardItem>> GetAll() {

// Make sure we're authenticated by passing the task into // ExecuteAuthenticated

return await this.ExecuteAuthenticated(async () => {

var table =_mobileService.GetTable<LeaderBoardItem>();

return await table.ToEnumerableAsync();

});

}

Notice, we've modified the GetAll method in the LeaderBoard service and have not changed its signature. So, we haven't touched our UI code, and we have now automatically authenticated all our requests. Pretty cool!

Logging out

The MobileServiceClient class has a Logout method that doesn't seem to do anything other than clear the CurrentUser property. It doesn't void the token with the provider when it is called. If we're storing the user token, we'll also need to clear these too so that the app doesn't log the user back in when it relaunches. The Logout method does this for us, as shown in the following code snippet:

public void Logout() {

this._mobileService.Logout();

// Clear credentials

StorageHelper.StoreSetting(USER_ID, null, true);

StorageHelper.StoreSetting(USER_TOKEN, null, true);

}

The DataServiceBase class

Now that we've got all the things we need to log the user in and out, it would be nice to wrap it all up so that it's common for all the data services we want to create. To do this, I've created a base class which has a static instance of MobileServiceClient and the methods we've just discussed, as shown in the following code snippet:

using System;

using System.Threading.Tasks;

using System.Collections.Generic;

using Microsoft.WindowsAzure.MobileServices;

using TileTapper.Models;

using TileTapper.Helpers;

namespace TileTapper.DataServices {

public abstract class DataServiceBase {

private const string USER_ID = "USER_ID";

private const string USER_TOKEN = "USER_TOKEN";

// Our MobileServiceClient instance with Url and application // key set

protected readonly static MobileServiceClient _mobileService = new MobileServiceClient(

"https://tiletapper.azure-mobile.net/",

"IWwHCZGhLgIKxkrBCFwxSGXKHzPLRq15"

);

protected static MobileServiceAuthenticationProvider _provider = MobileServiceAuthenticationProvider.MicrosoftAccount;

protected async Task<T> ExecuteAuthenticated<T>(Func<Task<T>> t, int retries = 1) {

int retry = 0;

T retVal = default(T);

while (retry < retries) {

// If we have no current user, login

if (_mobileService.CurrentUser == null) {

// If login fails return default

if (!await Login())

return retVal;

}

// Try and execute task

try {

retVal = await t();

break;

}

catch (InvalidOperationException ioex) {

// If task has failed because it was unauthorised try

// again

if (ioex.Message == "Error: Unauthorized" ||

ioex.Message == "Error: The authentication token has

expired.")

{

Logout();

}

retry++;

}

}

return retVal;

}

public static async Task<bool> Login() {

// First have a look and see if we have the user's token

var userId = StorageHelper.GetSetting<string>(USER_ID, null);

var userToken = StorageHelper.GetSetting<string>(USER_TOKEN, null);

MobileServiceUser user = null;

if (userId != null && userToken != null) {

// Create user and apply to client

user = new MobileServiceUser(userId);

user.MobileServiceAuthenticationToken = userToken;

}

else {

try {

// Login

user = await _mobileService.LoginAsync(_provider);

// Store credentials

StorageHelper.StoreSetting(USER_ID, user.UserId, true);

StorageHelper.StoreSetting(USER_TOKEN, user.MobileServiceAuthenticationToken, true);

}

catch (InvalidOperationException) {

// Something has gone wrong, most likely user cancelled // by backing-out

}

}

if (user != null) {

_mobileService.CurrentUser = user;

return true;

}

return false;

}

public static void Logout() {

_mobileService.Logout();

// Clear credentials

StorageHelper.StoreSetting(USER_ID, null, true);

StorageHelper.StoreSetting(USER_TOKEN, null, true);

}

}

}

Now, our data services can inherit from this base class, which means that they are really neat and only concerned with data operations—not security. This is shown in the following code snippet:

using System;

using System.Threading.Tasks;

using System.Collections.Generic;

using Microsoft.WindowsAzure.MobileServices;

using TileTapper.Models;

using TileTapper.Helpers;

namespace TileTapper.DataServices {

public class LeaderBoardService : DataServiceBase {

/// <summary>

/// Gets all LeaderBoardItems

/// </summary>

/// <returns>Task to get an enumerable collection of

/// LeaderBoardItem</returns>

public async Task<IEnumerable<LeaderBoardItem>> GetAll() {

// Make sure we're authenticated by passing the task into // ExecuteAuthenticated

return await this.ExecuteAuthenticated(async () => {

var table = _mobileService.GetTable<LeaderBoardItem>();

return await table.ToEnumerableAsync();

});

}

}

}

REST API and the master key

So far, we've seen the app key in action in our app, but we have not really said much about the master key. The master key allows us to access tables and APIs with authentication protection, without authenticating it against our authentication provider. Because themaster key has this capability, it must not be distributed with the mobile applications.

It is handy for administrative tasks as we don't need to implement OAuth2 workflow to access the services. Also, there is a useful feature that allows us to suppress custom scripts implemented on table methods, so we can get base-level CRUD operations on the table without any user customizations such as filtering by user or validation affecting the results.

HTTP requests are authenticated with the following optional headers:

· X-ZUMO-APPLICATION: Application key

· X-ZUMO-AUTH: User auth token

· X-ZUMO-MASTER: Master key

In this example, I used Fiddler (http://fiddler2.com/) to compose some HTTP requests (you can use any HTTP debugging tool you like). We'll preform a GET request on an authentication-protected table (I temporarily changed the LeaderBoard table for this example).

If we just use our app key, as shown in the following request:

GET https://tiletapper.azure-mobile.net/tables/leaderboard HTTP/1.1

X-ZUMO-APPLICATION: XXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxxxxx

Host: tiletapper.azure-mobile.net

We get a 401 Unauthorized response, as shown in the following:

HTTP/1.1 401 Unauthorized

Cache-Control: no-cache

Content-Length: 42

Content-Type: application/json

Server: Microsoft-IIS/8.0

x-zumo-version: Zumo.Main.0.1.6.4247.Runtime

X-Powered-By: ASP.NET

Set-Cookie: ARRAffinity=3b009d5d3272fba37561fb551f1b8cf912175fe784c5b1c8ca93e16259dc3f19;Path=/;Domain=tiletapper.azure-mobile.net

Set-Cookie: WAWebSiteSID=b86a3feb64a4441dbbfaa4b72a1704ea; Path=/; HttpOnly

Date: Tue, 10 Dec 2013 10:31:36 GMT

{"code":401,"error":"Error: Unauthorized"}

Then, if we use our master key as shown in the following request:

GET https://tiletapper.azure-mobile.net/tables/leaderboard HTTP/1.1

X-ZUMO-MASTER: YYYYYYYYYYYYYyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

Host: tiletapper.azure-mobile.net

We get a 200 OK response and our JSON data, as shown in the following:

Response:

HTTP/1.1 200 OK

Cache-Control: no-cache

Content-Length: 468

Content-Type: application/json

Server: Microsoft-IIS/8.0

x-zumo-version: Zumo.Main.0.1.6.4247.Runtime

X-Powered-By: ASP.NET

Set-Cookie: ARRAffinity=3b009d5d3272fba37561fb551f1b8cf912175fe784c5b1c8ca93e16259dc3f19;Path=/;Domain=tiletapper.azure-mobile.net

Set-Cookie: WAWebSiteSID=9d23b57f4f0447b080d0eb78ed69b328; Path=/; HttpOnly

Date: Tue, 10 Dec 2013 10:38:26 GMT

[{"id":"2FD9E522-B276-44F2-9801-A0007B1E1286","timeStamp":"2013-12-09T21:07:55.029Z","name":"Tank Man","score":885562},{"id":"10E4FC3A-B55D-43AA-9104-850A99868C3F","timeStamp":"2013-12-09T21:07:58.415Z","name":"Ultimate Fail","score":0},{"id":"36AB6BD0-716B-4871-8FD8-04B9E43A7DB7","timeStamp":"2013-12-09T21:07:59.315Z","name":"37337","score":999999999},{"id":"5CD944F0-7F2F-489E-B8BC-512FDDB764E6","timeStamp":"2013-12-09T21:08:00.247Z","name":"geoff","score":1000}]

To suppress scripts and go straight into the table, we add the noscript parameter as shown in the following URL:

https://tiletapper.azure-mobile.net/tables/leaderboard?noscript=true

The API supports OData queries, so we can build pretty flexible admin applications. The table methods have the following HTTP methods:

· Query: GET

· Insert: POST

· Update: PATCH

· Delete: DELETE

The POST and PATCH methods place JSON in the request body. In code, this is achieved by writing JSON text into the request stream before reading the response stream.

Summary

We've talked about the importance of security, discussed the options available to control access to our services, and implemented authentication in our app using Windows Live Connect. We've also implemented a base class for managing login, logout, and storing user credentials so the users don't have to log in repeatedly.

In the next chapter, we are going to learn how to customize our service behavior by using scripts.