Our Considerations on Token Design & Session Management

Stacy Goh
Government Digital Products, Singapore
7 min readJan 25, 2018
Source: https://www.pinterest.co.uk/explore/admit-one-ticket/

While working on our project, an app which facilitates communication between parents and teachers, we have to perform authentication and session management for both our deliverables — a mobile application and a web portal. A question we all had then was — should both platforms employ the same implementation? How different are they then?

Our team then came up with a few considerations, checked with a few other teams on the best ways to implement session management and here, I humbly try to summarise the findings and research we have made over the past few weeks.

Our considerations

We had the following considerations when designing tokens/session management.

  1. Security — How should we minimise attacks such as Cross Site Request Forgery (CSRF)?
  2. Scalability — Would the implementation be scalable if I deploy the same application across multiple servers?
  3. Control — In the case when I need to revoke a user’s access to an application, will I be able to push him out of the app or stop him from making API calls?

Token based authentication vs. the traditional Session based authentication

A long time ago, before token-based authentication, there was session-based authentication.

In session-based authentication, a user’s credentials are sent to the server where it authenticates the user. If the credentials are valid, the server creates a user session and sends back a cookie containing only the SESSION_ID, which is set on the user’s browser. The SESSION_ID is just an identifier which associates the user with a user session. The user sessions reside in memory in the server or in a database.

Since session data is stored in memory on the server, we need a separate central session storage system when we start to scale up horizontally that all the servers can access.

This form of authentication makes it stateful with a heavy dependency towards the central session storage system, leading to a single point of failure. Without the central session storage system being available, it could fail the entire session management flow.

Cons of session-based authentication:

  • Without a central storage system, scalability will be an issue if the data is stored in one server and there is a need to scale up horizontally — unless we can be sure that the user is directed to the same server each time he logs in
  • Single point of failure with a central session storage system
  • With user sessions stored in memory, there is risk of extensive memory usage unless stored in a memcache like Redis

If you are building a small scale application with no need to scale up, session-based authentication might be the way to go. However, in our case, we decided on tokens.

What are JSON Web tokens (JWT)?

JWT tokens are stateless which means unlike session-based management, user state is never saved in server memory, but rather it has all the information needed in the token body.

JWT tokens look like:

xxxxx.yyyyy.zzzzz

consisting of 3 parts:

  • Header (xxxxx)— contains the hashing algorithm which is then Base64Url encoded)
  • Payload (yyyyy)— contains claims such as information/metadata about a user (e.g., name) which is then Base64Url encoded
  • Signature (zzzzz)— take the encoded header, the encoded payload, a secret and sign with the algorithm specified in the header

How do JWT tokens work?

When a user signs in with his credentials, a JWT will be generated on the server side and sent back to the client. The JWT will then be saved locally on the client.

Whenever the user wants to access a protected route, it should send the JWT in the Authorization header using the Bearer schema :

Authorization: Bearer <JWT>

The backend will then receive the Authorization header with the JWT in it and verify the signature. If the JWT was altered, the signature of the JWT would not match anymore and hence, the request to access a protected route cannot be completed.

If you are using a Node backend like our project, a recommended library to facilitate JWT implementation would be https://github.com/auth0/node-jsonwebtoken.

What should be in the payload?

As one can see the contents of a JWT by decoding it, the payload of the JWT should not contain overly sensitive information, or else they could be encrypted to ensure that the payload and its contents are very secure.

An example of an unencrypted JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlN0YWN5IEdvaCIsImFkbWluIjoiZ292dGVjaC1zdGFmZiJ9.LqBohlEVeMctjvEBCLmIGMG3qPdBtXr6TWVDucwmE3A

Go ahead and paste it in https://jwt.io/ to decode it. You will be able to see the payload that is attached to this JWT. If we encrypt it however, you get this:

4C4334DDFEC22977C6E167FED9C99FE32915321E098FFE020FD2309DB0DB6ED2

Pasting this string into https://jwt.io/ to decode it now does not allow you to see of the user’s information.

Security Risks with JWT?

