Integration testing with Azure Mobile Services (or– how to generate authorisation token)

A customer I’m working with is deploying a mobile application backed by Azure Mobile Services and as part of their CI process they needed to automate integration testing of the mobile backend.

Given that an Azure mobile services .net backend is essentially ASP.net MVC Web API, unit testing it as part of a continuous deployment wasn’t much of an issue and, in-fact, integration testing would have been easy too  – simply calling the web API from a test project – if it wasn’t that our scenario, like many others’, involved authentication.

Required Headers

Every request to the mobile services backend needs to include the X-ZUMO-APPLICATION http header carrying the application key.

This does not pose any challenges for testing because it is essentially a constant value.

For backend operations that require authentication, however, in addition to the application key header the client must also supply a valid authorisation token through the X-ZUMO-AUTH header and in order to automate testing the test code must be able to programmatically provide a valid authorisation header.

Authentication flow with Mobile Services

In order to understand how best to approach this, it is important to understand the authentication flow of mobile services as I find that many have a small misunderstanding around it.

In many oAuth flows the client contacts the identity provider directly to obtain a token before calling the resource carrying the token retrieved. This is not the case with Mobile Services which uses a server flow-

Triggering authentication using the Mobile Services SDK (App.MobileService.LoginAsync) opens a browser that is directed at the mobile services backend and and not the identity provider directly.

The backend identifies the identity provider requested (which is a parameter to LoginAsync) and performs the necessary redirection based on the identity configuration.

The identity provider authenticates the user and redirects back to mobile services backend which is then able to inspect the token issued by the identity provider and use it to create the ZUMO authorisation token.

The key point here is that, with Mobile services, the authorisation token used is a JWT token which is ALWAYS created by mobile services as part of the authentication flow.

This approach means that following a valid authorisation flow, all tokens exchanged use the same structure irrespective of the identity provider.

It also means, that tokens are interchangeable, as long as they are valid.

Approach for Integration Testing

Understanding that the tokens are interchangeable, as they are always created by the Mobile Services backend, highlights a possible approach for integration testing – if I could create a valid token as part of my test code and supply it with the call I wish to test I could pass authentication at the backend and retrieve the results without being redirected to the identity provider for an interactive login.

So – the question is – can a token be created and if so – how?

To examine this lets first take a look at what a JWT token is –

The JWT token

A Json Web Token (JWT) has three parts separated by a dot (‘.’) -  the first part is a header indicating the token is JWT and the hashing algorithm used, the second part is the set of claims and the third part is a signature of the first two parts to prevent tampering.

The mobile services backend creates the JWT token and populates the appropriate claims based on the identity provider before signing the token with the accounts master key.

It is possible to examine the values in a token by obtaining it through tools such as Telerik’s Fiddler and decoding it using tools such as jwt.io

Here’s an example of the header and claim-set parts of a token generated following authentication with Azure Active Directory –

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "iss": "urn:microsoft:windows-azure:zumo",
  "aud": "urn:microsoft:windows-azure:zumo",
  "nbf": 1423130818,
  "exp": 1423134359,
  "urn:microsoft:credentials": "{\"tenantId\":\"72f966bf-86f1-41af-91ab-2d7cd011db47\",\"objectId\":\"c57dab9d-31a8-4cf5-b58c-b198b31353e6\"}",
  "uid": "Aad:XyCZz3sPYK_ovyBKnyvzdzVXYXuibT4R2Xsm5gCuZmk",
  "ver": "2"
}

Naturally – the signature part does not contain any additional information but is a combination of the first 2 parts – base64 encoded and hashed.

Creating your own JWT token

