Asp.NET Core 2.0 WebApi and Identity – Customizing Identity Models and Implementing Role-Based Authorization

Asp.NET Core 2.0 WebApi and Identity – Customizing Identity Models and Implementing Role-Based Authorization

Consider Your Use Case Before Deciding on Your Auth Strategy

There are a number of options for Authentication and Authorization strategies in a Web Api project. Use of Bearer tokens and Role-Based Authentication is relatively simple to implement, but is not the most advanced architecture for an authorization solution. Before deciding upon traditional Role-Based Authorization, you may want to examine the scope of your authentication and authorization needs, and determine if something simpler, or something more advanced, may be warranted.

ASP.NET Web Api can take full advantage of Claims-Based Authorization, which, for more complex systems, may be a better choice. Similarly, as mentioned previously, if the primary purpose of your Web Api is to act as an Authentication Service, you may want to go with a more robust token system (for example, shared private keys as opposed to the bearer tokens used by default), and do away with authorization at this level.

Role-Based Authorization is a good fit in a project where there exists a modest need for different levels of authorization/access, and possibly the Web Api is a part of, or associated with, a larger MVC or other ASP.NET site where Roles are used to govern authorization. Consider a standard MVC project, in which a few roles are sufficient to manage authorization, and which serves web pages as well as offers API access.

Getting Started – Create a New ASP.NET Web Api Project

First, in Visual Studio, create a new ASP.NET Web Api project. Once the project is created, update the Nuget packages in the solution, either using Manage Packages for Solution in the context menu for Solution Explorer, or by using Update-Package in the Package Manager Console.

Adding a Role Model, and Customizing ApplicationUser

To get started, let’s take another look at the Models => IdentityModes.cs file. Currently, there is not a lot there:

The Default IdentityModels.cs File in Web Api:

As we did in our ASP.NET MVC examples, we begin by modifying and adding to the existing models defined in Models => IdentityModels.cs. In fact, since we did a lot of the work previously, we will start by stealing the IdentityModels.cs code from the ASP.NET Extensible Template Project. Careful here. We can save ourselves some pain by pasting the classes into the existing namespace defined in the current code file, and leaving the using statements as they are for the moment:

Updated IdentityModels.cs Code:

Now, we need to add a few additional using statements at the top of the code file to bring in some references we need with the new code. Add the following to the using statements at the top of the file:

Additional Using Statements Added to IdentityModels.cs:

There are two immediate problems with the code we just pasted in there. The first is probably obvious, because the VS compiler is probably telling you that there is no DBInitializer class defined. Also, if you Build the project, a few other problems will surface in the VS error list. We’ll take care of that in a moment.

The other is not so obvious. The code we pasted in here is from an MVC project. For the most part this is fine. However, our ApplicationUser class defines a method GenerateUserIdentityAsync. The code we stole from our MVC project has this method, but defines it in terms of a single parameter of type ApplicationUserManager. Recall the code we pasted over, which defined ApplicationUser with two constructor parameters. The missing parameter in our newly copied method is of type string, and represents the authenticationType.

This is important, because GenerateUserIdentityAsync is called when we need to retrieve a user’s ClaimsIdentity, which represents the various claims the specific user has within our system.

Confused yet? We don’t need to worry about the details of ClaimsIdentity just yet. What we DO need to do is update the GenerateUserIdentityAsync method defined on ApplicationUser to accept a string parameter representing the authenticationType.

Update ApplicationUser for Web Api

To make our ApplicationUser class ready for use in a Web Api context, we can replace the code for the GenerateUserIdentityAsync  method with the following:

Update GenerateUserIdentityAsync with Authentication Type Parameter:

Adding a DBInitializer and Other Identity Config Items

We mentioned earlier, and the compiler is helpfully pointing out to you, that the code we stole from the Identity Extensible Template project is attempting to use a DBInitializer class that doesn’t exist (yet) in our Web Api project. Also, you probably notice (if you have built the project since adding the additional Identity Models), that there appear to be some problems with our new ApplicationUser class.

