.NET Auth internals pt2: cookies

Posted by : on

Category : .NET Auth Internals

Why you may want to read this article

The prerequisite for this article is the entry article about common services for Auth in .NET

This is the second article about authentication internals in .NET 5. Here I’m going to go through the source code of the Authentication Core of .NET, by implementing authentication using Cookies.

We will overview the basic magic of cookie-based auth in .NET, whether it is stateless or stateful, how it works, and all the magic underhood. On top of that we will do demo in the end.


It is a time to research how a particular IAuthenticationHandler implementation works. For now, we will take a look at cookies-based authentication.


AddCookie

Regularly, we register the cookies authentication handler using this method .AddCookie

alt_text

All calls go to this method. 36-40 lines are registering configurations and 42 line registers the handler itself. AddScheme implementation is here

But there is nothing interesting so let’s dive into CookieAuthenticationHandler. But before this, we also need to consider CookieAuthenticationOptions.


CookieAuthenticationOptions

CookieAuthenticationOptions source code

We need to understand some implementation from it:

CookieManager

The cookie manager is used to provide cookies from requests and write them to responses.

alt_text

It is initialized here

alt_text

ChunkingCookieManager is a service that breaks down long cookies for responses and reassembles them from requests.

TicketDataFormat

Ticket data format is used to serialize/deserialize + encrypt/decrypt identity (user info) from cookies.

alt_text

It is Initialized here

alt_text


CookieAuthenticationHandler - Magic

CookieAuthenticationHandler source code

To be able to authenticate the request successfully we should sign in it first.

HandleSignInAsync

CookieAuthenticationHandler extends SignInAuthenticationHandler which has SignInAsync method.

alt_text

You may be interested in what is ResolveTarget(), what is target at all, and why it is here. To put it simply we can combine different authentication schemes and this method is needed for managing those combinations. You could see the answer for “why” by following this stack overflow question

Target here is some another authentication scheme. If you have some different ForwardSignIn schemes in your authentication options it will try to sign in with this scheme and bypass the current handler.

The main behavior for signing in is implemented in HandleSignInAsync method which is abstract and implemented in our CookieAuthenticationHandler.

HandleSignInAsync - first part

alt_text

Line 292: EnsureCookieTicket tries to get cookies, then read, unencrypt, extract principal verify expiration of authentication ticket. Also, this operation sets additional data like session id.

alt_text

Here it ensures operation is done once.

alt_text

Then method ReadCookieTicket does:

Line 141: reads cookie from the request

Line 147: unprotect it (reads and unencrypt using data protection), from this ticket we can get user-specific data (identity).

Line 153-167: tries to save session key for future if the session is enabled (it might not be enabled)

Line 172-182: checks whether auth ticket is not expired

If all checks are passed it returns AuthenticateResult.Success with auth ticket.

Line 293: builds different cookie options like SameSite, HttpOnly, SecurePolicy, etc.

HandleSignInAsync - second part

alt_text

Line 295: builds signin context with all info available at this point including user info, httpcontext, scheme, cookie options, etc.

Line 304-317: updates issued and expired time for authentication validity.

Line 319: calls SigningIn event to notify subscribers.

Line 321: updates expired time for cookies (not for authentication validity).

HandleSignInAsync - third part

alt_text

Line 329-346: if the session store is configured - then a tricky thing happens - it stores the user in this store and puts only the session id into auth ticket. It means that your cookie will contain only the session id encrypted.

IMPORTANT NOTE

If your session store is an InMemory store, you cannot do straightforward scaling of servers, because your application is stateful.

In case of storing all user identity info in cookies, you could configure the same data protection keys for all replicas and do your replication as you want because the instances will be stateless.

Line 348: serializing and encryption of cookie value using data protection.

Line 350-354: writing the cookie value into the response (header).

Line 356-363: publishing signed in the event to subscribers

Line 366: setting additional headers and return URL, calling events.

HandleAuthenticateAsync

alt_text

Line 191: The actual authentication happens here. It reads user identity from cookies (already discussed in HandleSignInAsync) and returns successful AuthenticateResult if the cookie is valid and it is not expired along with the authentication ticket.

Line 199: we try to refresh cookies’ expiration if the remaining time is less than half of the whole expiration time.

alt_text


Demo

For the example purpose I’ve created a simple demo that could be found by following this link:

Demo source code

It is a pure backend without anything unnecessary. You could use it through the swagger because cookies are set to domain.

Registering scheme and other authentication services along with CookieAuthenticationHandler:

alt_text

Sign in using cookies:

alt_text

Line 15: this method is supposed to check the password hash against stored one in your database or any other storage. But for the sample, it checks whether the username and password are mines.

Line 22-32: setting claims identity with necessary claims (user data).

Line 34: setting auth cookies.

alt_text

As you can see we have set authentication cookies.

Authenticate using cookies:

alt_text

This endpoint just outputs one of your claims (Name). If you are authenticated you will see the username.

alt_text

alt_text

As you can see we can successfully read user identity from cookies.

Thank you for your attention. Please leave feedback.


About Andrii Bui

Hi, my name is Andrii. I'm Software Engineer at Microsoft with 5 years of experience.