Request Authorization in ASP.NET Web API

When you use ASP.NET Web API as a back-end for your JavaScript heavy Single Page Application (SPA), you might want to check who is performing requests, before you answer with a result. In my case, the different permissions are a bit bulky and I'm storing them in the user's Session object. On each request, I check which permissions are necessary and if the user has those permissions. If not, I return a 403 Forbidden. To do this, you simply extend the AuthorizeAttribute and perform the necessary checks in IsAuthorized. Peace of cake, how hard can it be?

Turns out, pretty hard...

1. AuthorizeAttribute != AuthorizeAttribute

Click on the links if you don't believe me. There are two classes with the same name but different namespace:

When you are working with Web API, you want to use System.Web.Http.AuthorizeAttribute instead of the one in the Mvc namespace.

2. Where is my Session?

This was probably the hardest part. Turns out, when an API Controller is launched, the session is not accessible. However, there is a workaround to this, which I've found here: https://soabubblog.wordpress.com/2013/07/10/web-api-sessions/

protected void Application_PostAuthorizeRequest()
{
  if (IsWebApiRequest())
  {
    HttpContext.Current.SetSessionStateBehavior(
      System.Web.SessionState.SessionStateBehavior.Required);
  }
}

private static bool IsWebApiRequest()
{
  return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(@"~/api");
}

These few lines will save you your sanity when you'll be looking for the session object everywhere.

3. I want to return 403 instead of 401

The AuthorizeAttribute returns 401 Unauthorized but I want to return 403. It is a small difference but it matters to me. You can read more about HTTP Codes 401 VS 403 here. The summary:

... In summary, a 401 Unauthorized response should be used for missing or bad authentication, and a 403 Forbidden response should be used afterwards, when the user is authenticated but isn't authorized to perform the requested operation on the given resource.

To do this, you need to override HandleUnauthorizedRequest and set the response.

The final solution:

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class PermissionsAttribute : AuthorizeAttribute
{
    public string[] AllowedPermissions { get; private set; }

    public PermissionsAttribute(params string[] allowedPermissions)
    {
        AllowedPermissions = allowedPermissions;
    }

    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        foreach (var perm in AllowedPermissions)
        {
            if (HasPermission(perm))
            {
                return true;
            }
        }
        return false;
    }

    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        // optionally remember that somebody tried accessing a forbidden url
        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
    }

    private bool HasPermission(string permission) {
        // Check here if the permission is stored in HttpContext.Current.Session
    }
}

However, turns out, this solution doesn't work in mono. There is a new post about that particular problem: Request Authorization in ASP.NET Web API in Mono