79
votes

I'm trying out some of the new stuff in VS2013 RC with MVC5 and the new OWIN authentication middleware.

So, I'm used to using the [Authorize] attribute to limit actions by role but I'm trying to use claims/activity based authorization, and I can't find an equivalent attribute for it.

Is there an obvious one I'm missing or do I need to roll my own? I kinda expected there to be one out of the box.

What I'm looking for specifically is something along the lines of [Authorize("ClaimType","ClaimValue")] I suppose.

Thanks in advance.

5
just as a suggestion, please put the UPDATE section as a new answer, so that's clear for everyone that it's another approach (and not part of your question)g3rv4
I'd do that, but then I'd want to accept my own answer,..and that's just not what a gentleman does :-)EightyOne Unite
I asked exactly that on meta and here's what they replied meta.stackexchange.com/questions/216719/… so there seems to be consensus ;)g3rv4
@Stimul8d I have to agree with Gervasio - questions are for questions, answers are for answers. You wouldn't have to mark it as accepted if you didn't want to; but it would make it clearer for other people.dav_i
I think it's brutal that this tech did not ship with the plumbing for attributes, as compared to the implementation for Roles.Pittsburgh DBA

5 Answers

76
votes

I ended up just writing a simple attribute to handle it. I couldn't find anything in the framework right out of the box without a bunch of extra config. Listed below.

public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
    private string claimType;
    private string claimValue;
    public ClaimsAuthorizeAttribute(string type, string value)
    {
        this.claimType = type;
        this.claimValue = value;
    }
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var user = filterContext.HttpContext.User as ClaimsPrincipal;
        if (user != null && user.HasClaim(claimType, claimValue))
        {
            base.OnAuthorization(filterContext);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

Of course, you could remove the type and value params if you were happy to use the controller-action-verb triplet for claims somehow.

30
votes
  1. You wouldn't check for claims specifically, but rather for action/resource pairs. Factor out the actual claims / data checks into an authorization manager. Separation of concerns.
  2. MVC and ClaimsPrincipalPermission is not a good match. It throws a SecurityException and is not unit testing friendly.

My version is here: http://leastprivilege.com/2012/10/26/using-claims-based-authorization-in-mvc-and-web-api/

9
votes

I found that you can still use the Authorization attribute with roles and users, with claims.
For this to work, your ClaimsIdentity have to include 2 specific claim types:

    ClaimTypes.Name

and

    ClaimTypes.Role

Then in your class derived from OAuthAuthorizationServerProvider, in the GrantXX methods you use, when you create your ClaimsIdentity, add these 2 claims.

Example:

    var oAuthIdentity = new ClaimsIdentity(new[]
    {
        new Claim(ClaimTypes.Name, context.ClientId),
        new Claim(ClaimTypes.Role, "Admin"),
    }, OAuthDefaults.AuthenticationType);

Then on any action you can use [Authorize(Roles ="Admin")] to restrict access.

4
votes

In ASP.NET Core 3, you can configure security policies like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
    });
}

then use AuthorizeAttribute to require the user meet the requirements of a specific policy (in other words, meet the claim backing that policy).

[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

Source.

3
votes
[ClaimsPrincipalPermission(SecurityAction.Demand, Operation="Delete", Resource="Customer")]
public ActionResult Delete(int id)
{
    _customer.Delete(id);
    return RedirectToAction("CustomerList");
}

ClaimsPrincipalPermissionAttribute Class