Josh Twist blogged on how one could create a valid Mobile Services token as long as one has access to the master key (hence the importance of protecting the master key); the code in Josh’s post is in node.js so I created the following c# equivalent to be able to use it from test projects in Visual Studio –

 private string createZumoToken(string masterSecret, string userId, DateTime notBefore, DateTime expiry)
        {
            int nNotBefore = convertToUnixTimestamp(notBefore);
            int nExpiry = convertToUnixTimestamp(expiry);

            string jwtHeader = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
            string jwtPayload = "{\"iss\":\"urn:microsoft:windows-azure:zumo\",\"aud\":\"urn:microsoft:windows-azure:zumo\",\"nbf\":" + nNotBefore.ToString() + ",\"exp\":" + nExpiry.ToString() + ",\"urn:microsoft:credentials\":\"{}\",\"uid\":\"" + userId + "\",\"ver\":\"2\"}";

            string encodedHeader= urlFriendly(EncodeTo64(jwtHeader));
            string encodedPayload = urlFriendly(EncodeTo64(jwtPayload));

            string stringToSign = encodedHeader + "." + encodedPayload;
            var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

            string keyJWTSig = masterSecret + "JWTSig";
            byte[] keyBytes = Encoding.Default.GetBytes(keyJWTSig);

            SHA256Managed hash = new SHA256Managed();
            byte[] signingBytes = hash.ComputeHash(keyBytes);

            var sha = new HMACSHA256(signingBytes);
            byte[] signature = sha.ComputeHash(bytesToSign);

            string encodedPart3 = urlFriendly(EncodeTo64(signature));

            return string.Format("{0}.{1}.{2}", encodedHeader, encodedPayload, encodedPart3);
         }
         public string EncodeTo64(string toEncode)
        {
            byte[] toEncodeAsBytes
                  = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
            
            return EncodeTo64(toEncodeAsBytes);
        }
         public string EncodeTo64(byte[] toEncodeAsBytes)
         {
             string returnValue
                   = System.Convert.ToBase64String(toEncodeAsBytes);
             return returnValue;
         }
     
         public  DateTime ConvertFromUnixTimestamp(double timestamp)
         {
             DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
             return origin.AddSeconds(timestamp ); 
         }

         public int convertToUnixTimestamp(DateTime dateTime)
         {
             DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
             return (int)Math.Round((dateTime - origin).TotalSeconds, 0);
         }

         string urlFriendly(string input)
         {
             input = input.Replace('+', '-');
             input = input.Replace('/', '_');
             input = input.Replace("=", string.Empty);
             return input;
         }

As you can see from the code, thee really isn’t much to it – the only dynamic values in this example are the not-before and expiry timestamp (expressed as seconds from 1/1/1970) as well as, of course the user id and master key values. It is possible, of course, to add additional claims as needed, based on the identity provider used, as these become available to the code though the User object.

After creating the plain text version of the first two parts, encoding them to base64 and ensuring they are url friendly the code creates a hash of they key (master key + “JWTSig” ) and then used that to create an HMAC of the first two parts. This HMAC-SHA256 signature becomes the third part of the JWT token – the signature and as long as it is created correctly, the Mobile Services backend will accept the token.

Summary – putting it to use

So – to round things up – with the understanding of how mobile services authentication works, how the JWT token looks like and how one can be created this can be put into use as part of a continuous integration set-up where the release process can deploy the mobile services backend, along side any other components of the solution and then run a set of tests to exercise the environment from the outside.

Of course – this relies on sharing the master key with the developers/testers and embedding it in the test project, so this should not be done for production! (but is entirely suitable, in my view, for dev/test environments)

Using Azure API Management between client and Azure Mobile Services backend

I’m working on a solution with a customer which requires placing Azure API Management (APIM) between the mobile devices and the published Azure Mobile Services (ZuMo) .net backend.

Conceptually this is fairly straight forward, given that all of the Mobile Services capabilities are exposed as Web APIs but I needed to figure out what changes might be required to the client app to support that and prove that it works.