We will resolve most of these issues by once again stealing select bits of code from the Identity Extensible Template project.

If we look at the App_Start => IdentityConfig.cs file in our Web Api project, we see that, as with the original IdentityModels.cs file, there is not much there:

The Default Identity.config File from a Web Api Project:

In order to work with Roles in our Web Api project, we will need an ApplicationRoleManager, and as mentioned previously, we will be adding the ApplicationDbInitializer from the Extensible Template project.

First, we need the following using statements at the top of the IdentityConfig.cs file:

Using Statements for the IdentityConfig.cs File:

Now, add the ApplicationRoleManager and ApplicationDbInitializer classes from the Extensible Template project to our IdentityConfig.cs file:

Add ApplicationRoleManager and ApplicationDbInitializer to IdentityConfig.cs:

Now, we need to make a few changes to our ApplicationUserManager. Since we have added our customizable models, including modified versions of UserStore and RoleStore, we need to adapt the ApplicationUserManager to play nice. We have expressed our models with different type arguments than the default implementation expected by the Web Api project. Specifically, we have employed a customized implementation of IUserStore. Rather than the concrete UserStore defined in Microsoft.AspNet.Identity.EntityFramework, we have implemented our own ApplicationUserStore, which is expressed in terms of specific type arguments.

We now need to tune up our ApplicationUserManager to work with our ApplicationUserStore.

Change the code for ApplicationUserManager in IdentityConfig.cs to the following:

Modified ApplicationUserManager:

With that, the very minimal basics are in place for us to use our new, extensible model classes (including Roles, which were not directly available to us in the default Web Api implementation) in our Web Api project.

We do need to clean up one more issue though. The AccountController still appears to rely on Microsoft.AspNet.Identity.EntityFramework.IdentityUser, and we need it to use our new implementation ApplicationUser.

Modify AccountController to Use ApplicationUser

We can easily correct this last remaining issue. Open the AccountController class, and locate the GetManageInfo() method. We can see where a local variable user is declared, explicitly types as IdentityUser.

Below that, we can see in the foreach() loop, we explicitly declare an iterator variable linkedAccount as type IdentityUserLogin.

Existing Code in the Web Api AccountController GetManageInfo() Method:

In both cases we have implemented our own versions of these types. Here, we can either change the declaration in each case to use the var keyword, which relieves us of the type constraint on the variable (but, some would argue, makes our code a bit ambiguous), or we can change the explicit type declaration in each case to use our own implementation.

For now, let’s change the explicit type declaration to use our own implementations:

Modified Code for GetManageInfo() Method:

Above, we have simply changed the declared type for the local user variable from IdentityUserto ApplicationUser, and the iterator variable linkedAccount from IdentityUserLogin to ApplicationUserLogin.

Add Initialization for ApplicationRoleManager in Startup.Auth

Recall from our high-level exploration of ASP.NET Web Api and Identity that initialization and configuration of Identity occurs in the Startup class defined in App_Start => Startup.Auth.

As we have seen, the original VS Web Api template did not really provide for Role-Based anything, and consequently, provides no configuration or initialization for our recently added ApplicationRoleManager at startup. We need to add a line of initialization code to our Startup.Auth file:

Add Initialization for ApplicationRoleManager in Statup.Auth:

We’ve added a single line, which initializes an instance of ApplicationRoleManager for each incoming request.

About the ApplicationDbInitializer

With the changes we’ve introduced so far, we should be able to take our new and improved Web Api project for a test spin to see if the most basic functionality works.

Before we do, though, we need to recognize that we have fundamentally changed how the EF/Code-First database generation has been changed with the introduction of our custom ApplicationDbInitializer.

Recall from our explorations of customizing an MVC project with extensible models, the ApplicationDbInitializer allows us to specify some options for how and when the database behind our application is generated, and to provide some initial data to work with.

