Understanding ASP.Net Identity

Sergey Orlov
Jul 26, 2017 · 4 min read

It’s weird, but I’ve never used ASP Net Identity engine even though it’s not that new. After looking at Identity implementation in MVC project template — for plain .Net, not .Net Core — I was a bit confused and decided that it might be a good idea to understand how it works in case I’d like to cistomize something.

By the way there are sources at https://github.com/aspnet/Identity for .Net Core
The difference between these and non-Core version isn’t big, as far as I can say from a first glance.

And yeah, there are lots of articles about Identity, but I’m probably too stupid to understand what’s explained there, so I had to do my own research.

The core library is Microsoft.AspNet.Identity.Core. Visual Studio Template projects use OWIN to handle cookie authentication and EF to store user data — Microsoft.Owin.Security.Cookies and Microsoft.AspNet.Identity.EntityFramework are about it.

Core namespace gives us a bunch of interfaces: IUser, IUserStore, IUserLoginStore, IUserPasswordStore… well, lots more. Interesting thing is UserManager class which:

Exposes user related api which will automatically save changes to the UserStore

And an instance of IUserStore is expected as UserManger ctor param:

/// <param name=”store”>The IUserStore is responsible for commiting changes via the UpdateAsync/CreateAsync methods</param>
public UserManager(IUserStore<TUser, TKey> store)

What I don’t much like about UserManager is a bunch of Supports_X properties. SupportsUserPassword, SupportsUserRole and so on. These are just interface support checks for the Store. For example SupportsUserPassword is implemented as

return this.Store is IUserPasswordStore<TUser, TKey>;

The reason I don’t like it is a weak connection between the parts. Yeah, I do know that it may be really good sometimes and I do believe that clever .Net devs had chosen the best solution, but it also means a lot of pain for a less clever guy like me, to understand how to bind all these cogs together.

Thankfully we have template project to rip apart, so here’s what I’m gonna do — create an empty MVC project and set up Identity authentication from scratch, using template project as a source of hints and boilerplate code.

I wanna use OWIN for cookie authentication, so I’ll need an OWIN enabled. Let’s reference Microsoft.Owin.Host.SystemWeb and add Startup.cs to the root (https://docs.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-startup-class-detection).
Next thing is to add Microsoft.AspNet.Identity.Core and implement some of them interfaces. Since my idea is to understand how they connect to each other, my implementations would be all about returning hardcoded values, completed tasks and so on. Below is code for IUser, IUserStore, IPasswordStore, IIdentityValidator as UserManager PasswordValidator, and UserManager itself:

Okay, that’s good. Since I have a hardcoded user, I’m gonna skip registration part and go straight to login. Template’s Login action uses Microsoft.AspNet.Identity.Owin.SignInManager inheritant to check user login and password entered on Login form:

var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

An instance of SignInManager is taken from OwinContext where it was added by CreatePerOwinContext call — regular instance-per-request DI pattern, as I get it.

Insides of SignInManager.PasswordSignInAsync method are more or less clear — find the user, check password and so on. Sign in itself happens in Microsoft.Owin.Security.AuthenticationManager, and that’s a bit unclear for me now, since all it does is assign a value to the AuthenticationResponseGrant property of AuthenticationManager.
Where the cookie gets set, I wonder. Inside (AuthenticationResponseGrant property setter is a wrapper for a SignInEntry property, which itself is a wrapper for IOwinContext “security.SignIn” key setter. MSDN article on AuthenticationResponseGrant says that it “Exposes the security.SignIn environment value as a strong type” and so it does)

As far as I understand, the next step is happening in Microsoft.Owin.Security.Cookies.CookieAuthenticationHandler — it’s ApplyResponseGrantAsync method checks for this._context.Authentication.AuthenticationResponseGrant (and _context.Authentication is an IAuthenticationManager) and if it’s not null, a cookie as added to response — it makes me understand why AuthenticationManager just sets this AuthenticationResponseGrant property.

Next step is to get this cookie from user request and identify the user. For that we have an extension method UseCookieAuthentication from Microsoft.Owin.Security.Cookies.Owin.CookieAuthenticationExtensions

public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options

thus the usage is

app.UseCookieAuthentication(new CookieAuthenticationOptions())

Options… well, it seems to be safe to leave ’em intact — this will cause some default settigns like .AspNet.Cookies for a cookie name, CookieAuthenticationProvider for AuthenticationProvider. WTF is AuthenticationProvider though? Here what ICookieAuthenticationProvider summary says:

Specifies callback methods which the Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware invokes to enable developer control over the authentication process.

This class gives us a possibility to inject our code in different stages of authentication by providing a set of delegates:
- OnValidateIdentity
- OnResponseSignIn
- OnResponseSignedIn
- OnResponseSignOut
- OnApplyRedirect
- OnException

So what middleware does (all happens in Microsoft.Owin.Security.Cookies.CookieAuthenticationHandler class by the way) is that it reads the cookie and extracts identity stored there. Here we can use OnValidateIdentity to validate it — template project uses Microsoft.AspNet.Identity.Owin.SecurityStampValidator for example.

Now I’m gonna start my app, go to login page and enter 1/1 as username and password respectively (password doesn’t matter, sunce my validator always returns true).
What happens?
- request is read by auth middleware; there’s no cookie, so no identity is set
- take user name and look for it in the user store
- check password for a user
- create AuthenticationResponseGrant
- check for AuthenticationResponseGrant
- since it’s not null, create a cookie and add it to response

Now, when I go any other page:
- get the cookie from request
- decode it and get stored identity
- execute OnValidateIdentity (if set, obviosly)
- set context Identity property

The experimental project is available at https://github.com/Usurer/Understanding-ASP.Net-Identity

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade