Can JWT be used for sessions?

Julija Alieckaja
8 min readApr 21, 2018

--

Some time ago I’ve stumbled upon this article stop-using-jwt-for-sessions
and its part 2 stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work.

Both are excellent reads and describe totally valid security and structure concerns. joepie91’s lists common misconceptions about JWT and mistakes developers make while using JWT for sessions. But the concept of JWT itself is not the essence of the problem, it’s just a means of authorization which can work just fine if used correctly.

Let’s repeat the terms first.

Session represents information associated with a particular user and is designed to persist throughout the user’s interaction with the application. That’s exactly what we’ll try to achieve using JWT.
Stateless JSON Web Token is a self-contained token which does not need any representation on the backend.
Stateful JSON Web Token is a token which contains only part of the required data, f.e. session/user ID and the rest is stored on the server side.

Stateless JWT has a set of use cases when it can fit perfectly into specific system requirements. But in a common case (API for web clients and mobile apps) developers usually would prefer to use stateful tokens to be able to fetch all required data from the db after request authorization is passed. In this article we’ll consider stateful JSON Web tokens, wonder if we can use them in such systems and what benefits/drawbacks we’ll get as a result.

Let’s imagine next conceptual architecture.

We’ll represent the session as a pair of JSON Web Tokens — access and refresh. Each of these tokens contains payload, expiration time and signature. Access token’s payload will contain expanded session information, like access policies, other information which could be used on the backend in order to decide if a concrete request should proceed based on the information in the payload. Refresh token’s payload will contain the bare minimum, basically user’s ID should be enough. Access token has a short life span, let’s say 15 mins and refresh token expires in 1 week. Refresh token is supposed to be used to obtain a renewed access token.

We can divide all API clients into 2 groups — web clients and all the rest including mobile apps, 3rd party services, etc. For web clients we’ll be using cookies as token transport, and for all the rest we’ll send raw tokens in JSON response body.

Our imaginary JSON API will have 5 endpoints:
1. Web Login endpoint, which sets the access/refresh tokens in cookies in exchange for user’s login/password.
2. Stand-Alone Login endpoint, which returns the raw access/refresh tokens as a plain JSON response.
3. Web Refresh endpoint, to refresh the access token via cookies.
4. Stand-Alone Refresh endpoint, which returns the raw access/refresh tokens as a plain JSON response.
5. An endpoint with protected data, for example ponies, with POST action.

Ponies API is used by JS web client, mobile app client and an external web service.

Both Login and Refresh endpoints for web clients return tokens in cookies. Cookies require CSRF protection, so our API must take care of it and include CSRF token into JSON responses for Web Login / Web Refresh endpoints, and the app must validate CSRF on POST/PUT/etc requests.
Right, CSRF is out of JWT concerns and developers must fulfill it on their own if they would like to store JWT in cookies. There are dozen of open-source libraries which can take care of CSRF, so it shouldn’t be much of a problem.
Web Refresh endpoint authorization mechanism will check request cookies in order to retrieve the refresh token, Stand-Alone Refresh endpoint will check request headers.
And lastly, the ponies endpoint should check request headers and cookies for access token. In case when the access token is found among cookies it must also validate CSRF token which should be present in the headers.

Cool, now our ponies API supports Stateful JWT Session mechanism, it also supports both cookies and headers token transports and is completely CSRF-safe.

Now let’s take a look at the ponies API from the client’s perspective.

Let’s start with JS-web client.

Previously, we’ve decided to use cookies as a transport for the access/refresh tokens since it’s well known how to mitigate CSRF attacks, comparing to XSS attacks which can be rather sophisticated. The downside of cookies in the context of JSON Web tokens is that we’ll not be able to get the token’s payload from the cookie, but still we can easily find a way around in order to reduce amount of requests to the server — f.e. send some required data like user settings together with the CSRF token in the JSON response for Web Login/ Web Refresh.

Next, mobile app client.

Although mobile HTTP libraries support cookies it’s more convenient for developers to operate with response body. As there’s no cookie-size limitation we’ll be able to send rather big amounts of data together with access token and decode it on the client. Mobile app can store only the refresh token in a keychain, and make requests for new access tokens when the app starts.

The last one, external web service.

Almost the same benefits as for mobile apps. The service we’ll be able to receive and store access/refresh tokens and decode the data from the payload as well.

Now keeping this in mind, let’s get back to joepie91’s articles. Firstly, let’s take a closer look on the JWT benefits lists. joepie91 provides the benefit itself, and an explanation on why it’s wrong/misleading. I’ll add quick summary of joepie91’s arguments and a short comment for each.

Claimed JWT benefits

Easier to horizontally scale

