30 Days of Zumo.v2 (Azure Mobile Apps): Day 12 – Conflict Resolution

One of the common items that everyone has to deal with is conflict resolution. Let me give you the scenario. I have two devices – a web browser and a Universal Windows App. In both cases, I temporarily hold the list of tasks in memory in a list. I update a specific task on the web browser, maybe changing the title. I update the same task on the app, changing it to a different value. What happens?

Well, the answer is “it depends”.

Let’s take the two cases. In the case of my Universal Windows App, I have the following model:

using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json;

namespace Shellmonger.TaskList.Services
{
    public class TodoItem
    {
        public string Id { get; set; }

        [JsonProperty("text")]
        public string Title { get; set; }

        [JsonProperty("complete")]
        public bool Completed { get; set; }

        [JsonProperty("shared")]
        public bool Shared { get; set; }

        [JsonIgnore]
        public bool NotShared { get { return !Shared;  } }
    }
}

When I update the title, I send a HTTP PATCH with the contents of the model being updated converted to JSON. If I do nothing else, the default conflict resolution is “last write wins”. In the specific case that I mentioned earlier, updating the web browser, then updating the app – with this model – the app change overwrites the browser change. Refreshing the browser will show the new value.

What about the other way round? Because the app does not send a version, the version does not get updated. That means last write wins again.

What if you want to do something else? Like, maybe, pop up an error and allow the user to choose? This is possible too, but all your clients need to support conflict resolution. I need to add an extra field called Version to my model for that:

using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json;

namespace Shellmonger.TaskList.Services
{
    public class TodoItem
    {
        public string Id { get; set; }

        [Version]
        public string Version { get; set; }

        [JsonProperty("text")]
        public string Title { get; set; }

        [JsonProperty("complete")]
        public bool Completed { get; set; }

        [JsonProperty("shared")]
        public bool Shared { get; set; }

        [JsonIgnore]
        public bool NotShared { get { return !Shared;  } }
    }
}

The version string is updated to a new version whenever a POST, PATCH or DELETE operation is performed – i.e., when the record is updated. This is done on the server. When I perform the same sequence of requests now, the server will return a 412 Precondition Failed response. This is trapped by the Azure Mobile Apps Client SDK and a MobileServicePreconditionFailedException is thrown. I can trap this myself like this (from TasksPage.xaml.cs):

            StartNetworkActivity();
            try
            {
                Trace($"taskTitle_Changed - updating title ID={item.Id} Title={item.Title} Complete={item.Completed}");
                await dataTable.UpdateAsync(item);
                TaskListView.Focus(FocusState.Unfocused);
            }
            catch (MobileServicePreconditionFailedException<TodoItem> conflict)
            {
                Trace($"taskTitle_Changed - Conflict Resolution for item ${conflict.Item.Id}");
                var dialog = new MessageDialog(conflict.Item.Id, "Conflict Resolution");
                await dialog.ShowAsync();
            }
            catch (MobileServiceInvalidOperationException exception)
            {
                Trace($"taskTitle_Changed - failed to update title ID={item.Id} Title={item.Title} Error={exception.Message}");
                var dialog = new MessageDialog(exception.Message);
                await dialog.ShowAsync();
            }
            StopNetworkActivity();

Put a breakpoint on the trace and inspect the value of the conflict exception – it contains a TodoItem in the Item property. This contains the server copy of the record. You have your copy of the record already.

At this point you have a couple of choices:

  1. You can choose “Client Wins” – update the version in your record to match the version on the server, then re-push the change
  2. You can choose “Server Wins” – copy the entire record from the server into your list
  3. You can somehow merge the results (see later)
  4. You can let the user choose (I will implement this)

Option #3 is worthy of some discussion. Let’s say you have a complicated model. You could add a hidden “dirty” field to each field you have in your model. As the model gets updated, you identify that the field is dirty. When a conflict happens, you can take the server record as a base, copy the dirty fields into the new record, thus creating a “server + my changes” record, then push that record. It’s a lot more complex to handle, but the process is relatively similar to what I Have below.

That leaves option #4. Leave it up to the user. I can present the user with two options – the server version and the client version. I can let the user decide which one wins. If the user chooses “server”, then I replace the client version with the server version. If the user chooses “client”, then I update the version and push the change to the server. That results in something akin to the following code:

            StartNetworkActivity();
            try
            {
                Trace($"taskTitle_Changed - updating title ID={item.Id} Title={item.Title} Complete={item.Completed}");
                await dataTable.UpdateAsync(item);
                TaskListView.Focus(FocusState.Unfocused);
            }
            catch (MobileServicePreconditionFailedException<TodoItem> conflict)
            {
                Trace($"taskTitle_Changed - Conflict Resolution for item ${conflict.Item.Id}");

                // If the two versions are the same, then ignore the conflict - client wins
                if (conflict.Item.Title.Equals(item.Title) && conflict.Item.Completed == item.Completed)
                {
                    item.Version = conflict.Item.Version;
                }
                else
                {
                    // Build the contents of the dialog box
                    var stackPanel = new StackPanel();
                    var localVersion = new TextBlock
                    {
                        Text = $"Local Version: Title={item.Title} Completed={item.Completed}"
                    };
                    var serverVersion = new TextBlock
                    {
                        Text = $"Server Version: Title={conflict.Item.Title} Completed={conflict.Item.Completed}"
                    };
                    stackPanel.Children.Add(localVersion);
                    stackPanel.Children.Add(serverVersion);

                    // Create the dialog box
                    var dialog = new ContentDialog
                    {
                        Title = "Resolve Conflict",
                        PrimaryButtonText = "Local",
                        SecondaryButtonText = "Server"
                    };
                    dialog.Content = stackPanel;

                    // Show the dialog box and handle the response
                    var result = await dialog.ShowAsync();
                    if (result == ContentDialogResult.Primary)
                    {
                        // Local Version - Copy the version from server to client and re-submit
                        item.Version = conflict.Item.Version;
                        await dataTable.UpdateAsync(item);
                    }
                    else if (result == ContentDialogResult.Secondary)
                    {
                        // Just pull the records from the server
                        await RefreshTasks();
                    }
                }
            }
            catch (MobileServiceInvalidOperationException exception)
            {
                Trace($"taskTitle_Changed - failed to update title ID={item.Id} Title={item.Title} Error={exception.Message}");
                var dialog = new MessageDialog(exception.Message);
                await dialog.ShowAsync();
            }
            StopNetworkActivity();

This is obviously not perfect code. Firstly, I don’t have a great story around what happens if other things (other than the title) change. Secondly, I don’t have a great story for what happens if the second update fails. Finally, I don’t have a good story around the “server” acceptance. I just refresh the tasks. However, this is good enough for you to get the general idea.

Updating the JavaScript app

This does not help me in the JavaScript world. I have an Apache Cordova app and a HTML app that are both based around the HTML/JS Client SDK. In the app, I have the following code:

    /**
     * Event handler for when the user updates the text of a todo item
     * @param {Event} event the event that caused the request
     * @returns {void}
     */
    function updateItemTextHandler(event) {
        var itemId = getTodoItemId(event.currentTarget),
            newText = $(event.currentTarget).val();

        updateSummaryMessage('Updating Item in Azure');
        console.info('updateItemTextHandler: itemId = ', itemId);
        todoItemTable
            .update({ id: itemId, text: newText })  // Async send the update to backend
            .then(refreshDisplay, handleError); // Update the UI
        event.preventDefault();
    }

