Fundamental Authentication Concepts - Cookies, Sessions, Tokens

Fundamental Authentication Concepts - Cookies, Sessions, Tokens

Authentication is the process of letting the server know whether the user is logged-in or not. When a request is made to a secret endpoint in the server, the server responds with either 200 OK or 401 Unauthorized depending on whether the user is successfully logged in or not respectively. Let's discuss two of the main ways that authentication is implemented in modern web applications.

There are multiple authentication methods, but I'll talk about the old traditional username and password one because it's simple and we will learn a lot about the browser/server communication along the way. The two main ways used for username and password authentication are cookies-based authentication and token-based authentication. Let's start with cookies-based authentication.

P.S. This article is a complementary resource for my YouTube video here:

Cookies-Based Authentication

What are cookies?

Cookies are simply a means of communication between the browser and the server. Cookies are not only about authentication, but they're commonly used for authentication. Anything that can be a string can be sent as a cookie! Another important thing you need to know about cookies is that they are automatically sent to the browser, and you don't need to do anything as a developer to make send them from the browser to the server.

To see the cookies on your browser, right click inside your browser > inspect > Application. Here you can see a list of all the cookies stored on your browser as pieces of strings.

If you click on any of these cookies, you will realize that they have certain properties like Name, Value, Domain, HttpOnly and Secure just to name a few.

  • Name: name of the cookie

  • Value: the string value that will be sent back and forth between the client and the server.

  • Domain: the domain that this cookie is associated with.

  • HttpOnly: it means that the cookie is not accessible by the JavaScript code on the browser which is a security feature that prevents hackers from using cross scripting attacks by sending malicious code through our cookies to the server. This gives more control over the cookie.

  • Secure: means that the cookie will be sent to the server only on HTTPS protocol.

For many years cookies were the main way to authenticate users using this flow shown below:

Whenever the user enters their credentials correctly, the server responds with Set-Cookie header with a cookie string value that identifies whoever user logged in the server. That cookie will be automatically sent in the future requests to the same domain, and the user will still have access secure endpoints until their cookie is expired.

If you look at the diagram, you see that in the first response from the server, the cookie has name "session". What is a session?

Sessions

A session is data that identifies the current active user. E.g: user's name or id. Data in a session is temporary and it is removed when the user is logged out.

Where do we store those sessions?

  1. Client-Side Sessions: here sessions are stored on the user's browser inside cookies after being encrypted. This is called stateless sessions because there is no database involved on the server aka. no "state" to be tracked by the server. This is great for scaling and performance, because we don't do extra checking inside the server by sending extra read/write requests to the database. But the problem is that there is a limit on how much we can store inside a cookie.

  2. Server-Side Sessions: if the session's data is sensitive and private, we store it in a database on the server side. This makes scaling the application challenging because with every request, the server has to do extra database query to check whether the session identifier is right or wrong. Unlike client-side sessions, cookies in this case don't hold the user's data. They hold a session ID which maps to its related session's data in the database on the server.

In other words, in Client-Side Sessions, cookies store the session while in Server-Side Sessions, cookies hold the session's ID while the session's data is stored inside a database.

Now you can say that stateless sessions aka cookies can be easily manipulated by the user from their browser, so what's the point?

Although that is possible, the server has to sign or encrypt the cookie using some key before sending it to the browser. That way, all the server has to do is simply to decrypt the cookie and check if it was tampered with or not. Now it's time to talk about tokens.

💡
Advanced Note: In microservices architecture, encrypting the session's data before sending it to the browser using language-specific package like express-session in Node.js is a bad practice because we don't know the algorithm behind that package, and that algorithm might be different in a different micro-service, which can be written in a different language like Java or Python which have no idea of how express-session works behind the scenes. Always encrypt with known algorithms or simply use JWT to encrypt as I will discuss later.

Token-based authentication (JWT)

Example of public data that can be stored in the cookies is JWT tokens. They are encrypted insensitive data.

Tokens are pieces of strings that act like digital identifiers that identify the user who's using our application. If you go to jwt.io you can see the JWT token in action. JWT is a JSON token which means it's a string that looks like an object with properties and values. The JWT's payload is a JSON object with certain properties which will be sent back and forth between the server and the client. You can add anything you want to the JWT payload, but usually we add the user's ID.

This payload is encoded using a secret key stored secretly on the server and every request that comes to the server, we have to check whether that JWT token was encoded with our key or a different key, and let the server responds accordingly.

The JWT authentication flow works like in the image below:

When the user logs in or signs up to our system correctly, we send the access token which is the JWT. The browser now has to send that token with every request it makes to any private endpoint on our servers using the Authorization header. That authorization header is usually BEARER JWT_TOKEN_STRING and we add the word BEARER because this token is "carried" from the client to the server on each request.

JWT in Cookies

Now that you know the meanings of a cookie, session and a token, what do you think of adding a JWT token inside a cookie instead of the Authorization header?

Yes, that's how production-level authentication works. This also avoids the problem of different encryption algorithms across different programming languages in microservices as I have mentioned above because JWT follows the same encryption algorithms across different programing languages.

How To Implement Sessions in Node.js

HTTP requests are not stateful by default. That means there is no data is associated with requests made to the server to identify the user (session). Thus, we have to use 3rd-party packages to achieve that.

  • Server-Side Sessions: we use a package called express-session and a database like PostgreSQL, MongoDB, Redis or others.

  • Client-Side Sessions: we use a simple package called cookie-session which supports automatic cookies encrypting

  • For JWT Tokens, we use a package called jasonwebtoken whose documentation are very straight-forward.

That was it about sessions, cookies and tokens. I hope you enjoyed this article, and would love to hear opinions down in the comments section below.