Cookie-based Authentication in ASP

iamprovidence
7 min readNov 2, 2022

--

Security is one of the most important and constantly forgetting topic in web development. That is not surprising. We are mostly focusing on our domain and only a few developers can brag they have to deal with authentication. Even when you haven’t worked with it, it is still important to understand how it works, so it does not suddenly catch you up.

Today we will briefly discuss how cookie-based authentication can be implemented, some core concepts and ideas.

I hope you have some knowledge in the Authentication theory:

If this one seems simple to you, just remember it just a staring point to a series of article about authentication:

You may also check out some others Auth related stories:

  • What is authentication schema in ASP about?
  • Authorization policy under the hood
  • Encapsulate authentication with DelegatingHandler
  • How to parse token on client side

Alright, enough of preface. Are you excited? Me too. So, let’s get started…

A little about our domain…

Imagine you are working on a project where domain is an online store. One day you get a task to implement two pages: a page where all available product listed and the one which contains only products that current user selected.

In no time, you created a pull request with such controller:

Can you spot what is wrong here? Fire alarm 🚨 We have a security leak here.

Instead of getting user information in the server, we send it from client side. Which is basically a big oof ... I understand it was easier to implement, but it also means anyone with a little knowledge can change our request and get data about any user he likes. We can not allow it.

Do not send current userId from client, always resolve it on server side.

To fix the issue, we need a way for our server to distinguish requests done by one user from request done by another. And that is what authentication about.

Authentication

Let's just do a little recap of our terminology before we start:

Authentication (AuthN) — is the process of defining who user is

Authorization (AuthZ) — is the process of defining what permissions user has

In this article, we will primarily focusing on authentication.

Back to our task. First thing we need to do here to change our endpoint and make it more secure:

Key things to notice:

  • we no longer pass userId as input parameter. It will be resolved by our server internally
  • we have added [Authorize] attribute. It should prevent not authenticated user from accessing that page

But having [Authorize] attribute is not enough. It just says which endpoint require authentication. We also need to specify how exactly authentication is done. For that go to Startup.cs and a few lines:

As you can see, we are going to implement cookie-based authentication. It is one of the simplest and will allow us to focus on other details. For those who doesn’t know, cookie — is just data that our browser sends to the server with each request.

Look at the code above, we are adding authentication type with AddAuthentication() and then configuring it with AddCookie(). Both of those methods takes a first argument the same string value, which is in our case “Cookies”. It is important for those to use the same value, otherwise the system won’t behave properly.

When configuring cookies, we did not add much. Just defined a page where user will be redirected if he has not authenticated.

So now let us add that page:

That was easy 😃

Every self-respecting login page should contain name and password inputs fields. So let’s add those:

And of course we need an endpoint where that page will be submitted:

The logic here is pretty straightforward:

If we can not find a user with such name and password in our database, it is likely that he entered incorrect data. In that case, we can redirect him back to the login page to try once again. Whenever user do exist in our system, we can authenticate him and redirect to a page with his products.

So far, so good. Alright, now it is time to have a look at our Authenticate() method:

Alright, that escalated quickly 😰 It may contain lots of unknown words. That’s not a surprise, we are finally getting close to authentication. So, let’s break it into pieces, and see whenever it is so complicated as it seems to be.

Firstly, we create an array of claims. What the hell is claim? Oh, it is simple. It just a key-value pair of some information about user: his id, name, phone number, address, pet’s name, favorite music whatever you like.

You can see in our example, we have two claims: ID and Name. “Name” is such a popular value for a claim that there are already predefined sets of constants with key for it, while for ID we got to define are own key. You will see how claims are used later.

After we are done with our claims, we create identity and principal classes. With our identity, we also define authentication type, which is just a random string describing what method you have used for authentication. Better use something meaningful, so you don't spend hours debugging trying to figure out what does “qwerty” means 😆

So, once again, what the hell are identity and principal?

Identity— something that can authenticate

Principal — it is an identity with permissions

Those are usually used together, but also can exist separately, for instance: regular command prompt and the one running in administrator mode. In both cases we have the same identity but with different principals.

Back to our example. At the very end, we are calling for SignInAsync() with our principal. The method will do follow:

  • generate cookie that contain our principal, identity and claims
  • cypher it, so nobody can read and fake the data inside
  • store that cookie in your browser

You can verify it there by opening DevTools. Press F12 and navigate to Application tab:

As I promised, cookie is not just random letters, it will be sent with each request to our server:

Even though, it looks like garbage, we both know that in reality it's information about our user 😉

We have a way to distinguish request done by different users. However, we also need a way to decode that cookie. And do you know what is the best part here? We don’t need to lift a finger here. ASP did everything for us. Just let’s add a few middlewares in Startup.cs, and we are done:

Once again, how does that affect our method? We no longer sending userId as a parameter, but getting it from cyphered cookie. Hey, hackers, try to crack that 😎 (Please, don’t. I am not getting paid that much 😓)

As you can see, the information about user is available through HttpContext.User. That value was populated thankfully to our UseAuthentication() middleware. And everything we put to our ClaimsPrincipal before now can be retrieved.

Especially pay attention how we work with claims here. Customly defined claim need to be resolved by key, while those that have native support, like Name, can be accessed with properties.

Alright, so now when we know how to log in. Can we log out? Oh, it’s simple:

Or you can manually delete cookies in your browser 😉

Recap

Now, when you have all the code in place, let’s recap once again, to make sure you understand how it works:

  1. Firstly, we need a user. We also need an MVC App with protected endpoint (action marked with [Authorize] attribute). The user want to access this protected endpoint.
  2. When that happens, it gets redirected to the login form.
  3. If the user enters the correct credential, it receives cookies, which is used to access the protected endpoint.

Conclusion

Well, in this article, we learned how to add authentication based on cookies. It is not much, but it is good starting steps toward authentication.

As a homework, you can try and create a method to Register users. I will give you a hint, it will be similar to Login, but instead of searching for a user we will create one 😉

I hope you liked it ❤️ Clap and follow if so 👏 Support me with coffee ☕️And if you think that was easy, just stay we me, and you will see that we just started here 😎 I mean, literally, we just started here, there are more… 🤔

--

--

iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence