How Identity works in ASP.NET Core
Khanh Nguyen • 8 October 2021 •
For everyone that have working with ASP.NET Core for a while, we all know and heard about Identity. As a standardize starting point that was implemented by default, and would work right out the box as needed. But many might want to exploring deeper down that rabbit hole, getting understand how the decision was make, therefore, implement and use other implementation over many storage engine that suit the tastes.
Unfortunately, It's not quite easy finding a place that point out its spirit, but many about how to use and customize it. It's like driving a car without knowing how strong the engine was, keep the same driving mode for city roads and long-steep hill road. Everything would be fine as long as the luck is still with us. But I prefer knowing how to upgrade the tires as it would give much more duration and stable in the long run. Even if I ever need to replace all of them.
So, I would be your tour guide as well as mechanic one today.
Identity with authentication and authorization, do they sticky ?#
Identity#
Identity mechanism was created for manage and storing user accounts in ASP.NET Core apps. Along with some necessary tools that help it achieve the goal right out the box.
What need to be store ? - As Entity Framework Core default implementation.
- User info, aka
AspNetUsers
, for User Name, Password, Email, etc... - Role info, aka
AspNetRoles
, just some basic info that would identified a role. - Claims info, aka
AspNetUserClaims
andAspNetRoleClaims
. - User and role relationship, aka
AspNetUserRoles
. - Mapping between external Identity provider user(like Google, Github, Twitter,..) and system user, aka
AspNetUserLogins
. - Storing external Identity provider token for user(if needed), aka
AspNetUserTokens
.
Authentication and authorization process#
Scratch the surface of Authentication. It was implement as a middleware that handle building HttpContext.User
object, that would stick with the entire process of each Http Request. It could verify and build them by many
mechanism (even customize), but two widely use was Cookie and Jwt Token.
At the end of this process. We would have access to Request authentication info, such as UserId, Role, Claim,...
Which was already store on Identity
.
So, after we can access authentication stuff for each http request, determine the user have the right to access corresponding resource became much easier. Role and Claims would mostly suitable for almost every small to medium size projects. And if more specific situation arrive, create more efficient policy as needed, might including further modification of what we need to store, for more convenience guarding resource process.
So, what's the point ?#
Both the two above flow are not coupled together. As long as Identity
step provide enough information, being
well serve for authentication process.
Therefore, We don't have to use default Identity
implementation, as long as it provide enough information
satisfied our need. But if there wasn't any specific reason that could lead to that decision. Extend the default
implementation would serve really well.
Assembly organization ?#
At first, let explore what's the default assemblies implement at the core of ASP.NET.
Define fundamental building blocks, such as:
- Identity Options: How long password should be, is email verify required, token provider, etc...
- Base definitions: Token provider, hashser, validator, User Manager,...
- Building block interfaces: User store, role store, token provider, validator,...
Default implementation of all necessary process that won't related to storing process to get Identity
works out of the box, such as:
- Manager: Responsibble for calling lower API, provide by
Building block interfaces
andBase definitions
up onMicrosoft.Extensions.Identity.Core
(UserManager, RoleManager, SignInManager,...) - Validator: We working with token all the times, the name tells what it's does already. (SecurityStampValidator, TwoFactorSecurityStampValidator)
Define default object implementation for storing purpose, such as:
- IdentityUser, IdentityRole, IdentityUserRole,...
- Abstract definition for storing process: UserStoreBase, RoleStoreBase.
Default implementation of Entity Framework Core for the whole Identity
process, which would work with any
corresponding Relational database implementation as MySql, MsSql Server, postgresSql,... (remember to install
corresponding package):
- Actual implement DbContext,...
- Implementation of UserStore, RoleStore.
Implementation of default UI helper, Microsoft.AspNetCore.ApiAuthorization.IdentityServer
was implement with
IdentityServer4
. As a fan of combination between SPA and API, I was not in favor these two, especially IdentityServer4
would be the last version that open source. Which would stick highest with .Net Core 3.1.
What's the idea ?#
Based on how those assemblies was organize, we can have many combinations that suit our taste.
Let's assume we want to take everything as default implementation, simply use Microsoft.AspNetCore.ApiAuthorization.IdentityServer
with Microsoft.EntityFrameworkCore.SqlServer
would work right out of the box. Storing on Sql Server, ofcourse.
If we want lower level that using our own Front-end ? meet Microsoft.AspNetCore.Identity
, Microsoft.AspNetCore.Identity.EntityFrameworkCore
and Npgsql.EntityFrameworkCore.PostgreSQL
for example.
How about document storage engine ? Microsoft.AspNetCore.Identity
with AspNetCore.Identity.MongoDbCore
would help.
The whole point that is: Clear seperation out storage mechanisms, UI implementation and facade API usage. That's the power of this design.
But that design came with a price. Every high level API would be scoped and finish their own flow.
Some what disappoint would appear at glance of those relational database lovers. As they are fully compatible with ACID which could execute many operations in a single transaction for fewer database roundtrip, with the same level of data consistence.
But consider the benefit that would bring to all other storage engines doesn't satisfied ACID. Totally acceptable!
Ofcourse we can extend our own operation that would take benefit of UnitOfWork
concept. But, In my opinion, seems like
not worth the benefit of performance that bring.
Conclusion#
I hope this would clear-out the spirit of the design that was choosed, as a default choice of ASP.NET Core framework. Get to know them would give much more freedom, choosing the right tastes for each of our project, regarding what's the size and purpose it is.
More over, ideas can spread, implement our own storage mechanism as needed or even bring them out to other languages are also possible. And most likely, all of them would have the same idea.