You can find the code in public/application.js for the web browser version. When you run this, you get the following request sent:

day-12-p1

Note that the request does not have the version included. This means that “last write wins”. The server will do the change, ignoring the version field completely. There are several methods by which this can be fixed. My version is to include the version in the record, like this:

    function createTodoItem(item) {
        return $('<li>')
            .attr('data-todoitem-id', item.id)
            .attr('data-todoitem-version', item.version)
            .append($('<button class="item-delete">Delete</button>'))
            .append($('<input type="checkbox" class="item-complete">')
                .prop('checked', item.complete))
            .append($('<div>')
                .append($('<input class="item-text">').val(item.text)));
    }

I can now write a copy of the getTodoItemId(el) method to grab the version of a record:

    /**
     * Given a sub-element of an LI, find the TodoItem Versuin associated with the list member
     *
     * @param {DOMElement} el the form element
     * @returns {string} the Version of the TodoItem
     */
    function getTodoItemVersion(el) {
        return $(el).closest('li').attr('data-todoitem-version');
    }

Now I can use these in the code to add the version attribute to my update:

    function updateItemTextHandler(event) {
        var itemId = getTodoItemId(event.currentTarget),
            itemVersion = getTodoItemVersion(event.currentTarget),
            newText = $(event.currentTarget).val();

        updateSummaryMessage('Updating Item in Azure');
        console.info('updateItemTextHandler: itemId = ', itemId);
        todoItemTable
            .update({ id: itemId, version: itemVersion, text: newText })  // Async send the update to backend
            .then(refreshDisplay, handleError); // Update the UI
        event.preventDefault();
    }

If there is a conflict, the handleError routine will get called and will show a 412 error.

Next Steps

This is not the last time I will deal with conflict resolution, as this topic is different when covering offline sync. In the next post, I’m going to cover the OData interface and all the errors that you can get from the server SDK. I’ll also take a look at a more reasonable version of the table managers I have been using up to now, providing an overall conflict resolution and two-way binding. Until then, here is the code:

30 Days of Zumo.v2 (Azure Mobile Apps): Day 11 – Table Access

There are times when accessing one table for your data is just not enough. Let’s take an example. I have a simple task list app right now. I’ve got it running on all the mobile platforms – iOS, Android and Windows. But I can only see my tasks. What if I want to show my tasks to someone else? I’m going to put together a fairly simple adjustment to my application during this article. Firstly, I’m going to add a “Friends” page to my Universal Windows application – where I can enter the email address of a friend and then they can see my tasks (with a different icon)

The Friends Table

To start with, I need a friends table. This is a new table called friend.js but is just like the todoitem.js table:

var azureMobileApps = require('azure-mobile-apps');

// Create a new table definition
var table = azureMobileApps.table();

// Require authentication
table.access = 'authenticated';

// CREATE operation
table.insert(function (context) {
    context.item.userId = context.user.emailaddress;
    return context.execute();
});

// READ operation
table.read(function (context) {
    context.query.where({ userId: context.user.emailaddress });
    return context.execute();
});

// UPDATE operation
table.update(function (context) {
    context.query.where({ userId: context.user.emailaddress });
    context.item.userId = context.user.emailaddress;
    return context.execute();
});

// DELETE operation
table.delete(function (context) {
    context.query.where({ userId: context.user.emailaddress });
    return context.execute();
});

module.exports = table;

I’ve also created a new Universal Windows client that processes this table. You can find the client at my GitHub repository. It has a People tab and a Tasks tab. The People tab allows you to enter email addresses in to the table so that other people can view your data. Note that this is in no way how I would write this code in reality. There are way too many mechanisms by which you can abuse this. However, it’s good to view as a proof of concept. I’m also not going to cover the .NET client – this is about the backend code, not the frontend.

The question for this blog post is this: How do I implement a mechanism whereby my table view is limited by another table? In this case, I want to view all records that I am allowed to see – either because I own them or because someone has given me permission to view them. This will be a change to the read() method in the todoitem.js table controller. I am splitting this into two halves. In the first half I will get a list of all the user IDs that I can view:

// READ operation
table.read(function (context) {
    return context.tables('friend')
        .where({ viewer: context.user.emailaddress })
        .select('userId')
        .read()
        .then(function (friends) {
            console.log('READ: Response from SQL Query = ', friends);
            var list = friends.map(function (f) { return f.userId; }).push(context.user.emailaddress);
            console.log('READ: List of user ids = ', list);
            
            // TODO: Adjust the query according to the requirements
            return context.execute();
        });
});

Let’s take this line by line. Line 18 gets a reference to the friend table. I then construct a query whereby all records where the current user is marked as a viewer. The table itself has just two columns – userId is the “owner” and viewer is the email address I place in the table in the UI. It reads “viewer can read the tasks for userId”. Once I’ve got that, I’m going to limit the returned columns to just userId and then finally (line 21) execute the query and read the results. The result of this is a Promise that resolves to the list of userIds. I’ll get an array of objects – each object will have one property (the userId field).

There are more methods that you can use that are “LINQ-like”. I’ve already introduced where, select and read. You can also use:

  • orderBy(field) and orderByDescending(field) to do sorting
  • skip(count) and take(count) to implement paging
  • includeTotalCount() to add a total number of records field to the results

Once I’ve resolved the promise, I need to convert the list to a list of email addresses – these become the list of email addresses to use as the viewership. Notice that I add the current users email address to the list.

Next, I need to adjust my search of the database to return tasks owned by anyone in the list of email addresses I’ve just created. This is actually quite hard to do. One of the methods of altering the query that is sent to the database is to provide a function:

// READ operation
table.read(function (context) {
    return context.tables('friend')
    .where({ viewer: context.user.emailaddress })
    .select('userId')
    .read()
    .then(function (friends) {
        var list = friends.map(function (f) { return f.userId; })
        list.push(context.user.emailaddress);
        context.query.where(function(list) { return this.userId in list; }, list);
        return context.execute().then(function (results) {
          // Process the results
          console.log('results = ', results);
          return results;
        });
    });
});

This is a different mechanism to altering the query. Underneath, the query is compiled to an AST by esprima, and then converted to SQL from the compiled form. You never have to worry about this until it bites you because you see JavaScript and assume you can use any JavaScript function in there where method. There is a limited set of things you can do within a function call. In the context of the function, this refers to the current item. You can pass in one or two arguments (such as lists) and anything you need to use must be passed in. You can’t rely on the context that the function appears to be running in – it’s not running a function. Once inside the function, there is a set of methods you can use:

  • toUpperCase()
  • toLowerCase()
  • trim()
  • indexOf()
  • concat()
  • substring() or substr()
  • replace()
  • getFullYear()
  • getUTCFullYear()
  • getYear()
  • getDate()
  • getUTCDate()
  • in