As we move towards Role-Based Authorization and a more restrictive security model for our Api, this becomes important.

The way we currently have ApplicationDbInitializer configured, it derives from DbDropCreateDatabaseAlways, which means every time we run our application, the backing store will be destroyed, and re-created from scratch. We also have it set up to create a default User, and we assign that user to the Admin role. In this manner, we start our application with a user with Admin-level access permissions.

The default VS Web Api project doesn’t take advantage of this out of the box. If we look at the class declaration for AccountController we see that the class itself is decorated with a simple [Authorize] attribute. What this essentially does is restrict access to all of the Action methods on the class to authorized users (except those methods specifically decorated with an [AllowAnonymous] attribute).

In other words, for now, any user who is registered, and who successfully signs in and presents a valid Bearer Token can access any of the Action methods on AccountController.

We’ll take a closer look at implementing Role-Based Authentication momentarily, First, we will extend our ApplicationUser and ApplicationRole classes with some custom properties.

Adding Custom Properties to ApplicationUser and ApplicationRole

As we saw when we examine customizing Users and Roles within an MVC project, we will add a few simple properties to our ApplicationUser and ApplicationRole models. Modify the code for each as follows:

Add Custom Properties to ApplicationUser and ApplicationRole:

Here, we have added an Address and related properties to ApplicationUser, and a simple Description property to ApplicationRole.

Now, let’s update our ApplicationDbInitializer to set some sample values for these new properties. Update the code for the InitializeIdentityForEF() method as follows:

Set Initial Values for Custom Properties in ApplicationDbInitializer:

With that, we should be ready to see if everything at least works correctly . . .

Create a Simple Web Api Client Application

To see if everything is working properly to this point, we will create a simple console application as an Api client.

In Visual Studio, create a new Console Application, and the use the Manage Nuget Packages for Solutions to add the Microsoft Asp.NET Web Api Client Libraries, or use the Package Manager Console and do:

Add Web Api 2.2 via the Nuget Package Manager Console:

Now that we have the required Web Api Client Libraries in our project, open the Program.cs file.

Make sure the following using statements are present at the top of the file. Note we have added references to System.Net.Http and Newtonsoft.Json, ad well as System.Threading:

Required Using Statements for Console Api Client Application:

Next, let’s add some very basic client code used to retrieve a token from the token endpoint of our Web Api. Add the following within the Program class:

Add Client Code to Retreive Response from Web Api Token Endpoint:

Note, at this point, we have added code the the Main() method, which calls out to a single, rather contrived method GetResponseAsDictionary() . All we are basically doing here is submitting an HTTP POST to the Token endpoint of our Web Api, and then de-serializing the JSON response body into a Dictionary<stringstring> .

Once we have the Dictionary, we are iterating over each Key/Value pair, and writing the contents to the Console.

If everything has worked the way we expect, our Console output should be something along the lines of the following:

Console Output from Token Endpoint Response:

We’ve seen this before, in our overview article. The de-serialized JSON above represents the content of the response to our POST to the Token endpoint of our Web Api. The important part of the response is and access_token itself.

This Doesn’t Look Any Different Than Before . . .

At this point, that de-serialized JSON response doesn’t look any different than it did in our previous post, before we added all our fancy new Roles and custom properties. Shouldn’t it have some new information in it now? Roles, and Addresses and stuff?

No.

A Little More On the Nature of Bearer Tokens (but only a little)

We had a really, really brief look Bearer Tokens in our Introduction to Identity in Web Api. As mentioned there, we will undertake a more in-depth exploration of Tokens in another post.

For our purposes here today, we are simply going to expand a little on what we learned previously, sufficient to understand how the access_token we retrieved as part of our JSON response above fits into the scheme of our newly modified Web Api project.

Bearer Tokens are, by design, “opaque” to the client. In other words, they are encoded (and sometimes encrypted) on the server, and can be decoded (and potentially decrypted) by the server. They are not designed to be decoded/decrypted by the client.