This is the only claim in the list that is technically somewhat true, but only if you are using stateless JWT tokens. The reality, however, is that almost nobody actually needs this kind of scalability — there are many easier ways to scale up, and unless you are operating at the size of Reddit, you will not need ‘stateless sessions’.

Horizontal scaling for Stateful JWT won’t differ much from scaling for classic sessions based on session store.

Easier to use

“You will have to deal with session management yourself, on both the client and the server side

Would agree on this, JWT definitely is not easier, but at least with it we can receive a valuable payload.

More flexible

“this is just used as a buzzword”

JWT allows developers to implement their own security mechanism. Assuming you have a refresh token stored in a secured cookie you’re not limited to store your access token in cookies, local storage or even in memory. It’s possible to use the same auth mechanism for web and mobile apps with JWT, while web will be using cookies for JWT and mobile apps will store tokens in a keychain or elsewhere.

More secure

“A lot of people think that JWT tokens are “more secure” because they use cryptography.”

JWT security isn’t only about cryptography.
Common problem with cookies which is not solved for the most session implementations is that you cannot detect when a cookie is hijacked. So the only input you can get is when a user of your application themselves notifies you about some actions which were not performed by him or her.
Yet it is possible to detect it with JWT if you’re using a pair of tokens: access and refresh. There are some conditions of the implementation that should be met: there can be only one active access token per refresh; an application should only perform refresh requests once an access token gets expired.

In this case it increases chances to identify if a refresh token got leaked. If both real user and attacker are going to use API at the same time they will have to perform refreshes before the expiration of the current access token since only one of them owns the correct one based on the latest refresh operation.

Built-in expiration mechanism

“This is nonsense, and not a useful feature. Expiration can be implemented server-side just as well”

A regular cookie has the same expiration mechanism.

No need to ask users for ‘cookie consent’

“If you are using a session or token for other purposes (eg. analytics or tracking), then you do need to ask for user consent, regardless of how you store that session.”

That’s true.

Prevents CSRF

“It doesn’t, really.”

True. It’s up for developer to prevent different kinds of security attacks. CSRF is easier to prevent than XSS. For a web application running in a browser it’s preferable to store JWT in cookies and use a CSRF token for all requests except GET and HEAD.

Works better on mobile

“Nonsense. Every mobile browser still in use supports cookies, and thus sessions.”

Also mobile app’s HTTP libraries can support cookies usually you’re not really interested to store them in a key-chain. It’s rather different for tokens as if you have a pair of access/refresh (access — contains a lot of data, refresh — is relatively small piece of data) you can store only the refresh token.

Works for users that block cookies

“Users don’t just block cookies, they typically block all means of persistence”

Agreed.

Summary

Indeed, some benefits are ruined, but still some of the claimed benefits take place. Next we’re gonna look at joepie91’s security drawback points in detail and do the same as we did with benefits list.

Claimed JWT drawbacks

They take up more space

“Especially when using stateless JWT tokens, where all the data is encoded directly into the token, you will quickly exceed the size limit of a cookie or URL.”

As being said, usually it’s preferable to use stateful JWT for sessions.
But even if we’re talking about stateless JWT this problem completely depends on the app’s architecture. You won’t really store too much data in JWT the same way as you won’t store it in a regular cookie.

They are less secure

“When storing your JWT in a cookie, it’s no different from any other session identifier. But when you’re storing your JWT elsewhere, you are now vulnerable to a new class of attacks.”

Depends on implementation. If you store your tokens in cookies you’re secure but yet you’ll need to face CSRF, if you store them in LocalStorage — you’re vulnerable to XSS.

You cannot invalidate individual JWT tokens

“individual stateless JWT tokens cannot be invalidated”

Indeed, you cannot invalidate stateless JWT, but the same goes for stateless cookies. That’s a general flaw of almost any stateless session identifier.
Yet, you can invalidate sessions based on stateful JSON Web Tokens.

Data goes stale

“The data in a stateless token will eventually ‘go stale’, and no longer reflect the latest version of the data in your database.”

Depends on what data do you store in the token and your expiration times.

Implementations are less battle-tested or non-existent

That’s true.
Just as a proof of concept I made a ruby implementation of Stateful JSON Web Token sessions https://github.com/tuwukee/jwt_sessions
README includes details, but in short it generates access, refresh, and CSRF tokens and implements headers/cookies requests validations. It can be easily ported to different languages.

Conclusion

Using JWT for sessions will require developers to implement a custom authorization system on both server and client sides. When implemented correctly it can represent a secure solution with a set of benefits like a uniform authorization mechanism for different API clients and a reduced server load, as some part of data can be retrieved from the token’s payload without extra DB hits.

--

--