Customizing Claims for Authorization in ASP.NET Core 2.0

There have been some major changes to ASP.NET Identity with the release of Core 2.0, which you can read about here.  One of the major changes is that it does not rely on using middleware anymore to customize it.  Instead dependency injection is used by configuring via services in the ConfigureServices method of StartUp.cs.   There is not much documentation or examples on how to do this so I am going to explore how to customize ASP.NET Identity and publish some examples in this blog.  First I wanted to look at how you would add custom claims to the identity and then use those claims for authorization.

To begin create a new ASP.NET Core Web Application project in Visual Studio 2017.  Be sure to select .NET Core and ASP.NET Core 2.0 in the two drop downs at the top.  Select the template for Web Application (Model-View-Controller) and Change the Authentication to Individual User Accounts.  When the project is created you are ready to add some code.


For this example we are simply restricting access to an Administration page to administrators only.  To indicate that the user is an administrator  add a property to the ApplicationUser which is a boolean named IsAdminsitrator.  You will find the ApplicationUser class in Models/ApplicationUser.cs.

    public class ApplicationUser : IdentityUser
    {
        public bool IsAdministrator { get; set; }
    }

Next we need to create a custom UserClaimsPrincipalFactory that creates a new claim that indicates if the user is an administrator or not.

    public class MyClaimsPrincipalFactory<TUser>: UserClaimsPrincipalFactory<TUser> where TUser : ApplicationUser
    {
        public MyClaimsPrincipalFactory(
            UserManager<TUser> userManager,
            IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor)
        {
 
        }

        protected override async Task<ClaimsIdentity> GenerateClaimsAsync(TUser user)
        {
            var id = await base.GenerateClaimsAsync(user);
            id.AddClaim(new Claim(MyClaimTypes.IsAdmin, user.IsAdministrator.ToString().ToLower()));
            return id;
        }
    }

Define the claim type in a class so that it can be reused later and avoid the problems caused by magic strings.

 public class MyClaimTypes
 {
     public const string IsAdmin = "http://myclaims.com/claims/isadmin";
 }

Use dependency injection in ASP.NET Core to configure the custom claims principal factory you just created by adding this line the ConfigureServices method of Startup.cs.

  services.AddTransient<IUserClaimsPrincipalFactory<ApplicationUser>, MyClaimsPrincipalFactory<ApplicationUser>>();

Now when the identity pipeline performs authentication it will call the custom claims principal factory and add the custom claims we will need for authorization.  With Core we no longer create custom AuthorizeAttributes to handle the claims. Instead you need to create a custom policy.  This is a fairly simple policy so we can add it like this in the ConfigureServices method of Startup.cs.

    services.AddAuthorization(options =>
    {
        options.AddPolicy("AdminsOnly", policy => policy.RequireClaim(MyClaimTypes.IsAdmin, "true"));
    });

In this simple policy we are just checking to see if the custom claim of type IsAdmin is set to either true or false.  Now to test this add a custom view for Administrators and add an Authorize attribute like this.

    [Authorize(Policy = "AdminsOnly")]
    public IActionResult Administration()
    {
        ViewData["Message"] = "Page for application administration.";
        return View();
    }

Now whenever this view is invoked the AdminsOnly policy will be checked to if the identity has a claim type of IsAdmin that is set to true or not.  If it is true the user will have access, otherwise they will not have access.  Fire up this web application and try it out.  Give the user access to this new view or not by setting the column IsAdministrator in the AspNetUsers table for the user you are testing.  When you are testing keep in mind that the change to the user will not take place as soon as the change in the database table.  You will need to sign the user out and back in again for it to take affect.  There is a way to handle keeping your logged in users in sync with database changes when using Core Identity.  We will explore that in future blog posts.

So that is all there is to add a custom claim that we can use for authorization of users.  This method  using policies for authorization works great if the logic is simple, but you can hit road blocks when the authorization logic gets more complex.  Look for future blogs where we will explore how to handle more complex authorizations.


Comments

Popular posts from this blog

Using Claims in ASP.NET Identity

Seeding & Customizing ASP.NET MVC SimpleMembership