Tuesday 24 May 2016

From Session to JWT : A graceful migration.

Introduction

Today, We will discuss about a relatively new way of authenticating requests in a stateless manner which I recently implemented for a client. We will also discuss about some of the issues with sessions and how JWT solves those problems. Finally I hope you can read more and consider migrating to JWT.

Plain Ol' Sessions

HTTP is a stateless protocol. This is because each request is new to the server irrespective of if the same client made the same request, and it is also connection-less in that when a request is made, a response is returned and the connection is closed.
The first problem that comes to mind is how does a server distinguish between several users? If I was a server, I would like to know who was requesting for content as that might allow me to send meaningful and personalized content. This is the problem of maintaining state.

This problem was first solved with the use of cookies. Cookies were a relatively good solution to the problem of maintaining state. Basically, when a user makes a request, a server sends back certain data that the user must attach with each subsequent request. It is this combined with additional processing at the server that enables us to distinguish each user.

Note that with each new request, the server must use the simple data sent with the request to get more information about the user. This can be making database calls or accessing a session data store. This can slow down the application.

Another issue with sessions is that it goes against the fundamental nature of HTTP which is that it is a stateless protocol. Sessions store state and try to verify every request. What if there was a way to achieve stateless authentication of a request without the need for any extra calls o a data source?

JSON Web Tokens (JWT)

JWT is an open standard for securely creating and signing tokens that assert a number of claims.
The first thing to have in mind is that JWT is just JSON and the aim is to carry some claims with each request to the server. Let us look at the structure of JWT and how it fits into the whole framework of of authenticating a request.

Header

The header looks something like this: header = '{"alg":"HS256","typ":"JWT"}'
This states the type of  token and that it is signed with HMAC-SHA256.

Payload

The pathyload contains the data the server want to send to the user. It can contain information such as an email or username. It looks like this: payload = '{"username":"admin","iat":1458694982, "email" : "@gmail.com"}'


The third and last part is called the signature and it involves the base64 encoding of both the header and the payload. We will consider these functionalities in terms of functions and who's (server or client) responsibility it is to perform the function.

Generating and Signing of Token

 When a request is made and the request contains no token, the server assumes that the user is not authenticated and send a 401 not authorized. Suppose that the user is trying to login or register, then it is the function of the server to generate and sign a token which is sent back to the client.


    function generate(privateKey, header, payload) {
        unsingedToken = e(header) + . + e(payload);
        signature = HMAC-SHA256(unsignedToken, privateKey);
        token = e(header) + . + e(payload) + . + e(signature);
        return token;
    }

So there is a private key that is known only to the server and is used for signing tokens. The function e(x) used is the base64url encoding scheme. Notice that the token uses dots to demarcate the header, footer and the signature parts. This is what is sent to the client and proves that the client is now authenticated.


Verification of Tokens

It is the function of the server to verify the authenticity of a token that is sent as part of a request.  The server knows that all its tokens are secretly signed and so, if this token is able to produce the same signature segment as what would be produced by the server then it must have been signed by the server and is hence authentic. Below is a basic idea of how the pseudocode should look like


    function generate(privateKey, header, payload) {
        unsingedToken = e(header) + . + e(payload);
        signature = HMAC-SHA256(unsignedToken, privateKey);
        token = e(header) + . + e(payload) + . + e(signature);
        return token;
    }

    //decode is base64decode function
    function(privateKey, token) {
        
        payload = decode(token.split(.)[1]);
        header = decode(token.split(.)[0]);
        return token == generate(privateKey, header, payload);
    }

As you can see in the second function, we basically fetch the header and payload parts by splitting by the dot operator. Subsequently we can re-generate a token based on our private key and compare with what the client sent in the request. If they don't match we reply with a 401 unauthorized otherwise, we allow the request to be handled.

The function of the client

The main function of the client is to store the token and ensure it is sent along with every request. To store the token in a browser, we can use local storage. Though this is not implemented in all browsers, we can default to using cookie storage.
With each request, the client must send the token as a header to the server.

Conclusions

JWT solves the problem of authenticating request beautifully. In place of HMAC-SHA256, You can use RSA public/private key system. It can also be extended to authenticating API requests. 
Migrating to JWT is easy and straight forward. It comes with libraries to handle the generating and signing of requests. It is also easy to make the client send the token on each request. All in all, you get an easy, stateless and efficient way to handle authentication.

For more about JWT please check out JWT
Qusestions : Leave in comments or shoot me an email at : arthurugochukwu@gmail.com

No comments:

Post a Comment