Recall from exploring the basic structure of an ASP.NET Web Api project, the ApplicationOauthProvider class, defined in the Providers => ApplicationOauthProvider.cs file.

When you POST a request to the Token endpoint of the ASP.NET Web Api application (at least, in the way we have it configured here), the server validates the credentials you present (in this case, user name + Password) by calling the GrantResourceOwnersCredentials() method defined on the ApplicationOauthProvider class:

The GrantResourceOwnersCredentials() Method from ApplicationOAuthProvider:

We can see that this code attempts to find a user with credentials matching those presented. If a valid user is found, the method calls the GenerateUserIdentityAsync() method defined on the ApplicationUser class to obtain an instance of ClaimsIdentity representing the user, and any claims the user may make within our system.

The ClaimsIdentity is then used to create an AuthenticationTicket.

In the code for GrantResourceOwnersCredentials(), above, when Validated() is called on the OAuthGrantResourceOwnerCredentialsContext, the OWIN middleware serializes the ClaimsIdentity into an encoded and signed token, to be returned in the HTTP Response header as a result of our request.

All of the above is a long-winded way of saying, the important user information, including user roles, and anything else we decide needs to be a part of our token, is present in the token when it is received by the client.

The Client just can’t get to it.

A Note About Bearer Tokens and Security

As mentioned in our previous posts, using bearer tokens, and submitting credentials to obtain the token from the token endpoint should only be done over SSL/STL in a production system. Bearer tokens are exactly what their name implies – anyone presenting a valid bearer token will be granted access to the system, with whatever privileges the actual “owner” of the token has.

OAuth Bearer tokens represent a fairly simple authentication/authorization scheme, In building out your Web Api, consider all of the alternatives, and make the best choice for your application.

As mentioned previously, we will take a more thorough look at Claims Identity, and token authentication/authorization in later posts.

Adding Role-Based Authorization to the Web Api Application

Now that we understand how to authenticate ourselves using a bearer token, let’s look at how we can use our new Role-Based Authorization capability within our application.

To start, let’s look at the two primary controllers present in the Web Api application, AccountController and ValuesController (we are ignoring the HomeController, since it serves no purpose for our needs here).

We’ve already seen that AccountController is decorated with an [Authorize] attribute. This means that only authenticated users may access the action methods defined on this controller (unless, of course, the method itself is decorated with an [AllowAnonymous] attribute).

Let’s look at the simplistic ValuesControllerValuesController is provided as a simple example of how one might add a basic CRUD-style functionality. ValuesController is similarly decorated with the [Authorize] attribute. Again, only authenticated users are able to access the Action methods defined on ValuesController.

As we saw in previous posts about implementing Role-Based Authorization in an MVC project, we can modify the access permissions for our Web Api, at either the Controller level, or the Action method level, by expanding on our use of [Authorize] .

Consider, we might want to restrict access to AccountController only to users who are in the Admin role, but allow access to ValuesController, and the functionality it provides, to any authenticated user.

In this case, we will want to make some modifications to our Web Api configuration.

Add a Vanilla Users Role as a Default in ApplicationDbInitializer

First, let’s make sure we have two distinct Roles available in our application – the “Admin” role we already create as an initial value during configuration, and a new “Users” role. Update the InitializeDatabaseForEF() method as follows:

Add a Users Role and a Default User to InitializeDatabaseForEF() Method:

Above, we have added a new role “Users” and another initial sample user.

Next, let’s modify the [Authorize] attribute on our AccountController class, and add a Role argument:

Modified [Authorize] Attribute for AccountController:

Now, we just need to change up our client code to attempt to access some methods from each of the two controllers to see how our Role-Based Authorization is working for us.

Modify Client Code to Attempt Controller Access

Here, we will simply set up some client code to attempt to retreive some basic data from both AccountController and ValuesController. We will do this as a user in the Admin Role, and then also as a user in the Users Role.

