Sessions: cookies vs localStorage - which one to use when
Tags: microservices, security, tutorial, webdev
Published: 2022-02-12 18:58:05

# Preamble
Have you ever debated whether to use cookies or localStorage for sessions?

I have spent many hours on many projects contemplating which one to use. For example, I've found [this guideline](https://stackoverflow.com/questions/26340275/where-to-save-a-jwt-in-a-browser-based-application-and-how-to-use-it/40376819#40376819
) from a stackoverflow post compelling:

> - If you create a Web application you need to deal with XSS; always, independently of where you store your tokens
> - If you don't use cookie-based authentication CSRF should not even pop up on your radar so it's one less thing to worry about


Generally, both options are considered viable. E.g. the [Auth0 team recommendation](https://auth0.com/docs/secure/security-guidance/data-security/token-storage#browser-local-storage-scenarios) is making tokens stored in localStorage shorter lived.


I was never fully satisfied with the distinction. After all my research it seemed the choice is somewhat arbitrary and the security argument isn't that strong; both can be made to work.

# The dividing line
There is a stronger decision boundary than the security argument. Security is just something you'll need to deal with depending on what you choose, but there is a dividing line rooted in functionality.

## Part 1: where localStorage wins: microservices, SPAs, pre-compiled HTML
Suppose we have several API services that serve very different purposes - e.g. a payment service, and a persistence API - which operate on the same user database.

Let's say the services require authentication with each API request.

This immediately sets up a <u>dividing line</u>: if the services live on different domains, cookies shouldn't be used (as they are not shared across domains).

As long as all services live on the same domain, cookies can work fine... However, they will still cause issues with local development if microservices are run on different ports - since each port is its own domain. This is solvable e.g. with a local nginx setup, but that makes the configuration for running locally more complex.

So, in a microservice context, localStorage/sessionStorage has these advantages over cookies for storing authentication credentials:
- Simpler local development for locally run microservices: different ports off localhost don't share cookies
- Cookies couple authentication logic and service architecture (by forcing services that share authentication onto a single domain), while localStorage does not

## Part 2: where cookies win: server-side rendering
Now suppose you have a server that serves HTML from templates that it pre-populates with data.

Some of that data may depend on your session - e.g. maybe we're just putting the user name into the page.

In that case, cookie sessions are the way to go: the web page HTTP GET request that happens automatically when you load a URL in a browser does not leave room for injecting an authorization header [from localStorage], but comes with all the cookies for the domain.

## Part 3: what if you have microservices AND want server-side rendering
This was my exact case - using NextJS's SSR capabilities, and a standalone cross-application API service.

I ended up backing out of server-side rendering (or rather pre-fetching log in data for the template), and instead exposing a `GET /profile` endpoint that would get the session parsed out from headers via the common session middleware, and returns a JSON with the logged in/out state data.

This impled a loading phase on the front end where after initial delivery, it sends out an HTTP request to fetch the profile, and only then finishes rendering.

Of course it's also possible to write both localStorage and cookies, but I decided against that as I prefer having a single source of truth when possible - but if the preload state is very undesirable, it could be worth it.

Or perhaps this consideration will make you think twice about extracting a microservice at all until it's really necessary!

# Conclusion
I find the above considerations very illuminating when considering whether I am ready to split out a microservice for an authenticated application, and gives me a better understanding of my toolbox of monoliths/microservices/SSR/SPAs/preloaders: I feel I am better able to build an elegant architecture based on the complexity of the project, since certain decisions pull in more and more complexity, and different forms of modularity come with different costs - for the architecture, developers, and end user.