Obviously, most of these only work on specific datatypes – strings and dates, most normally. However, you can use in with arrays. You can also use math capabilities as well as boolean logic inside the function. I’m using this functionality to change the query to be “records I am allowed to view”. My final step in this rather complicated read logic is to change the results so that I return a computed “shared” flag:

// READ operation
table.read(function (context) {
    return context.tables('friend')
    .where({ viewer: context.user.emailaddress })
    .select('userId')
    .read()
    .then(function (friends) {
        var list = friends.map(function (f) { return f.userId; })
        list.push(context.user.emailaddress);
        context.query.where(function(list) { return this.userId in list; }, list);
        return context.execute().then(function (results) {
          results.forEach(function (item) {
            // Item is shared if the owner of item is not current owner
            item.shared = (item.userId !== context.user.emailaddress);
          });
          return results;
        });
    });
});

Here, the highlighted lines add in a computed field called shared that is true when the record does not belong to the current user. I also need to add in some logic to REMOVE the shared flag from the incoming records:

// CREATE operation
table.insert(function (context) {
    context.item.userId = context.user.emailaddress;
    delete context.item.shared;
    delete context.item.Shared;
    return context.execute();
});

// UPDATE operation
table.update(function (context) {
    context.query.where({ userId: context.user.emailaddress });
    context.item.userId = context.user.emailaddress;
    delete context.item.shared;
    delete context.item.Shared;
    return context.execute();
});

Next Steps

Today, I’ve gone through the more esoteric adjustments to the query you can do, doing sub-queries and manipulating the results. In the next article, I’m going to start looking at doing conflict resolution.

In the mean time, you can download my client and server:

30 Days of Zumo.v2 (Azure Mobile Apps): Day 10 – Middleware

I’ve covered the basics of table controllers in the last two posts, covering both per-table configuration and per-operation configuration. Today I’m going to talk about extensibility and middleware. Middleware is a fairly basic concept – it provides a capability of inserting custom code into the HTTP request/response pipeline. Express has a whole section on middleware and pretty much all Express modules are written as middleware. Here is a template:

function myMiddleware(request, response, next) {
    // Do something with request

    // Return something with response or next

    next();  // Call the next middleware
}

app.use(myMiddleware);

Azure Mobile Apps is written as a set of middleware, but it’s been constructed to be interchangable. Let’s look at a sample service. Here is the app.js:

var app = require('express')(),
    mobile = require('azure-mobile-apps')();

app.use(function (req, res, next) {
  console.log('express.use - azureMobile = ', typeof req.azureMobile !== 'undefined');
  return next();
});

zumo.use(function (req, res, next) {
  console.log('zumo.use - azureMobile = ', typeof req.azureMobile !== 'undefined');
  return next();
});

zumo.tables.import('./tables');
zumo.tables.initialize().then(() => {
  app.use(zumo);
  app.listen(3000);
});

And my sample table:

var table = require('azure-mobile-apps').table();

table.use(function (req, res, next) {
  console.log('table.use: azureMobile = ', typeof req.azureMobile !== 'undefined');
  return next();
}, table.operation);

table.read(function (context) {
  console.log('tableop: azureMobile = ', typeof context.req.azureMobile !== 'undefined');
  return context.execute();
});

module.exports = table;

If you run this locally and do a GET of the /tables/todoitem endpoint, you will see the following console output:

express.use - azureMobile = false
zumo.use - azureMobile = true
table.use: azureMobile = true
tableop: azureMobile = true

This gives you a very clear indication of what is available and the most appropriate place to add code. As an example, let’s say the majority of your code depends on the email address being available. For example, you are doing the personal table. You may want to add the email address to the user object. You can easily do this with a zumo.use middleware. Add a file authMiddleware.js with the following contents:

var authCache = {};

function authMiddleware(request, response, next) {
  if (typeof request.azureMobile.user.id !== 'undefined') {
    if (typeof authCache[request.azureMobile.user.id] !== 'undefined') {
      request.azureMobile.user.emailaddress = authCache[request.azureMobile.user.id];
      next();
    }
    request.azureMobile.user.getIdentity().then(function (userInfo) {
      if (typeof userInfo.aad.claims.emailaddress !== 'undefined') {
        authCache[request.azureMobile.user.id] = userInfo.aad.claims.emailaddress;
        request.azureMobile.user.emailaddress = authCache[request.azureMobile.user.id];
      }
      next();
    });
  } else {
    next();
  }
}

module.exports = authMiddleware;

This code does some primitive caching of the result from getIdentity(), but otherwise pulls out the email address just like I did in the Personal Table article. Instead of just using it, though, it puts it in the context for use later.

What’s this request.azureMobile? It’s the context and has all the same properties that I discussed in the table operations article.

Now that I have my auth middleware written, I can use this to adjust my original personal table. Firstly, I need to link it into the app.js:

var express = require('express'),
    serveStatic = require('serve-static'),
    azureMobileApps = require('azure-mobile-apps'),
    authMiddleware = require('./authMiddleware');

// Set up a standard Express app
var app = express();
var mobileApp = azureMobileApps({
    homePage: true,
    swagger: true
});

mobileApp.use(authMiddleware);
mobileApp.tables.import('./tables');
mobileApp.api.import('./api');

I can now adjust my table controller to make it much more simple:

var azureMobileApps = require('azure-mobile-apps');

// Create a new table definition
var table = azureMobileApps.table();

// Require authentication
table.access = 'authenticated';

// CREATE operation
table.insert(function (context) {
  context.item.userId = context.user.emailaddress;
  return context.execute();
});

// READ operation
table.read(function (context) {
  context.query.where({ userId: context.user.emailaddress });
  return context.execute();
});

// UPDATE operation
table.update(function (context) {
  context.query.where({ userId: context.user.emailaddress });
  context.item.userId = context.user.emailaddress;
  return context.execute();
});

// DELETE operation
table.delete(function (context) {
  context.query.where({ userId: context.user.emailaddress });
  return context.execute();
});

module.exports = table;

Note that I’m not using getIdentity() any more. For reference, here is what my read method used to look like:

table.read(function (context) {
    return context.user.getIdentity().then(function (userInfo) {
        context.query.where({ userId: userInfo.aad.claims.emailaddress });
        return context.execute();
    });
});

I’ve isolated my getIdentity() call into the middleware so that the email address is available everywhere.

Doing Some Authorization

You can also do some basic authorization using the same technique. For example (assuming the list is small), you can configure Azure AD to provide the list of groups in a claim. Here is how to do it. To configure AAD to produce the groups as claims:

  1. Log onto the Azure Portal and go to your Azure AD Configuration.
  2. Click on Manage Manifest (at the bottom of the page), then Download Manifest – this will download a JSON file.
  3. Edit the downloaded file with a JSON editor (I use Atom)
  4. Set the “groupMembershipClaims” value to “SecurityGroup”

    day-10-p1

  5. Save the file and upload it by using Manage Manifest -> Upload Manifest

  6. Click on the APPLICATIONS tab
  7. Click on your Web Application (not the Native Client)
  8. Scroll to the bottom under “permissions to other applications”
  9. Click on the Delegated Permissions and check the box next to “Read directory data”

    day-10-p2

  10. Click on Save