Change the code in your Console application to match the following:

Modified Client Code to Access Both Controllers with Different Roles:

In the above, we changed our code up a bit. We now have a GetToken() method, which accepts a User Name/Password as arguments, and returns only the access_token string from the request to the Token endpoint of our Web Api.

Next, we added two different calls to our Web Api. One method calls the GetUserInfo() method on the AccountController, and the other calls the Get() method on our ValuesController.

If we spin up our Web Api, and, after it has spun up, run our client application, we should see the following output in our Console:

Console Output from Access Permissions Comparison:

Note the output from that third attempt. We are trying to call into GetUserInfo() as a plain vanilla User, in the Users Role. Appropriately, our Web Api has returned an authorization error, since Users are not allowed access to the method by virtue of the [Authorize(Roles="Admin")]attribute on the class declaration for AccountController.

In contrast, both users are able to access the Get() method on ValuesController, since this controller is decorated with a simple [Authorize] attribute, which requires only an authenticated user for access.

Accessing Custom User Properties

We have also added some custom properties to our ApplicationUserModel. Let’s take a look and see if we can work with those in the context of our examples here.

If we look more closely at the GetUserInfo() method on AccountController, we find that the actual return type for this method is UserInfoViewModel, which is found in the Models => AccountViewModels.cs file. Now, in our crude, simple Console application we are not going to all the effort of de-serializing the JSON from our GET request into an object, but we COULD.

For our purposes, here, it will be sufficient to modify the UserInfoViewModel to reflect the additional properties we want to return, and then update the GetUserInfo() method to suit.

Add our custom User properties to the UserInfoViewModel class:

Next, update the GetUserInfo() method to provide values for the additional properties we just added to UserInfoViewModel:

Update GetUserInfo() Method on AccountController with Custom Properties:

Above, we have called out to UserManager to retreive an instance of our user, so we can get at the new properties we have added. We then set the corresponding values on the UserInfoViewModel before returning.

This example is a little contrived, and we most likely would NOT do this in a production application this way. But for now, it will serve to demonstrate that our new and improved ApplicationUserindeed has the custom properties we added to the model, and that we are retrieving them from the database as expected.

If we run our Console Client application one last time, we should see the following output:

Output from the Console Application with Custom User Properties:

And we see, our custom user properties are returned with the JSON response.

Role-Based Authorization Versus Claims-Based Authorization

In this post, we have looked briefly at implementing Role-Based Authorization in the context of an ASP.NET Web Api project, and we had the briefest look at how Bearer Tokens work. It is important to note though, that for any but the simplest of authorization/access control schemes, Role-Based Authorization (“RBA”) is rapidly being overshadowed by Claims-Based Authorization.

Claims-Based Identity offers greater flexibility, and more effectively separates the Authentication and Authorization mechanism from your code. Note, in this example project, we need to specify, as part of the [Authorize] attribute, precisely which Roles are allowed access to which controllers and/or methods.

Claims-Based Auth is more complex than RBA, but is generally going to be a more natural fit for a Web Api scenario, unless your needs are fairly simple.

Looking for .NET Core and .NET Framework hosting provider?

You should really consider finding an option that offers both features and transparent pricing. I personally recommend ASPHostPortal. ASPHostPortal has extremely affordable plans starting at $3.81 per month. The price covers everything from 5 GB of local storage for fast performance to a free SSL certification for customer trust to 24/7, 365 support to answer any questions you have about your website. Choosing the affordable web hosting by ASPHostPortal is a much better option for your business than risking everything for free hosting because you will always know what you are getting for the price.

Asp.NET Core 2.0 WebApi and Identity – Customizing Identity Models and Implementing Role-Based Authorization
Rate this post

Anjali Punjab

Anjali Punjab is a freelance writer, blogger, and ghostwriter who develops high-quality content for businesses. She is also a HubSpot Inbound Marketing Certified and Google Analytics Qualified Professional.