Learn how to use the Policy system in ASP.NET Core to create authorization rules that can be evaluated in your controllers and action methods to enforce permissions for users.
- This API uses a simple role based system for authorization. Your API may use roles, claims, or something else regardless of how authorization is modeled you'll need to perform authorization checks in your controllers. The new policy system in ASP.NET Core makes it easy to create and reuse authorization policies. Policies are grouped of authorization requirements like claim or role checks, that are defined in the start up class. Let's define a policy for users who can see all other users in the system instead of just seeing themselves. I'll open up the start up class, and in the configure services method I'll add these policies to the very bottom.
We'll say services.AddAuthorization and in the option block we can define some policies we can say opt.AddPolicy we'll call this the "ViewAllUsersPolicy" and to define the policy, we'll say in order to view all the user's you need to be an authenticated user, and you need to be in the admin role. RequireAuthenticatedUser is a little redundant because they won't have admin role if they are not authenticated it's nice to be explicit, just in case though.
Now that we've defined the policy, let's go over to the user's controller in controllers, you can reference a policy in the authorize attribute to require it for a route sometimes though, you'll need to perform authorization checks inside of a route method instead of before it. In this case you can inject the built-in service: IAuthorizationService. I'll add IAuthorizationService to this controller, called _authzService, and I'll need to add it to the constructor as well.
Now down in the get visible users Async method I can use this IauthorizationSerivce. I'll replace this TODO with a real authorization check I can say if user.identity.IsAuthenticated I'll define a flag called canSeeEveryone and we'll use the authorization service AuthorizeAsync method to authorize the ViewAllUsersPolicy we need to pass the user from the controller as well if this authorization check passes, we will want to return all of the users in the collection Let's move the definition of users above and say var users = new PagedResults of user.
If the authorization check fails, we want the user only to be able to see themselves, so I'll say var myself = await _userService.GetAsync for the current user, and just manually fill out the page results class. The first check on user.Identity.IsAuthenticated will make sure that none of the code will run if the user is not authenticated. If we hit this route as an unauthenticated user we'll simply see an empty collection.
Alright, let's start up the project and test the entire flow in Postman. First I'll make a post to the user's route to create a new user. From that I get 201 created, now I'll get token for the new user I created. Using this token, I'll request the /users route and I'll attach the token to the authorization header. Because I'm only authorized to see myself I see one result in the collection. Now let's go get a token for the administrator user.
The administrator user is put into the admin role in the start up class. We'll take the access token for the admin user and retry the request. Now we see both users in the collection, the policy system can be used to express authorization requirements from simple role checks all the way to complex authorization needs. This wraps up our look at adding application security to the API in the form of authentication and authorization. As an additional challenge, try adding authorization checks to the rest of the application where they make sense. You can also add more links and forms so the API self documents the /token and /users routes.
Look for the final folder in the exercise files which will include a complete finished version of the project
- REST vs. RPC
- Using HTTP methods (aka verbs)
- Returning JSON
- Creating a new API project
- Building a root controller
- Routing to controllers with templates
- Requiring HTTPS for security
- Creating resources and data models
- Returning data and resources from a controller
- Representing links (HREFs)
- Representing collections
- Sorting and searching collections
- Creating forms
- Caching and compression
- Authentication and authorization for RESTful APIs