Now that you have configured the application to return groups, you need a group to return.

  1. Click on the back arrow to return to the top level of your directory
  2. Click on the GROUPS tab
  3. Click on ADD GROUP in the bottom banner
  4. Enter a name for the group, set the Group Type to Security, then click on the Tick.
  5. Add a member using the ADD MEMBER button in the bottom banner
  6. Click on the user you wish to add, then click on the + in a circle. Repeat for multiple users.
  7. Finally, click on the tick.
  8. Click on PROPERTIES and make a note of the Object ID

The Object ID is the GUID of the group and is what will show up in the groups list. After doing all this and re-running your client, the getIdentity() method will return the group information in the returned value, but it’s not in the most accessible manner:

day-10-p3

Note the item highlighted in user_claims. This is the following, when properly laid out:

"user_claims": [
    // Some other stuff
    { "typ": "groups", "val": "0d270bc3-6431-4d9c-8a05-ba7fa0b5463c" },
    { "typ": "groups", "val": "92d92697-1242-4d38-9c1d-00f3ea0d0640" },
    // More stuff
],

We can convert this into something usable using the following (this is the authMiddleware.js file):

var authCache = {};

/**
 * Reducer method for converting groups into an array
 * @param {string[]} target the accumulator for the array
 * @param {object} claim the current claim being processed
 * @returns {string[]} the accumulator
 */
function groupReducer(target, claim) {
    if (claim.typ === 'groups')
        target.push(claim.val);
    return target;
}
/**
 * Middleware for adding the email address and security groups to the
 * request.azureMobile.user object.
 * @param {express.Request} request the Express request
 * @param {express.Response} response the Express response
 * @param {function} next the next piece of middleware
 * @returns {any} the result of the next middleware
 */
function authMiddleware(request, response, next) {
    if (typeof request.azureMobile.user === 'undefined')
        return next();
    if (typeof request.azureMobile.user.id === 'undefined')
        return next();

    if (typeof authCache[request.azureMobile.user.id] === 'undefined') {
        request.azureMobile.user.getIdentity().then(function (userInfo) {
            var groups = userInfo.aad.user_claims.reduce(groupReducer, []);
            console.log('userInfo.aad.claims = ', userInfo.aad.claims);
            var email = userInfo.aad.claims.emailaddress || userInfo.aad.claims.upn;
            authCache[request.azureMobile.user.id] = {
                emailaddress: email,
                groups: groups
            };

            request.azureMobile.user.emailaddress = authCache[request.azureMobile.user.id].emailaddress;
            request.azureMobile.user.groups = authCache[request.azureMobile.user.id].groups;
            next();
        });
    } else {
        request.azureMobile.user.emailaddress = authCache[request.azureMobile.user.id].emailaddress;
        request.azureMobile.user.groups = authCache[request.azureMobile.user.id].groups;
        next();
    }
}

module.exports = authMiddleware;

Lines 9-12 are a reducer function for converting the object-version of the claims into an array. This is used in line 32 to convert the user_claims into an array that I can actually use. I’ve done the same thing as I did in the case of the email address. Now I can use that in my table controller to implement group-based authorization. For that, I need the object ID of the group I am interested in. For the purposes of this test, I’ve got a group called Administrators with an Object ID of 92d92697-1242-4d38-9c1d-00f3ea0d0640 – this was obtained from the Azure AD configuration screens in the Azure Portal. Authorization is easily done within the table controller with some code:

// DELETE operation
table.delete(function (context) {
    console.log('DELETE: context.user = ', context.user);
    // Authorization - if Administrators is not in the group list, don't allow deletion
    if (context.user.groups.indexOf('92d92697-1242-4d38-9c1d-00f3ea0d0640') < 0) {
        console.log('user is not a member of Administrators');
        context.res.status(401).send('Only Administrators can do this');
        return;
    }

    console.log('user is a member of Administrators');
    context.query.where({ userId: context.user.emailaddress });
    return context.execute();
});

If you log in as a member of the Administrators group, then you will be able to delete. You will see an error message for others.

Of course, this isn’t really convenient. I mean – who wants to code up the authorization for each group? Instead, let’s show off a little middleware for the operation. First the middleware, which I’ve put at the top of the file, but could just as easily be in its own file:

function isAdministrator(request, response, next) {
    if (request.azureMobile.user.groups.indexOf('92d92697-1242-4d38-9c1d-00f3ea0d0640') < 0) {
        response.status(401).send('Only Administrators can do this');
        return;
    }
    next();
}

I do the same check as before in this middleware. If the user is not a member of the required group, then the same response (401 Unauthorized) with a custom message is done. You may remember that I can integrate middleware at the table level using this:

table.use(isAdministrator, table.operation);

I can also do this on a per-operation basis. The table.use is just a shortcut for the individual operations. So:

table.delete.use(isAdministrator, table.operation);

Putting it together, I can code my delete script as follows:

// DELETE operation
function isAdministrator(request, response, next) {
    if (request.azureMobile.user.groups.indexOf('92d92697-1242-4d38-9c1d-00f3ea0d0640') < 0) {
        response.status(401).send('Only Administrators can do this');
        return;
    }
    next();
}

table.delete.use(isAdministrator, table.operation);
table.delete(function (context) {
    context.query.where({ userId: context.user.emailaddress });
    return context.execute();
});

Next Steps

Authorization is a major milestone for most real applications. However, we are still operating within a single table without any interactivity between tables. I’d like to adjust that with my next article – going both multi-table and providing linkage between the tables.

Until then, I’ve uploaded my code to my GitHub Repositories. To make it easy, I’ve separated server and client.

30 Days of Zumo.v2 (Azure Mobile Apps): Day 9 – Table Controller Operations

I introduced the table controller in my last article and showed off all the things you can do in defining tables at the table level. All the settings are common across all operations to the table. This includes schema and authorization settings. Today, I am looking at individual operations. A table controller provides an OData v3 data source with GET, POST, PUT and DELETE operations.

The per-operation settings

There are four operations on a table, corresponding to the four operations in a typical CRUD controller:

  • CREATE is table.insert
  • READ is table.read
  • UPDATE is table.update
  • DELETE is table.delete

There is also a table.undelete() – this is a special case of a POST with an ID to undelete a previously deleted record. Soft delete must be enabled for this option. If you want to set an option on the READ operation, you would set it on table.read, for example. Let’s look at some:

Per-Operation Access Control

Last time, I showed you how to specify the default access control: something like this:

var table = require('azure-mobile-apps').table();

table.access = 'authenticated';

module.exports = table;

You can also specify access control on a per-operation basis. For example, let’s look at a read-only table:

var table = require('azure-mobile-apps').table();

table.access = 'disabled';
table.read.access = 'anonymous';

module.exports = table;

Note that I specify the default permission using the table.access, then override it with individual operation access permissions. Another common pattern would be the ability to read anonymously, but only update when authenticated:

var table = require('azure-mobile-apps').table();

table.access = 'authenticated';
table.read.access = 'anonymous';

module.exports = table;

Yet another option would be a table where you can’t delete or update anything – only insert into the table:

