Sunday 23 February 2014

Windows Azure Mobile Services - Web API - Security

If we continue examining the template project we downloaded in this article, we notice there is no mention of security in the controller and no way of configuring permissions in the portal as there are for a Node.js service; so is our service secure and how do we adjust permissions for each controller method so we have the same level of control as in Node.js?


Default Permissions

To do a quick test and see if we have any security, we can quickly fire up fiddler and compose a simple GET request on the TodoItem, without even having to do any client-side coding:

Request:
GET https://tiletapperwebapi.azure-mobile.net/tables/todoitem HTTP/1.1
Host: tiletapperwebapi.azure-mobile.net

Response:
HTTP/1.1 403 Forbidden
Content-Length: 0
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=d3b0dd468eddabcdcdade2ddbc3126e837df5031a73d3c0f5da9eda3a33b354e;Path=/;Domain=tiletapperwebapi.azure-mobile.net
Set-Cookie: WAWebSiteSID=7f59130dd78d4d8b99c890c1868d4dac; Path=/; HttpOnly
Date: Sat, 22 Feb 2014 18:36:44 GMT

You can see we get a 403 Forbidden which means we don't have access to the API and the service is secure.

If we put the app key in the header (get this from 'MANAGE KEYS' in the portal root toolbar), we are granted access get the result we want:

Request:
GET https://tiletapperwebapi.azure-mobile.net/tables/todoitem HTTP/1.1
Host: tiletapperwebapi.azure-mobile.net
X-ZUMO-APPLICATION: XXXXXXXXXXXXXxxxxxxxxxxxxxxxxxx

Response:
HTTP/1.1 200 OK
Content-Length: 98
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Set-Cookie: ARRAffinity=d3b0dd468eddabcdcdade2ddbc3126e837df5031a73d3c0f5da9eda3a33b354e;Path=/;Domain=tiletapperwebapi.azure-mobile.net
Set-Cookie: WAWebSiteSID=88d016736ed8442f81ff998d1db4d832; Path=/; HttpOnly
Date: Sat, 22 Feb 2014 18:48:34 GMT

[{"id":"1","complete":false,"text":"First item"},{"id":"2","complete":false,"text":"Second item"}]

This means with the default controller configuration we are secured with the application key which equates to the 'Anybody with the application key' permission in a Node.js service.

Modifying Permissions

If we want to modify the table method permissions which we are likely to do, it's not obvious what to do from the template application. I had a dig about in the 'Microsoft.WindowsAzure.Mobile.Service.Security' namespace and found that there is an attribute called 'RequiresAuthorizationAttribute' with a nice description of it's function:

"Apply this attribute to System.Web.Http.ApiController actions or controllers access to them. Based on the Microsoft.WindowsAzure.Mobile.Service.Security.AuthorizationLevel specified, access to the target action will be restricted to requests that have been granted that level or higher."

We can apply this attribute to our controller methods to achieve 4 different permissions:

Admin

This is equivalent to 'Only scripts and admins' in Node.js services and only permits requests bearing the master key (Z-ZUMO-MASTER header) or direct access from other scripts:

[RequiresAuthorization(AuthorizationLevel.Admin)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();

}

Anonymous

This is equivalent to 'Everyone' in Node.js services and permits anybody on the internet to access the method which is to be used with caution:

[RequiresAuthorization(AuthorizationLevel.Anonymous)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();
}

If we make a request with no auth header now we get our data.

Application

This is equivalent to 'Anybody with the application key' in Node.js services and only permits requests bearing the application key and is the same as the default with not attribute used:

[RequiresAuthorization(AuthorizationLevel.Application)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();
}

User

This is equivalent to 'Only authenticated users' in Node.js services and only permits users bearing a valid OAuth2 token from one of the 4 available OAuth providers:

[RequiresAuthorization(AuthorizationLevel.User)]
public IQueryable<TodoItem> GetAllTodoItems()
{
    return Query();
}

Finally

Now we know how to set our permissions it's important to think about them for each method (if all methods require the same permissions, we can apply a single attribute to the controller). In my book I wrote a whole chapter on security and basically advised applying Admin permissions to anything which was not used by the application, then User for most other cases. Anonymous is one to be careful of as it allows access to anyone on the internet who knows the service URI.

No comments:

Post a Comment