I remember the day when I was talking to a colleague about the httpOnly cookie flag and how it could be used in combination with JWT token-based authentication. His idea was enough interesting for me to start working on it. My initial research revealed that some developers are also using this combination. So this article won’t bring something totally new on the table but rather provide an overview of my findings regarding the mentioned authentication.

When I was coding multipage applications with Rails or Laravel framework the whole authentication logic was already there. Because I was in the delivery mode I didn’t put a lot of attention on how the authentication works under the hood. However, I always wanted to learn more about it. When I switched to NodeJS and started writing REST APIs for SPA, I was finally able to dive deep into the authentication process.

Stateful VS Stateless

So the main difference between SPA vs Multipage application regarding the authentication is whether session data is stored on the server or not. Or in other words, multipage applications have a so-called stateful authentication approach and SPA have a stateless one.

The most common SPA’s data layer is provided by the REST API. And one of the architectural constraints of the RESTful API design is that every request must hold all data from the application state that is required to alter the resource state on the server. That constraint is the “ST” from REST - REpresentational State Transfer, which means that you are constantly sharing a state between server and client through the HTTP protocol.

JWT Token - the key component

Stateless REST API requires storing session data on the client-side. A key component for doing this is a JWT token which holds authentication data that can be confidently transmitted between clients.

A common pattern that I’ve seen in SPA is that developers tend to store JWT token in the browser’s local storage and then include the token in an authorization header for each request. Which is fine until security questions arise. Because local storage is readable from Javascript a simple cross-site-scripting attack or XSS could read the JWT token and open doors to impersonate a user.

So we need to find another solution to store JWT token on the browser.

Bring the good old cookies back

What if we store JWT token in a cookie? But cookies are also readable by Javascript. So it’s the same situation as when we store the token in the browser’s local storage.

Is there any option to prevent Javascript from accessing a cookie? Actually, there is. When a server sets a cookie with an HttpOnly flag, Javascript won’t be able to read that cookie.

But with cookies, old security issues become relevant again. When you send a request, all cookies from the cookie domain are sent as well. This means that if a victim performs a request to the API from the attacker’s malicious site, also cookies, like session cookies, are sent. This security risk is known as CSRF - cross-site request forgery.

To mitigate CSRF risk we can use two things:

  • Custom request header, like “x-requested-with” which is then checked on serverside. This works, because of the restriction that only JavaScript can be used to add a custom header on Ajax requests, and only within its origin.
  • SameSite cookie flag, practically, this means the cookie will only be sent if the site for the cookie matches the site currently shown in the browser's URL bar. As of writing, this flag is currently supported by 87 % of browsers according to “Can I Use” site.

Final step

Can you imagine what the main problem with this idea might be? A browser client can not read data that is sent via JWT token. This is especially important when the SPA needs to use session data for manipulating the app’s user interface. Like preventing some parts of the app to be shown to some user roles.

JWT token is constructed of three parts: payload, signature, and header.

If the payload has data that frontend needs, then we have to provide it somehow. A simple solution is that we split JWT token into two cookies, one that holds payload and one with signature and header data.

Payload cookie should have httpOnly flag set to false and signature.header cookie must have httpOnly flag set to true. Here is a diagram that shows the whole flow.

The main benefit of using this kind of authentication mechanism is, of course, the increased overall security of the app. But there is also one that is not so obvious at first glance and that’s a much better frontend developer experience. He simply doesn’t need to think anymore where to store the session data, all that he’s interested in is, how he’s going to read the content of the payload cookie.

The majority of work is done on the backend side, which is the right place to handle the authentication.

Written by: Tadej Stanic

Follow him on GitHub https://github.com/tadejstanic
Follow him on LinkedIn https://www.linkedin.com/in/tadejstanic