var table = require('azure-mobile-apps').table();

table.access = 'authenticated';
table.update.access = 'disabled';
table.delete.access = 'disabled';

module.exports = table;

In all cases, the same logic as for table.access applies:

  • ‘authenticated’ requires the submission of a valid X-ZUMO-AUTH token – respond with 401 Unauthorized if not validly authenticated.
  • ‘anonymous’ allows anyone to perform the operation
  • ‘disabled’ means that the server responds with 405 Method not allowed

Per-operation Middleware

You can also define a method of executing some additional code during the execution of the operation. I’ve already shown an example this when I was demonstrating the personal table – obtaining the email address so that I can use it in my table. The general form here is:

var table = require('azure-mobile-apps').table();

table.access = 'authenticated';

table.read(function (context) {
    // Pre-operation tasks

    return context.execute().then((response) => {
        // Post-operation tasks
    });
});

module.exports = table;

Common pre-operation tasks are authorization, query adjustment and field insertion. I showed off examples of the latter two in the Personal Tables article – query adjustment to only show records that belong to the authenticated user and field insertion to insert the users email address. I’ll cover authorization in another article.

The Operation Context

When the operation middleware is called, a context is passed. The context has the following properties:

Property Type Usage
query queryjs/Query The query to be executed
id string The record ID
item object The inbound record
req express.Request The request object
res express.Response The response object
data data The data provider
push notification hub The push provider
logger winston The logger provider
tables function Accessor for tables
table object The current table
user object The authenticated user
execute function Execution function

The query, id and item properties are used in various operations:

Property Used in
query read (search), update
id read (fetch), delete
item insert, update

The req and res options are from the express object – you can use these to access other headers and cookies or directly manipulate the response. Most of the other properties allow you to access pre-configured services. I’ve already introduced the user object – the only thing guaranteed there is the user.id value – everything else is optional, although the user object also has a getIdentity() method. Finally, we get to the execute() method. This executes the query and returns a Promise that resolves (eventually) to the response. However, that doesn’t stop you from altering the response as you go.

Let’s take a quick look at an example. Let’s say we adjust our existing TodoItem.js script like this:

// READ operation
table.read(function (context) {
  console.log('context.user = ', JSON.stringify(context.user));
    return context.user.getIdentity().then(function (userInfo) {
        console.log('userInfo.aad.claims = ', JSON.stringify(userInfo.aad.claims));
        context.query.where({ userId: userInfo.aad.claims.emailaddress });
        return context.execute();
    }).then(function (response) {
        console.log('read.execute response = ', response);
        return response;
    });
});

All this code does is print out the response to the log. You’ll need to turn on application logging at a verbose level to view this. When you do (and run your application), here is what you will see:

day-8-p1

The response in this case (for a read/search) is an array of results. We can use this fact to do some sort of business logic.

Business Logic like what?

Well, we’ll get into some very specific things dealing with Push Notifications later on in the course. However, some things could be to asynchronously kick off a queue submission. As a quick example, let’s say you are using the Azure Storage API to store images. Once the image is stored, you update the database with a unique ID and some meta-data on the image. But you want to add a watermark, resize the image, or notify someone that an image is available for review. You could do this in a post-execute phase.

Next Steps

Middleware is an incredibly powerful functionality that allows you to do pre- and post-processing on requests on a per-operation basis. The last two articles have been just introducing the table controller functionality. In the next article, I’m going to use this functionality to provide authorization capabilities to my operations in a way that, hopefully, can be considered somewhat boilerplate. Until then, you can find todays code at my GitHub Repository.

30 Days of Zumo.v2 (Azure Mobile Apps): Day 8 – Table Controller Basics

Over the last six articles, I’ve covered just about everything there is to do about Authentication. It’s time to turn my attention to data. Specifically, SQL table data. Azure Mobile Apps has two SDKs on the server side – an ASP.NET 4.6 version and a Node.js version. The node.js version is also used for the “in-portal” version of Azure Mobile Apps. I’m going to do another series of Zumo Days explicitly about the ASP.NET version of Azure Mobile Apps, so stay tuned for that.

So, what is a Table Controller?

If you strip away all the “mobile” specific pieces of Azure Mobile Apps, it’s an OData v3 data source at its core. A table controller responds to the GET, POST, PUT and DELETE HTTP requests and gives you data within the SQL table that backs it.

The bad way of defining a Table Controller

You don’t need anything special to define a table controller. In you app.js file, do the following:

var express = require('express'),
    zumo = require('azure-mobile-apps');

var app = express(), 
    mobile = zumo();

mobile.tables.add('todoitem', tableDefinition);

// Rest of the app definition

We will get to the table definition later on. This is a bad way of defining a table. It doesn’t provide any separation of duties. It just mixes up the configuration of the tables in code. Don’t do this.

The good way of defining a Table Controller

Create a tables directory. For each table that you need to expose to your mobile applications, create a JavaScript file with the same name. For example, let’s look at the TodoItem table. This would be called todoitem.js, with the following contents:

var table = require('azure-mobile-apps').table();

module.exports = table;

In your app.js file, you need to import your table files:

var express = require('express'),
    azureMobileApps = require('azure-mobile-apps');

var app = express(),
    zumo = azureMobileApps({
    homePage: true,
    swagger: true
});

zumo.tables.import('./tables');
app.use(zumo);
app.listen(process.env.PORT || 3000);

Defining a Table

There are several options that you can define on a table – the tableDefinition from earlier. Here are all the options:

  • name – the name of the endpoint. You don’t need to set this – it’s set from the name of the file (in import mode) or the first parameter of the add() method.
  • databaseTableName – the name of the SQL table you are using for this endpoint. It’s assumed to be the same as name if you don’t see it.
  • schema – the schema that the SQL table is in. By default, that’s the dbo schema, or whatever is set in the MS_SchemaName app setting.
  • access – one of ‘anonymous’, ‘authenticated’ or ‘disabled’ – controls access to this endpoint.
  • dynamicSchema – a boolean. If true, turns dynamic schema on (more on this later).
  • columns – a definition of the model to export if dynamicSchema is not turned on.
  • maxTop – the maximum number of rows to return in one page of results.
  • softDelete – true if soft delete is turned on.

You can define all of these in your table definition file like this:

var table = require('azure-mobile-apps').table();

table.name = 'todoitem';
table.schema = 'dbo';
table.databaseTableName = 'Tasks';
table.access = 'disabled';
table.dynamicSchema = true;
table.maxTop = 1000;
table.softDelete = false;

module.exports = table;

Dynamic or Static Schema?

The node.js Server SDK defines four data types for you to use:

  • string converted to NVARCHAR(255)
  • number converted to FLOAT(53)
  • boolean converted to BIT
  • datetime converted to DATETIMEOFFSET(7)

The “converted to” indicates what column type the value will be in the SQL Azure table. If you turn dynamicSchema on by setting the property to true in the table definition, then the SDK will accept any field and create fields if necessary. This is great during development because you don’t have to think about what your tables are going to look like – you just send data to the backend and it will store it. However, this is really problematic during production – malicious users can overwhelm your database and store data secretly, using up your data allotment. You will want to lock down the schema before going to production. Let’s take a look at a more typical todoitem schema:

