March 31, 2016

Implementing JWT using Passport

by Caio Ribeiro Pereira

jwt_passport

In this post, we’ll explore the main concepts and implementations of user’s authentication using the mechanism called JWT (JSON Web Token) via a Passport module. After all, this is an important step to ensure that users can safely authenticate into a REST API.

Before we start, let’s create a simple REST API, which will be used during this post. We are going to create an Express API to simplify our example. To start off, let’s setup our project, open the terminal and type this command:

mkdir my - api  
cd my - api  
npm init  

The npm init shows a quick quiz to setup some descriptions and generate the package.json file, which is the main file that we will use to install some modules for our project. Feel free to answer each question in your own way. After this, install the express framework and body-parser module, by running this command:

npm install express body - parser--save  

Now that we have the Express module installed, let’s write our API code. To do it, let’s create the index.js with the code below:

// index.js
var express = require("express");  
var bodyParser = require("body-parser");  
var app = express();

app.use(bodyParser.json());

app.get("/", function(req, res) {  
    res.json({
        status: "My API is alive!"
    });
});

app.listen(3000, function() {  
    console.log("My API is running...");
});

module.exports = app;  

In this post we will use a simple array of users data to facilitate the implementation of JWT auth. But, in real applications it’s highly recommended to use a database instead of a simple array of users. We will use this array only for sample purposes. We need a list of users data which will be used to verify if a request is from an authenticated user, to do it, create the users.js with the code below:

// users.js
// Fake list of users to be used in the authentication
var users = [{  
    id: 1,
    name: "John",
    email: "john@mail.com",
    password: "john123"
}, {
    id: 2,
    name: "Sarah",
    email: "sarah@mail.com",
    password: "sarah123"
}];

module.exports = users;  

Now we have a simple API enough to explore in the next sections how to implement JWT authentication.

Introduction to Passport.js and JWT

About Passport.js

There is a Node.js module very cool and easy to work with user’s authentication, it’s called Passport.

Passport is a framework that is extremely flexible and modular. It allows you to work with the main authentication strategies: Basic & Digest, OpenID, OAuth, OAuth 2.0 and JWT. And also allows you to work with external services authentication, such as Facebook, Google+, Twitter and more. By the way, in its official website, there is a list with +300 authentication strategies, created and maintained by 3rd-party.

site-passport

Its official website is: passportjs.org.

About JWT

JWT (JSON Web Tokens) is a very simple and secure authentication’s strategy for REST APIs. It is an open standard for web authentications and is totally based in JSON token’s requests between client and server. Its authentication’s engine works like this:

  1. Client makes a request once by sending their login credentials and password;
  2. Server validates the credentials and, if everything is right, it returns to the client a JSON with a token that encodes data from a user logged into the system;
  3. Client, after receiving this token, can store it the way it wants, whether via LocalStorage, Cookie or other client-side storage mechanisms;
  4. Every time the client accesses a route that requires authentication, it will only send this token to the API to authenticate and release consumption data;
  5. Server always validate this token to allow or block a customer request.

To specific details about JWT, go to jwt.io.

site-jwt

Installing Passport and JWT

To start the fun, we’ll use the following modules:

  • passport: as authentication’s engine;
  • passport-jwt: as JWT authentication’s strategy for Passport;
  • jwt-simple: as encoder and decoder of JSON tokens;

Now, let’s install them by running this command:

npm install passport passport-jwt jwt-simple --save  

To start this implementation, first we are going to create a config.js file to add two settings items for JWT (jwtSecret and jwtSession):

// config.js
module.exports = {  
    jwtSecret: "MyS3cr3tK3Y",
    jwtSession: {
        session: false
    }
};

The field jwtSecret keeps a secret key string that serves as a base to encode and decode the tokens. It’s highly advisable to use a complex string with many different characters and never share this secret key in public, because, if it leaks, you will let your application vulnerable, allowing any bad intentioned person to access the system and manages the tokens from logged users without informing the correctly credentials in the authentication process.

To finish, the last included field is jwtSession that has the object {session: false}. This item is used to inform Passport that the API won’t manage session.

Implementing JWT authentication

Now that we have the Passport and JWT settings ready, let’s implement the main rules on how the client will be authenticated in our API. To start, we are going to implement the authentication rules, that will also have middleware functions provided by Passport to use into the API’s routes. This code is going to have a middleware and two main functions. The middleware will be executed in the moment it starts the application, and it basically receive in his callback a payload that contains a decoded JSON which was decoded using the secret key cfg.jwtSecret. This payload will have the attribute id that will be a user id to be used as argument to search a user in the database, in our case, this id will be used to get a user data from the array of users from users.js file. As this middleware is going to be frequently accessed, to avoid some overheads, we are going to send a simple object containing only the id of a user, using the callback function:

done(null, {  
    id: user.id
});

This middleware will be injected via passport.use(strategy) function. To finish, two functions will be included from Passport to be used on the application. They are the initialize() function which starts the Passport and authenticate() which is used to authenticate the access for a route.

To understand better this implementation, let’s create in the root folder the auth.js file, using this code:

