I like my cookies delicious and httpOnly

Ryan Chenkie recently released a React Security Fundamentals course over at https://reactsecurity.io/ and I highly recommend it.

In Ryan's course, there is a section on using httpOnly cookies for auth tokens like JSON Web Tokens (JWT) that is very relevant today because in my experience, most frontend developers are using local storage to store their auth tokens.

Why frontend developers favor local storage#

  1. Local storage is easier to work with.
  2. Many web applications are client only single page applications, i.e. they only run in the browser.
  3. Backend APIs expect tokens to be passed as Authorization headers.

So when I explain to developers on my team that we use httpOnly cookies, the questions are almost always the same.

Isn't that cookie useless? How am I supposed to access the token in my JavaScript?

Cookies!? What? Why aren't we using local storage?

How do I pass the token to the backend via header?

Why httpOnly cookies are the way#

If your application code is capable of accessing your auth token in the browser, so is any other code executing in your web application. That includes any 3rd party scripts that are running and any possible JavaScript that is maliciously injected into your application. You may have some measures in place to prevent cross site scripting attacks, but new code can introduce leaks. If you have a leak, httpOnly cookies limit the damage that can be done by injected scripts.

How do I access the token in my JavaScript?#

The short answer is: Don't.

Instead, expose an API in your backend that can extract whatever data you need from your auth token. If you are using JWTs, you can decode that on your server via an API endpoint and respond with the decoded information.

Next.js makes it very easy to do this via API Routes but there are other ways to implement an API like this as well.

Why aren't we using local storage?#

This may be obvious by now, but again: local storage is accessible via JavaScript in the browser. There is no way around that.

Keep in mind, you can still use local storage for storing non-sensitive data. One common pattern is to store an isAuthenticated boolean value in local storage as a convenience.

How do I pass the token to the backend via header?#

Many web applications are decoupled from their main backend. Think about a web application that communicates with a set of microservices or a backend-as-a-service that expects an Authorization header that expects your auth token.

In this case, I typically setup a same domain proxy. The easiest way to do this in a Next.js application is to use API Routes and/or something like http-proxy-middleware, which also works with just express.

import proxy from 'http-proxy-middleware';

proxy({
  onProxyReq: async (proxyReq, req) => {
    const accessToken = req.cookies[options.authCookie || 'ACCESS_TOKEN'];
    if (accessToken) {
      proxyReq.setHeader('Authorization', `Bearer ${accessToken}`);
    }
  },
});

Further questions about httpOnly cookies? Find me on Twitter.

Free email course

Get the free email course and newsletter for developers ready to put stressful tech interviews behind them and start flourishing in their careers.

We are a participant in the Amazon Services LLC Associates Program, an affiliate program designed to provide a means for us to earn fees by linking to Amazon.com and affiliated sites.