var table = require('azure-mobile-apps').table();

table.dynamicSchema = false;
table.columns = {
    userid: 'string',
    text: 'string',
    complete: 'boolean',
    due: 'datetime',
    alert: 'number'
};

module.exports = table;

This shows off all the data types. There is one more wrinkle here. The Azure Mobile Apps SDK creates tables defined with dynamic schema turned on automatically on first insert. That table creation doesn’t happen automatically when you are using static schema. You have to initialize your database like this:

var express = require('express'),
    azureMobileApps = require('azure-mobile-apps');

var app = express(),
    zumo = azureMobileApps({
    homePage: true,
    swagger: true
});

zumo.tables.import('./tables');
zumo.tables.initialize().then(() => {
    app.use(zumo);
    app.listen(process.env.PORT || 3000);
});

The initialize() method creates the database, returning a Promise that resolves when the database creation is complete. In this case, I don’t start listening for requests until the database is created. If I don’t do that then there is the possibility that I receive a request that I cannot fulfill – the SDK will return a HTTP 500 status (Internal Server Error) in this case. If you have dynamic schema turned on then initialize() becomes a no-op. As a result, you should ALWAYS include the initialize() call to ensure your users never get a 500 Internal Server Error. In fact, I recommend you never use dynamic schema – it’s a bad idea unless you are just playing around.

Seeding Data

Since you are running initialize() all the time now, you may want to store some data by default:

var table = require('azure-mobile-apps').table();

table.dynamicSchema = false;
table.columns = {
    userid: 'string',
    text: 'string',
    complete: 'boolean',
    due: 'datetime',
    alert: 'number'
};

table.seed = [
  { userid: '', text: 'My First Item', complete: false, due: null, alert: '7' }
];

module.exports = table;

When you have a seed property, the data contained within the array will be pushed into the table when the table is created. If the table already exists (and so initialize() skips over the table), then seeding doesn’t happen.

Controlling Access

You can control access to the table as a whole with the access property:

  • ‘authenticated’ means you must be logged in to access the endpoint. If you are not authenticated, a 401 Unauthorized response will be returned
  • ‘anonymous’ means anyone can access the endpoint.
  • ‘disabled’ means the table is not visible. The server will return a 405 Method Not Allowed response.

Normally, you would only use authenticated as an access value at the table level. You can also set access at the operation level, which is why this option exists. Anonymous access is the default.

Soft Delete

When a user sends a HTTP DELETE operation, there are two mechanisms. Firstly, you can actually delete the record – a hard delete. The record is removed from the database table immediately. In the second case, a soft delete, the deleted property on the record is set to true. It will no longer be returned by the server unless explicitly requested. This is useful in multi-client situations where some clients are using offline sync. The sync table will be updated to remove the record. You will, however, have to manually remove the actual database record – it will no longer be removed from the database through the SDK.

You can configure soft delete as an option on the table like this:

table.softDelete = true;

Next Steps

Today I took a look at almost all the table level operations that you can perform with the Azure Mobile Apps SDK. There is one more, but I’ll leave that for a later post as it’s particularly awesome. I know – teaser! In the next article, I’m going to go down one level and look at what I can configure at the HTTP operation level. In the mean time, you can review the API documentation for the Azure Mobile Apps SDK.

30 Days of Zumo.v2 (Azure Mobile Apps): Day 7 – Refresh Tokens

So far in this series, I’ve look at the following:

Today I’m going to cover a common problem within token-based authentication. Every token that is given out has some in-built security – an expiry time. How do you deal with the expiry of a token? Facebook expires tokens once every 60 days (not exactly a problem). However, most authentication providers are much smaller – Azure Mobile Apps, Azure AD and Google all use 1 hour – a number in common with a lot of providers. Some providers use very short times (miiCard, for example, uses 2 minutes, which is crazy as it practically guarantees you need to do two API calls instead of 1 to do something). To ensure that the user is still authenticated, identity providers implement a refresh token mechanism. Today, we are going to look at integrating refresh tokens into our client and server authentication flows. If you are using Custom Authentication, then this is another topic you have to deal with this yourself. Sorry – there are no generalized rules here.

Refresh Tokens

You’ve logged in (somehow) and received a token from Azure Mobile Apps. Here is a little secret. It expires after 1 hour. Actually, that’s not much of a secret. It’s kind of well-known. What isn’t well known is how to deal with it. The short version of this is that there is an endpoint available for refreshing all your authentication tokens at the same time. The response is a new token. You have to submit the old token. Some of the Azure Mobile Apps client SDKs will include a method InvokeApiASync() that does this for you. The Apache Cordova Plugin doesn’t, but it does have a _request() method that I can use instead. Let’s start by adding a button to interactively call the refresh API:

    function initializeApp() {
        $('#wrapper').empty();

        // Replace the wrapper with the main content from the original Todo App
        var content =
              '<div id="wrapper">'
            + '<article>'
            + '  <header>'
            + '    <div id="title"><h2>Azure</h2><h1>Mobile Apps</h1></div><div id="rt-title"><button id="reftoken">Token</button></div>'
            + '    <form id="add-item">'
            + '      <button id="refresh">Refresh</button>'
            + '      <button id="add">Add</button>'
            + '      <div><input type="text" id="new-item-text" placeholder="Enter new task" /></div>'
            + '    </form>'
            + '  </header>'
            + '  <ul id="todo-items"></ul>'
            + '  <p id="summary">Initializing...</p>'
            + '</article>'
            + '<footer><ul id="errorlog"></ul></footer>'
            + '</div>';
        $('#page').html(content);

        // Refresh the todoItems
        refreshDisplay();

        // Wire up the UI Event Handler for the Add Item
        $('#add').on('click', addItemHandler);
        $('#refresh').on('click', handleRefresh);
        $('#reftoken').on('click', handleTokenRefresh);
    }

Line 62 adds the button – I can make some CSS to push the entire DIV right and clean it up. Line 82 adds a click handler for the button. This is fairly standard HTML and JavaScript code so far. Let’s take a look at the event handler:

    /**
     * Event Handler for clicking on the token button
     */
    function handleTokenRefresh(event) {
        event.preventDefault();

        client._request('GET', '/.auth/refresh', function (error, response) {
            console.error('refresh error = ', error);
            console.info('refresh response =  ', response);
        });
    }

Configuring OpenID Connect “Hybrid Flow”

Spoiler: It doesn’t work

I got a 400.80 Bad Request response from the server. That’s because I have not configured the Azure AD service to use refresh tokens. App Service Authentication / Authorization express configuration uses the OpenID Connect “Implicit Flow”, which is convenient because it doesn’t require a secret key, but has the disadvantage of not allowing you to get a refresh token. To get a refresh token, I need to configure the OpenID Connect “Hybrid Flow”.

Log into the Azure Portal, select Browse> and then Active Directory (it will be at the top)

Quick Tip Click on the star next to Active Directory – it will make the link appear in the side bar so you don’t need to click on Browse.

After the directory appears (you may need to click on it if you have more than one), click on the APPLICATIONS tab, then click on your application followed by the CONFIGURE tab.

