ASP.NET Core Custom User Manager
In some reason you might want to avoid using the standard Identity package to work with users, roles, permissions etc. I had 2 reasons:
- Identity works only with Entity Framework (unless you write your own implementation of the
IUserStoreinterface), but in my project (content management system) I didn’t want to limit future users in this way;
- it is too huge and complicated, I don’t want to have all that features I will never use.
That’s why I decided to write my own. But I had to replace only user/role/permission management, while the standard ASP.NET Core sign in/sign out features had to be still in use.
We will write simple web application that will have its own user manager to validate, sign in (using the standard ASP.NET Core implementation), and sign out users. It will use SQLite database to store related information and Entity Framework as the ORM, but it is easy to replace it with any other storage and ORM you want.
I want our user manager to support different login types (email and password, Microsoft, Google, Facebook etc.) and I want it to be easy to add new ones. Also, I would like
User object to have information only about the user, not about the ways he logs in. I don’t like the way it is done in Identity:
Why we have so huge class? What if we use only Facebook login? In this case most of these properties (
PasswordHash etc.) are useless. Of course, that is not so big deal, but it is not elegant from my point of view.
I decided to have 3 main classes:
CredentialType. Each login type supported by the application is described by the corresponding
CredentialType class object:
Login information of the particular user is described by the set of the
Credential class objects (one per credential type):
As you can see, there are the
Secret properties in this class. In context of email and password authentication, they contain email and password hash respectively. In case of Facebook authentication,
Identifier property will contain Facebook ID, while
Secret property value will be null.
User is represented by the
User class and contains nothing except user-related information:
I think this separation is useful, because user is user and the way how he logs in into the application is something different.
Also, we have 4 more classes:
UserPermission. I will not pay too much attention to these 4 to keep post simple, but you can look at them in the sample web application (see link at the bottom). The main idea is that roles can be assigned to the user, and permissions can be assigned to the role. We could also make it possible to assign permissions to the user directly too, but it is not so important now.
User Manager Implementation
First of all, we should turn on cookies authentication in our web application. This is done my registering corresponding middleware inside the
Configure method of the
Now let’s create the
UserManager class. The most important method of this class is the
It tries to find user with given identifier and password within given credential type. PBKDF2 transformation is applied to the secret automatically. If matching credential is found, corresponding user is returned.
Next method logs in the user:
The key thing here is calling of the
GetUserClaims method. It gets all of the user roles and permissions and creates corresponding claims for them. Also, it creates claims for user ID and name:
This allows developer to get all the user claims when user is logged in and check them and to restrict access to different resources based on this information.
Using in a Web Application
We will test how our custom user manager works using extremely simple web application. First of all, register our
UserManager class inside the DI (using scoped lifetime is important, because
Storage service, which is registered as scoped; we will not pay attention on the
Storage service, but it’s just the Entity Framework database context):
Now, create the
HomeController class with the few methods:
Our view will display the information about logged in user (if user is authenticated), and Login/Logout user buttons. These buttons (inside the corresponding forms) will make simple POST requests to our controller’s
Logout methods, which will then redirect you to the index page again. To make everything as simple as possible, user’s login and password are hardcoded inside the
Login method, but you can replace this with login page instead.
Here is our view:
You can see that we use standard ASP.NET Core
User.Identity.IsAuthenticated property to check whether the user is authenticated or not. Also, we use same
User property to check claims. While our
UserManager is registered inside the DI, we can inject it into the view and use to display current user’s name as well.
I have created the demo project for this post. Feel free to ask if you have any questions!