Search code examples
asp.net-web-apiasp.net-identityasp.net-web-api2owinasp.net-identity-2

Web Api 2: Authorising against custom claims


In certain controllers, I want to authorize a user against a company ID.

For example, consider the following resource:

api/v1/companies/1234/orders

This should only be accessible by users who belong to company 1234.

Note that I am using OAuth token bearer authentication.

Creating the company claim in OAuthAuthorizationServerProvider:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    //...

    var identity = new ClaimsIdentity(context.Options.AuthenticationType);

    // Creating the companyId claim
    identity.AddClaim(new Claim("CompanyId", user.CompanyId.ToString()));

    context.Validated(identity);
}

The current controller implementation for the orders resource:

[RoutePrefix("api/v1/companies/{companyId:Guid}/orders")]
public class OrdersController : ApiController
{
    [Route]
    public IHttpActionResult GetOrders(Guid companyId)
    {
        var orders = OrdersRepository.Get(companyId);

        return Ok(orders.Select(x => OrderModel.From(x)));
    }
}

Where do I authorize the companyId URL value against the identity claim?

Can [Authorize] be somehow used here?


Solution

  • Here is a custom authorize filter you could use

    using System.Net.Http;
    using System.Web;
    using System.Web.Http;
    using System.Web.Http.Controllers;
    
    public class AuthorizeAction : AuthorizeAttribute {
    
        public string CompanyId;
    
        protected override bool IsAuthorized(HttpActionContext actionContext) {
    
    
            if (String.IsNullOrEmpty(CompanyId))
            {
                var routeData = actionContext.Request.GetRouteData();
                var myId = routeData.Values["CompanyId"] as string;
                CompanyId = myId;
            }
    
            var user = actionContext.RequestContext.Principal as ClaimsPrincipal;
    
            if (user == null || !user.Identity.IsAuthenticated)
                return false;
    
            if (user.Claims.Any(claim => claim.Type.Equals("CompanyId") && claim.Value.Equals(CompanyId)))
                return true;
    
            return false;
    
        }
    
    }
    

    You could also decorate your action or controller with below if you wanted only a single company to access a action or controller.

    [AuthorizeAction(CompanyId = "1234")]