day-7-p1

Scroll down to the keys section. Select Duration = 2 years in the drop-down, then click on Save. (Don’t forget to come back and re-issue the key before the 2 years are up!) Copy the key that is generated after you saved.

day-7-p2

Now, go back to the Azure Portal, select All Resources or App Services, then select your app service. Mine is 30-days-of-zumo-v2. Click on Tools, then Resource Explorer, then Go

day-7-p3

You will get a new tab pointing to your site inside of https://resources.azure.com – an awesome site for viewing all the settings. Expand config and then authSettings:

day-7-p4

Because this is a dangerous place, it’s set to read-only mode. Click on the grey Read/Write box at the top of the screen, then click on Edit next to the PUT button. I need to set two things. Firstly, the key that I created in the Azure AD portal needs to be copied into the clientSecret field as a string. Secondly, I need to set the additionalLoginParams to ["response_type=code id_token"], like this:

day-7-p5

Note: Client Secrets and keys should be kept secret. I re-generated mine immediately after these screen shots were made.

Once you are happy with your edits (double-check them as they are impacting your site significantly), click on the PUT button at the top. It’s a good idea to put the interface into Read-only mode after you have made the edits – this prevents accidents from happening.

You’ve now configured a minimal Hybrid Flow. Note that this is the same flow you would need in order to access the AAD Graph API. However, there are a couple of extra steps. Refer to this blog post for more details.

The Refresh Response

Now that I’ve got the configuration done, my application will run again and will actually get a response. If you run the client with the logging, you will notice that error is null (indicating no error) and response is a standard XmlHttpRequest object. The _request() method is an under the covers method and isn’t meant to be run directly. Looking at the response from the server in the Network tab, I see the following:

day-7-p6

The authenticationToken is the new token that I need to store in the client.currentUser.mobileServiceAuthenticationToken (note that capitalization changes between Client SDKs to conform to the client conventions). I can now deal with the refresh token thusly:

    /**
     * Event Handler for clicking on the token button
     */
    function handleTokenRefresh(event) {
        event.preventDefault();

        client._request('GET', '/.auth/refresh', function (error, response) {
            if (error != null) {
                console.error('Auth Refresh: Error = ', error);
                alert('Authentication Refresh Failed');
            } else if (response.status !== 200) {
                console.warn('Auth Refresh: Status = ', response.status, response.statusText);
            } else {
                var data = response.response;
                client.currentUser.mobileServiceAuthenticationToken = data.authenticationToken;
                console.info('Auth Refresh: New Token Received and stored');
            }
        });
    }

How to use Refresh Tokens

Of course, handling refresh tokens in an interactive way is definitely not the way to go. This is meant to be a hands-off approach to dealing with refresh tokens. My personal favorite is to write a method that returns a Promise and deals with it all for you:

  1. If you are not authenticated, then call the authenticate routine – resolve the Promise when authenticate returns successfully.
  2. If you are authenticated and it’s been more than 30 minutes (for example) since the last refresh, refresh the token then resolve the Promise when the refresh returns successfully.
  3. If the refresh fails, call the authenticate routine and resolve the Promise when authenticate returns successfully.

In this way you can use the authenticate-or-refresh method like this:

// pseudo-code only - do not use this as is
authenticateOrRefresh().then(() => {
   client.insertAsync(todoItem);
});

For the purposes of this topic, I’ve just checked in the interactive refresh code. You can get it from my GitHub Repository.

What about Custom Authentication?

If you are using a third-party identity provider, then you will need to do the same sort of logic against the third party identity provider. You can read about the process for Auth0 on their site, for example. The underlying process will pretty much be the same, however. Configuration and URLs or SDKs will differ. Understand what it takes to get a refresh token and how often you need to request one.

Next Steps

We will be leaving our review of authentication in Azure Mobile Apps now. I recommend you follow Chris Gillumhis blog contains lots of additional information on the authentication system used by Azure App Service. In fact, this blog post could not have happened without him. If he hasn’t written about it, then just ask.

In my next article, I’ll move onto table controllers. We kind of got a taste of table controllers in the last article, but I’ll be going much deeper next.

30 Days of Zumo.v2 (Azure Mobile Apps): Day 5 – Custom Authentication

Over the past two articles, I’ve explored using a server-flow and a client-flow technique to authenticate users to Azure AD and accessing information with Azure Mobile Apps. However, there are times I don’t want social authentication – I want users to sign up to my service and deal with registration, forgotten passwords, etc. Maybe I want to link to an existing user database, or maybe I want to use something that Azure Mobile Apps doesn’t support – like Weibo (ok – that’s a stretch since I don’t speak Chinese). What then?

Azure Mobile Apps is a pro-dev solution. Pretty much everything is changeable or optional. In this tutorial, I’m going to set up Auth0 as a source. Last time I looked at Auth0, it was being used as a client-flow where the token was swapped for a zumo token. That only works if the identity provider is supported by Azure Mobile Apps. This time, I’m going to integrate custom authentication which isn’t supported by Azure Mobile Apps. To do this, I’m going to get the service to accept the Auth0 identity token rather than swapping the token for a zumo token.

Start by setting up Auth0

I’m going to configure a brand new application in Auth0. Sign into the Auth0 dashboard (you have an account, right?). Click on Apps / APIs, then on the big New App/API and give it a name. Then click over to the Settings tab for the new application.

Screen Shot 2016-03-19 at 7.38.23 PM

Add the mobile URI (which is the auth0 tenant domain followed by /mobile) to the allowed Callback URIs. Next I can set up the authentication – this can literally be anything that Auth0 supports. However, I want custom authentication. I want to be able to allow users to sign up or sign in without having a social media account. In the sidebar, there is a Connections section – click on that, then select the Database option underneath it. Set up the database to allow sign-ups:

Screen Shot 2016-03-19 at 7.42.23 PM

I want to force people to set up a new account, so I removed all the social providers from my application definition in the Connections tab for my application.

Take note of the Auth0 Domain, ClientId and ClientSecret – those will be needed later.

Integrating Auth0 Lock into the Apache Cordova App

I’ve got my Apache Cordova app and I’ve integrated the Auth0 Lock process in plenty of times before – it’s one of my favorite methods to authenticate users. Let’s look at the changes. Most of the changes are in www/js/index.js. First the settings:

    // The ADAL Settings
    //var adal = {
    //    authority: 'https://login.windows.net/common',
    //    resourceUri: 'https://30-days-of-zumo-v2.azurewebsites.net',
    //    redirectUri: 'https://30-days-of-zumo-v2.azurewebsites.net/.auth/login/done',
    //    clientId: 'f35243c4-a4da-4b97-8beb-6a1cf2f76976'
    //};

    var auth0 = {
        clientId: '04pHmxVuhYU1QTDo4lEojLo1sseRLfKG',
        domain: 'shellmonger.auth0.com'
    }

The ADAL code isn’t needed any more, so I’ve just commented it out. The auth0 settings for Client ID and Domain are the only things I need. Don’t specify the client secret. That should never be put in any sort of client code, nor should it ever be checked into a source code repository. It’s, well, secret. Treat it as such.

