User Authentication and Authorization – Part ll

Introduction

Authentication is used by the server when it needs to know the identity of someone who is accessing their information.Authorization is a process by which the server determines if the client has permission to use a resource or
access a file. In general, authentication answers the question“Who are you?” and authorization answers “What are you allowed to do?”.

In an earlier blog, we have discussed about Token based authentication and authorization and how it is being implemented in one of our projects. Over these past few months, we’ve made changes to our approach.
Read on to find out what enhancements we have made and why.

Tools used for implementation

JWT

JSON Web Token (JWT) is a means of representing a signed content using JSON data structures (generally called as claims). You can find more information about JWT here.

REDIS

Redis is a data structure server and acts as “NoSQL” key value cache data store. You can find more information about Redis here.

Authentication

Login flow

When the user logs in, server will validate the credentials.

If the credentials are invalid, server will return 401 (Unauthorized) status to the client.

If the credentials are valid, server will check if the token (JWT) exists in redis or not. If it exists, then server will return the old token, else it will generate a new token (JWT) and send that new token to the client.

In addition to this, server will also store the token in Redis. This will be dealt with in the later sections where we will discuss about this in more detail.

Client should store the token and send it for every request.

If the client is making a request without a token, server will return 403 (Forbidden) status to the client.

Token creation, verification and destruction

To work with JWT token, first we need to set up the following things:

Install jsonwebtoken npm module to generate JWT tokens in NodeJS. You can find instructions for installing this module here.

Install Redis npm module to connect to the Redis server from NodeJS app.

npm install redis --save

JWT uses an algorithm while generating the token. You can find the list of algorithms supported by JWThere. We chose to use the RSA256 algorithm to generate the token. You can learn about different JWT algorithms here and select a more befitting one for your use case.

As we are going with RSA256 algorithm, we need to generate our own public and private keys to create and verify JWT tokens.

You can follow the instructions mentioned in the below URL’s to create your own public and private key pair.

Storing token

We are storing the token in Redis because JWT doesn’t provide any mechanism to deactivate/delete the token.

When the user is logged out, server will delete the token from Redis. That way the user cannot make any more requests with the old token. You can check how the server is performing token validation here.

Server will also store logged in user system IP as a part of Redis key in Redis. This way the same token will be used when the user is logging in from multiple machines which have same public IP.

Server will store the generated token in Redis which will internally store the token as the key-value pair as follows.

Token verification

Every time the client makes a request to the server, the token will be sent in the request header. Server will retrieve the token from request headers.

If the token does not match with the token in Redis, server will return 403 (forbidden) status to the client.

If JWT token has not expired and the token matches with the token present in Redis, then the server will process the request. In addition to it, the decoded data is stored in the request as follows –

request.currToken = decodedData;

Destructing a token

If the JWT token has expired, then the token will be deleted from Redis.

If the user logs out, then we cannot do anything about JWT token since it does not provide a method to destroy the token. This limitation is handled by deleting the token from Redis.

Authorization

Types of user roles we followed

Let’s do a quick recap of what was done previously. In our project, we segregated the APIs into two categories:

Public

All the public APIs are accessible without a token.

Private

All the private APIs need token in the request header.

Further, we allotted two levels to the private APIs,

Super admin level

Organization level

Super admin will have access to all the public and private APIs.

Organization level APIs will be accessible by the users based on their role in the organization for which they are requesting data. The role can be one of the following:

Full

Read

None

Note that users can access public APIs irrespective of their role.

Mapping user roles (Organization)

We implemented user roles in such a way that one user can belong to multiple organizations and can have a different role in each organization.

Note: Super admin will have access to all organizations irrespective of his / her role in the organization.

In the above figure, you can observe that user (green color shaded) has a different role in each organization. Refer this table to understand how we are assigning different roles in different organizations for the same user.

Restricting users based on role

This restriction will be applicable only for organization level APIs. Following is an example URL for organization level API.

GET /api/v1/organization/:orgID/users

When the user is making a request to the above URL, the following process will happen on the server.

Server will get the organization id from the API URL.

var orgID = request.params.orgID;

As server is already storing the user ID in the current token, server will get the user ID from the current token.

var userID = request.currToken.userID;

Then, server will fetch the role of the user from this table using user ID and organization ID.