How to store a JWT token inside an HTTP only cookie?

I have created an app that simply uses a JWT sent by the server upon correct login credentials, and authorizes against any /api route on my backend Express.js server.

AngularJS, on the other hand, took this token, stored it in session storage, and used an auth interceptor every go around to send the token back to the server.

I’ve more recently come to understand how dangerous this practice is.

I understand the transfer method of tokens back and forth, in this scenario. However, would someone be so kind as to explain, at a high level, the method that takes place when you want to store that JWT inside a secure, HTTP only cookie that the client side Javascript cannot read?

For example: upon credential success

  1. cookie is created on server,
  2. create a JWT at the same time as the cookie
  3. store the JWT in a cookie property called token etc..

I’m trying to gain a mental model here of how it works. If my understanding is correct, doing it this way wouldn’t require an auth interceptor anymore because upon correct credential login, the server would do all of the transferring of the token inside the cookie.

Answer

Dealing with cookies has their fair share of subtleties, but at an high level a cookie is a piece of data that your web server can set, that will be then stored by the user’s web browser and sent back to the server on any future requests that browser makes to the same server as long as the cookie is valid and applicable to the request being made.

(this is why you’ll no longer need to use the Angular interceptors, because it’s the browser itself that ensures the cookie is sent)

Besides some specials flag options, like the HTTP only, at an higher level you can set cookies to be associated with a given domain and path. For example, your server could set a cookie in such way that it would only be later sent by the browser to requests made under the /api path.

To sum it up, cookies are a state management mechanism for HTTP, see the associated RFC 2617 for more details.

In contrast, a JWT is just some data that has a well-know representation and follows some conventions. More specifically, a JWT is composed of an header, payload and signature sections and is generally advised to keep the size of the payload small for most of the JWT use cases. See Get Started with JSON Web Tokens for more details.

If you go through the previous article you’ll notice that the final representation of a JWT is three Base64url encoded strings separated by dots. This is specially of interest because it means a JWT is well-suited to be used within HTTP, including as the value of a cookie.

One thing to have in mind is that by the specification you are only guaranteed that a browser will support a cookie up to 4096 bytes per cookie (as measured by the sum of the length of the cookie’s name, value, and attributes). Unless you’re storing way to much data in the token you should not have an issue, but it’s always something to consider. Yes, you can also break a JWT token into multiple cookies, but things start to get more complex.

Additionally, cookies have their notion of expiration, so have that in mind also because the JWT itself, when used within the scope of authentication will also have thei own notion of expiration.

Finally, I just want to address some of your concerns about storing the JWT in localStorage/sessionStorage. You’re correct that if you do it you have to understand its implication, for example, any Javascript code within the domain for which the storage is associated will be able to read the token. However, HTTP only cookies are also not a silver-bullet. I would give the following article a read: Cookies vs Tokens: The Definitive Guide.

It focuses on the differences between the traditional session identifier cookies vs the token-based (JWT) authentication systems, the section named Where to Store Tokens? warrants a read as it tackles the security related aspects of storage.

A summary for the TL:DR folks:

Two of the most common attack vectors facing websites are Cross Site
Scripting (XSS) and Cross Site Request Forgery (XSRF or CSRF). Cross Site Scripting) attacks occur when an outside entity is able to execute code within your website or app. (…)

If an attacker can execute code on your domain, your JWT tokens (in local storage) are vulnerable. (…)

Cross Site Request Forgery attacks are not an issue if you are using JWT with local storage. On the other hand, if your use case requires you to store the JWT in a cookie, you will need to protect against XSRF.

(emphasis is mine)

Leave a Reply

Your email address will not be published. Required fields are marked *