Illustration of a laptop, tablet and mobile phone

Part two of a two-part series. View part one.

Stateless authentication with JSON Web Tokens  —  safely!

As developers who build REST APIs, we are quite familiar with JSON Web Token (JWT) based authentication. The typical pattern is this:

  1. Client sends credentials in POST body for authorization.
  2. Authentication service receives those credentials and validates using whatever authorization backend your framework of choice provides.
  3. Authentication service generates a JWT and signs it with a secret key, then sends it back to the user in the response body.
  4. The client receives the token and stores it.
  5. Every subsequent request made to the API contains this access token, which the REST API uses to verify the identity of the requesting user.

The Problem

Clients must provide an access token with every request to verify their identity. That means that the client code bears the responsibility of storing that token. In many applications, this isn’t a big deal . Mobile devices and many IoT devices have secure storage that can be accessed by application code alone. But if your API is sending tokens to web applications — a single page app, for instance — in the response body, no such secure storage exists. The client application has no choice but to use javascript to store that token somewhere in the browser using something from the Web Storage API ,  either sessionStorage or localStorage. It’s important to realize that a JWT auth token is just as sensitive as a username and password. If I have your auth token, I am you for all intents and purposes. The Web Storage browser API is not meant for secure storage  —  retrieving information from Web Storage is trivial if your site is vulnerable to cross site scripting (XSS).

Welcome to the Jungle

If you intend for your REST API to be consumed by a web application, you must provide an alternative pattern for JWT authentication. It’s quite easy, as developers of REST APIs, to simply throw access tokens over the wall. It would not be unreasonable to argue that the means by which clients store tokens is not the purview of an API developer. We like to pretend that our applications are client-agnostic, but we’re simply ignoring the reality of the environment in which much of our data is consumed. It’s a bit like handing a group of Boy Scouts some basic survival gear and turning them loose. Fine if they’re camping in a state park. Not so great if they’re in the Amazon rainforest.

Provide the Tools to Use Your Application Safely

When improving the security of a pattern like this, there is often a tradeoff. Increased security can come at the cost of ease of implementation and ease of use. In our case, we have a pattern that is valuable because, to review, it is:

  1. Stateless
  2. Tamper-proof (signed)

When considering an update to the pattern, we should do our best to keep these qualities and not increase the burden of implementation too greatly. As to the latter, abstraction is typically the key. As to the former, the following is a potential solution.

One Potential Solution

There are probably a number of ways to do this, but one such method is through the use of secure and scoped cookies and anti-CSRF tokens. It looks a bit like this:

  1. Client sends credentials in POST body for authorization.
  2. Authentication service receives credentials and validates.
  3. Server generates a JWT and signs it with a secret key.
  4. Server adds a Set-Cookie directive to the response header containing the JWT.
  5. In addition to the normal cookie attributes, the authentication service adds the ‘Secure’ and ‘HttpOnly’ attributes, as well as scoping attributes like ‘SameSite’ or ‘Domain’ and ‘Path’, where possible.
  6. Client receives a response with anything but a token in the response body. The browser sets cookies according to the Set-Cookie directive defined by the server, and includes them on outgoing requests to the domain defined.

We’ve created a pretty secure exchange here. Sticking the JWT in an httponly cookie means that it’s a lot more securely stored. That does, however, require us to implement an anti-CSRF policy. Here we can implement a reasonable (though not invulnerable) anti-CSRF pattern called the “double submit pattern”:

  1. An anti-CSRF token is generated and added via the Set-Cookie directive, as well as in the response body.
  2. The client must store the anti-CSRF token from the response body and send it back in a request header.
  3. REST API compares the CSRF token from the header with the CSRF token from the cookie and denies access if they do not match.

New Pattern, New Problems

JWT-based authentication is a relatively new pattern. Mature security models have yet to fully establish themselves. If you find yourself using a library for JWT authentication, check to see if it has a secure cookie JWT auth implementation available. If it doesn’t, consider making the request to the developers. Security models mature when the general developer public becomes aware of a need.

Good Tool, Bad Pattern

For reasons mentioned in part one of this series, the JWT is an ideal messaging scheme to use for REST API authentication. However, the common practice of returning the auth token in the response body of a login request is not sufficiently secure. To resolve this security issue, JWT authentication libraries need to offer a more secure delivery method by default.

New Call-to-action
blog comments powered by Disqus



You're already subscribed