How to implement Shared Access Signatures using Azure Mobile Services and Node.js
This post will provide essential skills to enable you to use shared access signatures to upload photos as blobs. Azure mobile services using node.JS.
This is supporting documentation for an upcoming MSDN article around the creation of a raspberry pi application that is fitted with the camera and capable of uploading images to the cloud.
This same code can be used for virtually any client application that wishes to upload images to the cloud.
The end result of this post is to provide client applications a shared access signature, which is a secure and efficient way to enable client applications to upload files to the cloud.
Here are the high-level steps of this post:
Provisioning an Azure Storage Account
- Recording the account name, account key, and container name
Provisioning an Azure Mobile Service
- Adding Node.js code to it
Testing with a REST client
Related Psots
PROVISIONING AN AZURE STORAGE ACCOUNT
If a client is to upload a file storage, we need to provision a storage account
The storage account will have a name, key, and container for blobs
Hit "+" to create a new storage account.
Figure 1: Creating a new storage account
Click Quick Create
Figure 2: Choosing QUICK CREATE
Type in a unique URL.
Choose a region that you are consistent with. Mobile Services that we will provision will need the same region.
Figure 3: Specifying URL, location, etc
It will take less than 1 minute to provision.
Figure 4: Viewing progress
Click MANAGE ACCESS KEYS. We will need this later for Azure Mobile Services.
Figure 5: Managing Access Keys
Drill into the service by hitting
Figure 6: Verifying online
Choose CONTAINERS.
Figure 7: Choosing CONTAINERS
Select CREATE A CONTAINER
Provide a container name and write it down. You will need it later.
Figure 8: Providing a container name and access
Note the container name
Figure 9: Validating container creation
RECORDING THE ACCOUNT NAME, ACCOUNT KEY, AND CONTAINER NAME
Take note of the information (account name, access key, container name, etc).
Figure 10: Recording Access Keys
PROVISIONING AN AZURE MOBILE SERVICE
Provision an Azure Mobile Service account
It will be used to respond to client devices that want to upload files
It will provide a Shared Access Signature
- A shared access signature provides delegated access to resources in your storage account. This means that you can grant a client limited permissions to your blobs, queues, or tables for a specified period of time and with a specified set of permissions, without having to share your account access keys
- This allows clients to upload to storage directly on a secure fashion
Navigator mobile services and click the + to provision a new service
Figure 11: Creating a new Mobile Service
Make sure to select CREATE
Figure 12: Creating a new Mobile Service
Enter the URL for the new mobile service
We will not initially be using the database so you can select whatever you want there
Figure 13: Providing a URL, Database, etc
Provision a new SQL Database or use an existing one
We will not initially use a database so it doesn?t matter much right now.
Figure 14: Indicating database settings
It will take a few moments to provision.
Figure 15: Verifying creation of mobile service
CREATING THE NODE.JS BACKEND
Drill into to the service by hitting ->
Figure 16: Verifying mobile service is ready
From the menu below choose API
Figure 17: Creating a custom API (for get requests)
Provide a new API name. This is the API name used by clients in their web request. Leave permissions to their default values.
Figure 18: Providing an API name
Verify that you have provisioned a new API. Click the right arrow (->) to drill into the API just created.
Figure 19: Verifying API
To note is the default code below. We will replace it with our own code that returns a shared access signature
Figure 20: Viewing default API code
Here is what the code looks after you paste in the code provided below
Node.js in Azure Mobile Services 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 var azure = require('azure'); var qs = require('qs'); var blobService; // used to communicate with blob storage service var containerName; // container must exist to support photo uploads var accountName; // storage account name (you create at portal) var accountKey; // storage account key (generated for you) var blob = 'sassample' var BlobConstants = azure.Constants.BlobConstants; var ServiceClient = azure.ServiceClient; var CloudBlobClient = azure.CloudBlobClient; // Issued by Raspberry Pi // https://[you put yours here].azure-mobile.net/api/photos // With headers = "X-ZUMO-APPLICATION: [get the mobile servies app key from the portal]" // Header contains your "application key" /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Entry point here // // /////////////////////////////////////////////////////////////////////////////////////////////////////////////// exports.get = function(request, response) { // These are part of "App Settings" in the configuration section // of your service. Shouldn't be hard-coded into this Node.js script containerName = request.service.config.appSettings.PhotoContainerName; accountName = request.service.config.appSettings.AccountName; accountKey = request.service.config.appSettings.AccountKey; // Connect to the blob service blobService = azure.createBlobService(accountName, accountKey, accountName + '.blob.core.windows.net'); createContainer(); createPolicies(); var sasResponse = GetSAS(); return request.respond(201, sasResponse); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // // // /////////////////////////////////////////////////////////////////////////////////////////////////////////////// function GetSAS() { var startDate = new Date(); var expiryDate = new Date(startDate); expiryDate.setMinutes(startDate.getMinutes() + 15); var sharedAccessPolicy = { AccessPolicy: { Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.WRITE, Start: startDate, Expiry: expiryDate } }; var blobService = azure.createBlobService(accountName, accountKey, accountName + '.blob.core.windows.net'); var blobname = genRandNum() + '.jpg'; var signature = blobService.generateSharedAccessSignature(containerName, blobname, sharedAccessPolicy); var sharedAccessSignature = new azure.SharedAccessSignature(blobService.storageAccount, blobService.storageAccessKey); blobService.authenticationProvider = sharedAccessSignature; sharedAccessSignature.permissionSet = [signature]; console.log('baseUrl = ' + signature.baseUrl); console.log('path = ' + signature.path); console.log('queryString = ' + encodeURIComponent(signature.queryString)); console.log('queryString = ' + signature.queryString); var sasResponse = { 'sasUrl': signature.baseUrl + signature.path + '?' + qs.stringify(signature.queryString), 'photoId': blobname, 'expiry':expiryDate }; return sasResponse; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // // Purpose: Generate a random number to use in the filename // // /////////////////////////////////////////////////////////////////////////////////////////////////////////////// var genRandNum = function() { return Math.floor(Math.random() * 90000) + 10000; } Figure 21: Pasting in code
Return back to the portal main menu and choose MANAGE KEYS, as seen in the lower right corner of the diagram below.
Figure 22: Retrieving the application key
Take note that the application keys below. Clients will use these to connect to the service. Save this application key to be used later when we use a rest-based client to test our mobile service.
Figure 23: Getting the applications
STORING CONFIDENTIAL INFORMATION INSIDE APP SETTINGS
App settings is a safe and convenient way to avoid hardcoded in your node.JS scripts
It is similar to the configuration file for a web application
We will use it to store our storage account information, such as the computer name, storage account name, and access keys
ADDING APP SETTINGS
Choose CONFIGURE from the menu below
Figure 24: Configuring App Settings
Notice the app settings provided below
This information comes from the previously provisioned storage account. This information is available from the Azure storage account portal
Putting it here avoids hard coding it into our mobile service application
Figure 25: Specifying App Settings
TESTING WITH A REST CLIENT
We are now ready to begin testing our freshly created service.
There are any number of rest-based client applications to test with
The one below is a Google Chrome extension called advanced rest client
It is free and works well for testing purposes
Figure 26: Using Advanced REST Client
HOW TO USE ADVANCED REST CLIENT
Notice that there are five pieces to this
This is the URL by clients who used to request the shared access signature
- Notice the first part of the URL is the name of the mobile service
- The last part of the URL is the actual API name we provided
The request type will be GET
There needs to be one header entry is seen below.
- X-XUMO-APPLICATION is the key
- The value here is the application key for the mobile service we created
Click send to call into the mobile service API
View the shared access signature returned by the service.
- Client applications that received this signature can then upload blobs to the specified storage account
- The scenario might be a client application that wants to upload a photo using a shared access signature
Figure 27: Verifying the Shared Access Signature
Conclusion
This post illustrated all the needed steps to be able to call into a mobile services API. . It even demonstrated how to test the API using the Google Chrome extension, Advanced REST client.
You are required to provision both a Azure storage account as well as an Azure mobile service.
This post is useful for those who wish to upload blobs to Azure storage. having a shared access signature makes it easy for client applications to directly upload into Azure storage.
This post is part of a larger article coming in MSDN magazine, where we enable a raspberry pi device to upload photos to Azure storage services.
Comments
Anonymous
August 27, 2014
Returns a error :( { code: 500 error: "Error: Internal Server Error" }Anonymous
August 28, 2014
Corrected for those who need it :) var azure = require('azure'); var qs = require('querystring'); exports.get = function(request, response) { // These are part of "App Settings" in the configuration section // of your service. Shouldn't be hard-coded into this Node.js script var containerName = request.service.config.appSettings.PhotoContainerName; var accountName = request.service.config.appSettings.AccountName; var accountKey = request.service.config.appSettings.AccountKey; var host = accountName + '.blob.core.windows.net'; // Connect to the blob service var blobService = azure.createBlobService(accountName, accountKey, host); var startDate = new Date(); var expiryDate = new Date(startDate); expiryDate.setMinutes(startDate.getMinutes() + 15); var sharedAccessPolicy = { AccessPolicy: { Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.WRITE, Start: startDate, Expiry: expiryDate } }; var genRandNum = Math.floor(Math.random() * 90000) + 10000; var blobname = genRandNum + '.jpg'; var signature = blobService.generateSharedAccessSignature(containerName, blobname, sharedAccessPolicy); var sasResponse = { 'sasUrl': signature.baseUrl + signature.path + '?' + qs.stringify(signature.queryString), 'photoId': blobname, 'expiry': expiryDate }; return request.respond(201, sasResponse); } By @PeterConchaR and @BrunoTerkalyAnonymous
October 24, 2014
The comment has been removedAnonymous
October 25, 2014
Found the issue: I copied the text for the Header....it is misspelled WebPage shows: X-XUMO-APPLICATION Should be: X-ZUMO-APPLICATION correction on ZUNOAnonymous
October 26, 2014
The comment has been removedAnonymous
October 26, 2014
In regards to my question above you can make sure that the correct version will get added to the PUT for your generated SAS by adding it to your PUT request; beforeSend: function(xhr) { xhr.setRequestHeader('x-ms-version','2014-02-14'); }Anonymous
November 06, 2014
The comment has been removedAnonymous
November 06, 2014
Hey AI How to solve error of 401 ? ???Anonymous
December 01, 2014
The comment has been removedAnonymous
April 10, 2015
a hint on testing if you get 500 errors like I did comment out most of the get code and just send the following as response. Then check that the info you get back is what you expected. var sasResponse = { 'accountkey': accountKey, 'accountName': accountName}; check done revert to (corrected) get codeAnonymous
April 23, 2015
Hi all, Know if they've made any changes as generated the SAS for writing Blobs; because my project was working, but now it shows me the following error when using the SAS: "Error: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature." Excuse my English, I speak Spanish.Anonymous
August 19, 2015
Hola a todos, yo tengo problemas con el PUT, me aparece el error 403... parece que es algo de autenticacion/permisos o cosas asÃ. AYUDA Hello to all, I have problems whit the PUT, returns me an error '403'... i think is something about authentication or similar. Please HELPAnonymous
November 19, 2015
Hi Bruno, Do you know what is needed to get blobService.generateSharedAccessSignature() to return an HTTPS sasURL? I've tried the code above and I only get a signature.baseUrl with HTTP. Thanks, Al