// auth.js
var passport = require("passport");  
var passportJWT = require("passport-jwt");  
var users = require("./users.js");  
var cfg = require("./config.js");  
var ExtractJwt = passportJWT.ExtractJwt;  
var Strategy = passportJWT.Strategy;  
var params = {  
    secretOrKey: cfg.jwtSecret,
    jwtFromRequest: ExtractJwt.fromAuthHeader()
};

module.exports = function() {  
    var strategy = new Strategy(params, function(payload, done) {
        var user = users[payload.id] || null;
        if (user) {
            return done(null, {
                id: user.id
            });
        } else {
            return done(new Error("User not found"), null);
        }
    });
    passport.use(strategy);
    return {
        initialize: function() {
            return passport.initialize();
        },
        authenticate: function() {
            return passport.authenticate("jwt", cfg.jwtSession);
        }
    };
};

The JWT validation starts when a new strategy is instantiated via new Strategy(). This object receives two important arguments:

Inside the strategy callbacks, you can do any validation you want it, in our case, we are just searching for the right user if the request sends the right payload.id. In real world, you can write authentications to find users into a database.

To load the auth.js during the server boot time and initiate the Passport middleware via app.use(auth.initialize()). Edit the index.js to be like this:

// index.js
var express = require("express");  
var bodyParser = require("body-parser");  
var auth = require("./auth.js")();  
var app = express();

app.use(bodyParser.json());  
app.use(auth.initialize());

app.get("/", function(req, res) {  
    res.json({
        status: "My API is alive!"
    });
});

app.listen(3000, function() {  
    console.log("My API is running...");
});

module.exports = app;  

Generating tokens for authenticated users

To finish the JWT authentication, we are going to create a route to generate tokens users who are going to authenticate themselves using their email and password on the system, and we’ll do a refactoring in the main route so that their access properly render the authenticated user’s data. Doing this, we finish this authentication step, making our application reliable and safer.

Now let’s create the endpoint /token. This route will be responsible for generating an encoded token with a payload, given to the user that sends the right e-mail and password via req.body.email and req.body.password in the request.

The payload is going to have only the user id. The token generation occurs via jwt-simple module using the function jwt.encode(payload, cfg.jwtSecret) that mandatorily, will use the same secret key jwtSecret which was created on the config.js file. To simplify the error handler of this endpoint, any error will be treated using the HTTP 401 - Unauthorized status code using the res.sendStatus(401) function.

To include this rule of tokens generation, let’s edit the index.js file using the following code:

// index.js
var express = require("express");  
var bodyParser = require("body-parser");  
var jwt = require("jwt-simple");  
var auth = require("./auth.js")();  
var users = require("./users.js");  
var cfg = require("./config.js");  
var app = express();

app.use(bodyParser.json());  
app.use(auth.initialize());

app.get("/", function(req, res) {  
    res.json({
        status: "My API is alive!"
    });
});

app.post("/token", function(req, res) {  
    if (req.body.email && req.body.password) {
        var email = req.body.email;
        var password = req.body.password;
        var user = users.find(function(u) {
            return u.email === email && u.password === password;
        });
        if (user) {
            var payload = {
                id: user.id
            };
            var token = jwt.encode(payload, cfg.jwtSecret);
            res.json({
                token: token
            });
        } else {
            res.sendStatus(401);
        }
    } else {
        res.sendStatus(401);
    }
});

app.listen(3000, function() {  
    console.log("My API is running...");
});

module.exports = app;  

To finish our API, let’s create a private route, which will output the authenticated user’s data, this route must use the auth.authenticate() middleware running before the route app.get("/user") function. This private route will only run for authenticated token and you can use the req.user.id object inside this route, because this data will be available if you send the right token, and with this id we will output a JSON with the authenticated user via res.json(users[req.user.id]) function. To create this route, let’s edit the index.js again, take a look:

// index.js
var express = require("express");  
var bodyParser = require("body-parser");  
var jwt = require("jwt-simple");  
var auth = require("./auth.js")();  
var users = require("./users.js");  
var cfg = require("./config.js");  
var app = express();

app.use(bodyParser.json());  
app.use(auth.initialize());

app.get("/", function(req, res) {  
    res.json({
        status: "My API is alive!"
    });
});

app.get("/user", auth.authenticate(), function(req, res) {  
    res.json(users[req.user.id]);
});

app.post("/token", function(req, res) {  
    if (req.body.email && req.body.password) {
        var email = req.body.email;
        var password = req.body.password;
        var user = users.find(function(u) {
            return u.email === email && u.password === password;
        });
        if (user) {
            var payload = {
                id: user.id
            };
            var token = jwt.encode(payload, cfg.jwtSecret);
            res.json({
                token: token
            });
        } else {
            res.sendStatus(401);
        }
    } else {
        res.sendStatus(401);
    }
});

app.listen(3000, function() {  
    console.log("My API is running...");
});

module.exports = app;  

Conclusion

Congratulations! We have finished an extremely important implementation for a lot of kind of applications, the authentication process. Thanks to JWT, now we have a safe mechanism for user’s authentication between client and server using only JSON data.