JWTs are self-contained, which means it contains user information in the payload section. As the JWT is Base64encoded, it would also be possible to view the contents of the JWT (and hence the user’s information/metadata) simply by decoding it if the JWT is not encrypted. However, it is not possible to modify the contents of the JWT if you have no understanding of the secret since the receiver will notice that the signature does not match.

I like this layman explanation on how JWTs work which I found in a Stack Overflow thread:

Let’s assume Alice wants to send a JWT to Bob. They both know some shared secret. Mallory doesn’t know that secret, but wants to interfere and change the JWT. To prevent that, Alice calculates Hash(payload + secret) and appends this as signature. When receiving the message, Bob can also calculate Hash(payload + secret) to check whether the signature matches. If however, Mallory changes something in the content, she isn’t able to calculate the matching signature (which would be Hash(newContent + secret)). She doesn’t know the secret and has no way of finding it out. This means if she changes something, the signature won’t match anymore, and Bob will simply not accept the JWT anymore.

Source: https://stackoverflow.com/questions/27301557/if-you-can-decode-jwt-how-are-they-secure

For more an in-depth explanation on JWT Tokens:

https://auth0.com/learn/json-web-tokens/

Consider lifespan of JWT

Having short-lived JWTs (e.g., an hour) and requiring them to be re-issued regularly can allow you to revoke an unwanted user from your system.

Next, let’s talk about refresh tokens.

Using Refresh tokens with JWT tokens

Refresh tokens allow us to get a new JWT token when the JWT token has expired. A client may use a refresh token to exchange for a new set of JWT token and refresh token whenever the client is trying to access an endpoint but the token has already expired. Refresh tokens can expire as well, but have longer time span than JWT tokens.

Why do we need refresh tokens?

Refresh tokens allow a user’s access to an application to be revoked in the case of blacklisting a user. Since refresh tokens generate new JWT tokens, we store the refresh tokens in the database and if the particular user is blacklisted, we disallow generating of new JWT tokens. Having said that, there is still a window during which the refresh token has been revoked, but its JWT token may still be valid. Hence, JWT tokens should be short lived.

TOKEN FLOW (PART 1)
TOKEN FLOW (PART 2)

How different is session management for web & mobile?

Session management is slightly different for web and mobile in terms of how to store the JWT.

When developing a mobile application in React-Native, we can use libraries such as this which helps us store the JWT securely in the KeyChain or KeyStore.

However, if you are developing a web portal, there are two options to store your tokens in — Cookies vs. local/session storage.

Does it matter? Both methods are stateless since all the data is in the payload.

In fact, both methods have pros and cons. Let’s explain it in terms of security.

Security

Local/session storage is accessible through JavaScript on the same domain and hence, makes it vulnerable to Cross Site Scripting XSS (injecting of JavaScript) attacks.

As for cookies, setting a http-only flag mitigates risk of theft of cookies through XSS, but you should handle the risk of CSRF.

According to Wiki, “CSRF is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated”. One way of doing so would be to use a library such as CSURF which allows you to pass in an additional hidden CSRF token in a form or to any request which mutates state. This token is validated in the server-side.

Lastly, we should always use HTTPS/SSL to ensure JWTs are encrypted during client and server transmission, reducing the risk of man-in-the-middle attacks.

For more information on cookies vs local storage: https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

In summary, we have tried to address our considerations when building our application as follow.

Security — Use HTTPS/SSL and if you are using cookies, you can use a library such as CSURF to minimize the risk of CSRF attacks.

Control — Using refresh tokens is one way of revoking a user’s access to an application (blacklisting a user)

Scalability — Using a token based authentication method allows scalability because we can use tokens to access resources from different servers without worrying if the user was actually logged in on a particular server.

That’s all folks.

Feel free to leave any comments if you have better suggestions on session management! We are also hiring developers/QA engineers, so do ping me at stacy_goh@tech.gov.sg if you think you have what it takes to join the happy club!

P.S. Just got news that we have open sourced a JWT authentication server. Check it out here: https://github.com/GovTechSG/rowdy

--

--

Stacy Goh
Government Digital Products, Singapore

Software Engineer at GDS, Govtech Singapore. I don't believe in deep down. I kinda think that all you are is just the things that you do.