To get started, I created an API in Azure API Management with the relevant operations in APIM (for now I focused on the default GET request for the ToDoItem table so I defined a simple GET operation in APIM with the URL template /tables/TodoItem?$filter={filter}

image

The generated windows 8 app code for the ToDo item sample includes the following code to create the MobileServiceClient instance, which points at the ZuMo URL –

        public static MobileServiceClient MobileService = new MobileServiceClient(
            "https://travelviewer.azure-mobile.net/",
            "hmptBIjrEnAELyOgmmYPRPSOZlLpdo56"
        );

Naturally, to hit APIM instead of ZuMo directly, this needed to be updated to point at the APIM API published, in my case  I changed it to be

        public static MobileServiceClient MobileService = new MobileServiceClient(
            "https://apiyd.azure-api.net/travelviewer/",
            "hmptBIjrEnAELyOgmmYPRPSOZlLpdo56"
            );

Running the application with only this change was clearly not enough, I got the following error when trying to retrieve the ToDo items from the backend (currently configured to require no authentication)

image

This was not unexpected, but I wanted to prove the point. Trusted Fiddler provided more information – I could see the request going exactly as expected –

GET https://apiyd.azure-api.net/travelviewer/tables/TodoItem?$filter=(complete%20eq%20false) HTTP/1.1

and it included the various ZUMO headers (Installation Id, application, etc.

But the response was – HTTP/1.1 401 Access Denied

With the body providing the exact details –

{ 
   "statusCode": 401, 
   "message": "Access denied due to missing subscription key. 
               Make sure to include subscription key when making 
               requests to an API." 
}

Authentication to Azure API Management (APIM) is done by providing a subscription key either as a query string parameter (‘subscription-key’) or an HTTP header (‘ocp-apim-subscription-key’) and clearly the ZuMo client does not know it needs to do that, so – how do we work around that?

The answer, I believe, is to create an HttpMessageHandler or – more specifically – a DelegatingHandler.

The MobileServiceClient takes in a third parameter which is an array of such delegates that are then given the chance to process the outgoing requests and responses flowing through the client, this is a great opportunity to inject the missing HTTP header in a central place without affecting the application’s code.

and so I created a class that inherits from System.Net.Http.DelegatingHandler

class InsertAPIMHeader : DelegatingHandler
    {
    }

and I’ve overridden the main method – ‘SendAsync’ – and in it simply added the required header to the HttpRequest (in my case – hardcoded)

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                    System.Threading.CancellationToken cancellationToken)  
{ request.Headers.Add("ocp-apim-subscription-key", "84095a7d792a47738464faae6bf950d3"); return base.SendAsync(request, cancellationToken); }

The last step is to write that into the MobileServiceClient Constructor, in my App’s App.xaml.cs I’ve added an instance of the delegate as the last parameter

public static MobileServiceClient MobileService = new MobileServiceClient(
            "https://apiyd.azure-api.net/travelviewer/",
            "hmptBIjrEnAELyOgmmYPRPSOZlLpdo56",
            new InsertAPIMHeader()
            );

 

Running the app again and refreshing the list of ToDo items now works. inspecting the request in Fiddler shows the request now has the APIM subscription key header

GET https://apiyd.azure-api.net/travelviewer/tables/TodoItem?$filter=(complete%20eq%20false) HTTP/1.1
X-ZUMO-FEATURES: TT,TC
X-ZUMO-INSTALLATION-ID: 226d6ea7-2979-4653-96d4-a230128719c5
X-ZUMO-APPLICATION: hmptBIjrEnAELyOgmmYPRPSOZlLpdo56
Accept: application/json
User-Agent: ZUMO/1.2 (lang=Managed; os=Windows Store; os_version=--; arch=Neutral; version=1.2.21001.0)
X-ZUMO-VERSION: ZUMO/1.2 (lang=Managed; os=Windows Store; os_version=--; arch=Neutral; version=1.2.21001.0)
ocp-apim-subscription-key: 84095a7d792a47738464faae6bf950d3
Host: apiyd.azure-api.net
Accept-Encoding: gzip

and, importantly – I actually got a result, meaning that APIM accepted the request, forwarded it to the mobile services backend and relayed the result. Success!

%d bloggers like this: