Follow

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 server. You can find installation instructions here.
  • Install Redis npm module to connect to the Redis server from NodeJS app.
  • JWT uses an algorithm while generating the token. You can find the list of algorithms supported by JWT
    here. 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.

Check for existing token

  • Before creating a new token, check whether the token is present for the user or not.
  • If the previous token is present in Redis is a valid JWT token (token is not expired), increase the token expiry time in Redis and send the token to the client.
  • If the previous token is not present, server will create a new token.

New token creation

  • Before we create a new JWT token, server will fetch the necessary information that needs to be stored in the token. In our case, we are storing the following information.
  • Read private key file.
  • Generate the token using jwt sign method. Find more details about jwt sign method here.
  • Store the token in Redis. You can find more information about how and why we are storing the token in Redis here.
  • Set the expiry time for token in Redis.
  • Return the new token to the client.

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.
    • Get the ip address from the request.
    • Server will read the public key file.
    • Verify the token using verify method provided by jwt module. Find more details about jwt verify method here.
    • The ignoreExpiration option will allow the JWT to still decode the token even if the token is expired.
    • Get the user ID from the decoded token.
    • Create Redis key using user ID and ip.
    • Check whether the JWT token has expired or not.
      • Get the token expiry time from the decoded token.
      • Convert token expiry time from seconds to milliseconds.
      • Get the current time in milliseconds.
      • If the current time is greater than the token expiry time, then that means JWT token is expired.
    • If JWT token has expired, return 403 (forbidden) status to the client and also delete the token from Redis.
    • If JWT token hasn’t expired, check for the token in Redis.
    • 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 –

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.

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.
  • As server is already storing the user ID in the current token, server will get the user ID from the current token.
  • Then, server will fetch the role of the user from this table using user ID and organization ID.
  • Server maintains the mapping of user roles in a constant in the configuration file.
  • Based on user role, server will get the allowed methods.
  • If the user requested method is present in the allowedRoutes, then the server will process the request, else it will return 403 (forbidden) status to the client.

Database structure

The database structure we followed in our implementation consists of four tables.

Table 1: users

Column Type Comments
email_id VARCHAR(100) UNIQUE
password VARCHAR(100) NOT NULL
is_admin TINYINT(1) DEFAULT – 0
user_id CHAR(36) PRIMARY KEY, NOT NULL

 

Table 2: roles

Column Type Comments
role_id CHAR(36) PRIMARY KEY, NOT NULL
role_name VARCHAR(50) UNIQUE, NOT NULL

 

Table 3: user_roles

Column Type Comments
user_role_id CHAR(36) PRIMARY KEY, NOT NULL
user_id CHAR(36) FK_user_user_id
organization_id CHAR(36) FK_organization_organization_id
role_id CHAR(36) FK_role_role_id

 

Table 4: organizations

Column Type Comments
organization_id CHAR(36) PRIMARY KEY, NOT NULL
organization_name VARCHAR(100) NOT NULL

Advantages

  • This system is simple and easy to implement.
  • JWT token doesn’t provide a mechanism to destroy or expire the token, due to which implementing log out is difficult. Our system overcomes that snag by using Redis.
  • A single user can have different roles in multiple organizations.

Drawbacks of this implementation

  • API classification for roles is limited to HTTP methods.
  • Token has a fixed invalidation period and is not invalidated based on inactivity period.

Posted By: Surendra Kumar B, Osmosee

Are you interested? follow us and get notified of new posts

Leave A Reply

15 + 6 =