On to the button logic. I have commented out the entire authenticate() method – it isn’t needed for this functionality. The button handler needs to have the Auth0 lock UI:

        // Set up auth0
        auth0.lock = new Auth0Lock(auth0.clientId, auth0.domain);
        auth0.options = {
            focus: true,
            popup: true,
            rememberLastLogin: true,
            authParams: { scope: 'openid email name' },
        };

        // Wire up the button to initialize the application
        $('#loginButton').on('click', function (event) {
            event.preventDefault();

            auth0.lock.show(auth0.options, function (err, token, profile) {
                console.log(err, token, profile);
            });

            //authenticate(function (data) {
            //    console.log(data);
            //    client.login('aad', { 'access_token': data.accessToken })
            //    .then(initializeApp, function (error) {
            //        console.error(error);
            //        alert('Failed to authenticate to ZUMO!');
            //    });
            //});
        });

As with the ADAL investigation, all I’m doing is logging information at this point – not initializing the application. That comes later.

I will also need to add the Auth0 library to the index.html file. That’s a two step process. Firstly, I need to add the library requirements to the content security policy, and that’s a lot of stuff:

        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://30-days-of-zumo-v2.azurewebsites.net; connect-src https://shellmonger.auth0.com https://30-days-of-zumo-v2.azurewebsites.net; script-src https://cdn.auth0.com https://shellmonger.auth0.com 'self'; style-src https://cdn.auth0.com 'self' 'unsafe-inline'; font-src https://cdn.auth0.com data: 'self'; img-src http://cdn.auth0.com https://www.gravatar.com data: 'self'; media-src *">

This is a complex Content Security Policy – let’s break it down:

Most of these entries are for the Auth0 plugin. I wish Auth0 would use external CSS files so I don’t have to use unsafe-inline, but it is what it is. You can find a great source of information on the Content-Security-Policy website. About the only thing they don’t document is gap: (which is for phonegap/cordova APIs).

Secondly, I need to load the Auth0 Lock library:

        <script type="text/javascript" src="https://cdn.auth0.com/js/lock-8.2.min.js"></script>

Run this project in the Visual Studio Emulator to see the authentication process in progress. The important thing is the data that is logged on the console though. Note that the lock screen is not hidden afterwards – I’ll get to that next. In the console, I get the following:

day-5-pic-1

the important thing is the token. There are some requirements for using 3rd party tokens with an Azure Mobile App:

  • It must follow the Json Web Token format
  • The user Id must be in the subject (sub) field
  • The audience (aud) and issuer (iss) must be known and configured

Head over to http://jwt.io/ and copy your JWT into the Encoded box. The site will decode the JWT for you. Here is mine:

day-5-pic-2

Note that I have a sub field, and the audience and issuer are my Auth0 client Id and domain respectively. Let’s finish off the code on Apache Cordova first, then go configure the server. Here is my button handler:

        // Wire up the button to initialize the application
        $('#loginButton').on('click', function (event) {
            event.preventDefault();

            auth0.lock.show(auth0.options, function (err, profile, token) {
                auth0.lock.hide();
                if (err) {
                    console.error('Error authenticating with Auth0: ', err);
                    alert(err);
                } else {
                    client.currentUser = {
                        id: profile.user_id,
                        profile: profile,
                        mobileServiceAuthenticationToken: token
                    };
                    initializeApp();
                }
            });

As I noted earlier, auth0-lock doesn’t close when it’s finished, so the first thing I do is hide the pop-up. Then I set the ZUMO client.currentUser – the important field here is the mobileServiceAuthenticationToken – this is sent as the X-ZUMO-AUTH string and verified on the server.

Custom JWT Verification in the Server

Now that I have my client in hand, I can work on my server. This is done in two parts. In the Azure Portal, go into your App Service, then All Settings. Go into the Authentication / Authorization blade and turn on the App Service Authentication / Authorization.¬† You don’t need to have any configured services, but the service needs to be turned on.¬†Then go back to All Settings and go to Application Settings. Scroll down to the App Settings section. You need to add an App Setting called Website_Auth_Signing_Key. It is set to the client secret from Auth0.

Note that when you turn off App Service Authentication / Authorization, that isn’t the same as deconfiguration. You can turn it on later and get exactly the same configuration.

day-5-pic-3

Click on Save before leaving the blade. Now, let’s revisit the server code. I need to specify the audience and issuer in the initialization of the azure-mobile-apps SDK:

// Configuration of the Azure Mobile Apps can be done via an object, the
// environment or an auxiliary file.  For more information, see
// http://azure.github.io/azure-mobile-apps-node/global.html#configuration
var mobileApp = azureMobileApps({
    // Explicitly enable the Azure Mobile Apps home page
    homePage: true,
    // Explicitly enable swagger support. UI support is enabled by
    // installing the swagger-ui npm module.
    swagger: true,
    // Authentication settings for custom auth
    auth: {
        audience: '04pHmxVuhYU1QTDo4lEojLo1sseRLfKG',
        issuer: 'https://shellmonger.auth0.com'
    }
});

The audience and issuer must match exactly (including case) what is in the JWT when decoded.

Verifying that the X-ZUMO-AUTH token is working

Since the Authentication / Authorization service is no longer enforcing the X-ZUMO-AUTH token validity, there won’t be any logging to show a bad token. Instead, I need to add some code to log the decoded user during a request. The best place for that is in tables/TodoItem.js – the table controller for the table I am using. Here is my new table controller:

var azureMobileApps = require('azure-mobile-apps');

// Create a new table definition
var table = azureMobileApps.table();

table.read(function (context) {
    console.log('user = ', context.user);
    return context.execute();
});

module.exports = table;

This is a sneak preview for the next article. All I do is log the user object from the passed-in context. If you’ve followed my articles thus far, you will want to check this code into the master branch and then merge it into the azure branch to deploy it:

git add app.js
git add tables/TodoItem.js
git commit -m "Logging for authentication"
git checkout azure
git merge master
git push

Once you hit the final Enter key, the site will re-deploy. While the service is re-deploying, log onto the Azure Portal and select your App Service, then All Settings, then Diagnostic Logs. You want to turn everything on so you can see what is going on. The blade settings should look like this:

day-5-pic-4

Click on Save, then close the blades for the App Service until you are back at the App Service main blade. Now click on Tools and then Log Stream to see the flow of logs that was just enabled. When the cliet logs in, you should see something similar to this:

day-5-pic-5

This is all the information from the JWT that Auth0 passed in.

One final note

You will note that there is a getIdentity() method on the context.auth – it’s really tempting to use it, but that’s for getting additional information on the Azure App Service Authentication / Authorization users. It won’t work when using custom authentication and it’s likely that your server will return a 500 error if you do use it.

Next Steps

I’ve taken the smallest of peeks into the Node.js table controller today – just enough so I can take a look at the authentication response. I’ll use that next time to turn this application into a multi-user application.

Before next time, I’m going to switch everything back to ADAL authentication with a client auth. That will be the standard authentication scheme going forward. Then, next time I’ll be taking a look at the things we can do with the table controller.

Until then, the code is up on my GitHub Repository.