Quantcast
Channel: Fabric Controller
Viewing all articles
Browse latest Browse all 18

Backend-less applications with Auth0 and Azure Table Storage

$
0
0

There are plenty of scenarios where mobile applications don't need a backend, but where an online, centralized data store is still required. To save a user profile, pictures, documents, settings, application data, … This to make sure the user can keep accessing his/her data on a multitude of devices and platforms.

One of the more advanced features available in Auth0 is the delegation endpoint allowing you to interact with a system on behalf of another user. For example, this endpoint can be used to get an AWS token for the authenticated user. As a result you can use this token in your mobile application to safely store data in S3 for example. This is made possible through dynamic policies which restricts the access to a specific folder in the bucket (the folder could be the user id of the current user).

What about… Azure?

Through application add-ons you can add a delegation endpoint for platforms like AWS, Firebase, Salesforce, SAP and … Microsoft Azure.

The support for Mobile Services and Service Bus opens up a few interesting scenarios, but what about Azure Storage? Just like with AWS we could use Azure Storage to create backend-less applications. As you can see there's no out-of-the-box support to create a delegation endpoint for Azure Storage, so why not build it ourselves?

Building something in Auth0?

Yes, you can actually build stuff in Auth0 to extend the authentication process. Rules allow you to execute sandboxed Javascript within the authentication pipeline, opening up a ton of new scenarios (there's even repository filled with cool examples). And the scenario we'll be looking at today: creating our own delegation logic for Azure Table Storage.

Let's look at a simple use case. I'm building yet another "todo" application in which users can manage things they need to do. This kind of application can work fine without a backend, but offering the ability to synchronizing todos on multiple devices and platforms is a must for this kind of application. So in order to add this feature to my application I need a way for users to store their data online. Azure Table Storage is a cheap, non-relational storage service which can store huge amounts of data in a fast and reliable way (by partitioning data of multiple servers).

Table Storage comes with 2 features that will allow us to build a delegation logic for it:

These features would allow us to create a partition for each user in which we can store the user's todos. This will provide use with unlimited scalability and using Shared Access Signatures we make sure that the user can only access his/her own partition.

Setting up Table Storage

Go ahead and create a new Storage Account if you don't already have one. After that you can connect to your account using Visual Studio or any other storage management tool to create a new table. I created a table MyTodos in which I'll be storing the user's todos. And that's it for the Azure part. Everything else will be handled by Auth0 and your application.

Creating a Table Storage delegation rule

Next we'll create a Rule in the Auth0 portal which will create a Shared Access Signature to extend the user's profile with information about the Storage Account, the table name, the partition key and the shared access signature. For the partition key we'll be using a base64 encoded version of the user id (because of the disallowed characters for key fields).

The first thing we'll do is define a few settings in the Rules tab (the name of the account, the account key, the table name and the validity of the SAS in days):

Now you would typically use the Azure Storage SDK for Node.js to interact with Table Storage from server-side Javascript, but the sandbox only supports a few of modules and the Azure Storage SDK isn't one of them. This is why we'll need a rule which creates the Shared Access Signatures "by hand." The complete documentation on how to create Shared Access Signatures is available here.

function(user, context, callback) {  
    // Create the Table Storage SAS querystring 
    // http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx 
    function formatSharedAccessSignature(accountName, accountKey, tableName, partitionKey, expiresInDays, permissions) {
        var expiryDate = formatExpiryDate(expiresInDays);
        var sig = formatSignature(accountName, accountKey, tableName, partitionKey, expiryDate, permissions);
        return '?' + querystring.stringify({
            sv: '2014-02-14',
            tn: tableName,
            spk: partitionKey,
            epk: partitionKey,
            sig: sig,
            se: expiryDate,
            sp: permissions
        });
    }

    function formatSignature(accountName, accountKey, tableName, partitionKey, expiryDate, permissions) {
        var stringToSign = 
            permissions + '\n\n' + 
            expiryDate + '\n/' + 
            accountName + '/' + 
            tableName.toLowerCase() + '\n\n2014-02-14\n' + 
            partitionKey + '\n\n' + 
            partitionKey + '\n';
        stringToSign = stringToSign.toString("UTF8");
        return crypto.createHmac('sha256', new Buffer(accountKey, 'base64'))
            .update(stringToSign).digest('base64');
    } 

    // Handle disallowed characters in PK 
    // http://msdn.microsoft.com/en-us/library/azure/dd179338.aspx 
    function formatPartitionKey(pk) { 
        return new Buffer(pk.toString("UTF8")).toString('base64').replace('/', '_'); 
    } 

    // ISO 8061 formatting (YYYY-MM-DDThh:mm:ssZ) 
    function formatExpiryDate(daysFromNow) {
        var expiryDate = new Date();
        expiryDate.setDate(expiryDate.getDate() + daysFromNow);
        var expiryDateText = expiryDate.toJSON();
        return expiryDateText.substr(0, expiryDateText.lastIndexOf('.')) + 'Z';
    } 

    // Enrich the current user with the required information. 
    user.storageTableName = configuration.TableName;
    user.storageAccountName = configuration.AccountName;
    user.storagePartitionKey = formatPartitionKey(user.user_id);
    user.storageSas = formatSharedAccessSignature(configuration.AccountName, 
        configuration.AccountKey, configuration.TableName, 
        user.storagePartitionKey, configuration.ValidityInDays, 'raud');
    return callback(null, user, context);
}

Creating the SAS is actually very simple:

  • We format the partition key for the user_id using base64 and we escape disallowed characters.
  • We create a shared access signature containing the account name, account key, table name, partition key, expiry date and permissions and a signature for all these parameters.
  • And as a last step we extend the user's profile with information about our storage account and the Shared Access Signature.

Note that we're not sharing the Account Key with the user. Only a signature for the table/partition/permissions/expiryDate will be shared with the user.

Go ahead and try the rule in the portal:

And that's it! Using one simple rule you can delegate access to Table Storage from within the authentication pipeline.

Using the Shared Access Signature in our application

For this example I created a simple Windows Phone "Todo" application which uses the Auth0 SDK for authentication.

After logging I'll be able to access the user's profile, including the information that was added by the rule we just created.

Using the Azure Storage SDK we can finally connect to the table and store the user's todos online.

And that's how easy it was. Our application can now use the advantages of cloud storage without having to worry about setting up and securing a complete backend for handing out tokens.

The code for the mobile application is available on GitHub: https://github.com/sandrinodimattia/azure-samples/tree/master/Storage/Auth0-SASDelegation

Enjoy!


Viewing all articles
Browse latest Browse all 18

Trending Articles