Implementing JWT Using Passport
March 31st, 2016 | By Caio Ribeiro Pereira | 8 min read
We will explore the main concepts and implementations of user 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, let’s set 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 set up 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 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;
We will use a simple array of user 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: "[email protected]",
password: "john123"
}, {
id: 2,
name: "Sarah",
email: "[email protected]",
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 authentication, 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.
On its official website, there is a list with +300 authentication strategies, created and maintained by 3rd-party.
About JWT
JWT (JSON Web Tokens) is a very simple and secure authentication strategy for REST APIs. It is an open standard for web authentications and is totally based on JSON token requests between client and server. Its authentication engine works like this:
The client makes a request once by sending their login credentials and password;
The 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;
The client, after receiving this token, can store it the way it wants, whether via LocalStorage, Cookie, or other client-side storage mechanisms;
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;
The server always validates this token to allow or block a customer request.
Installing Passport and JWT
To start the fun, we’ll use the following modules:
passport: as an authentication 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 make your application vulnerable, allowing any bad-intentioned person to access the system and manage the tokens from logged users without informing the correct credentials in the authentication process.
To finish, the last included field is jwtSession which has the object {session: false}. This item is used to inform Passport that the API won’t manage the session.
Implementing JWT authentication
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, which will also have middleware functions provided by Passport to use in the API’s routes. This code is going to have a middleware and two main functions.
The middleware will be executed at the moment it starts the application, and it basically receives in its 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 an argument to search a user in the database, in our case, this id will be used to get user data from the array of users from the 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 in the 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 the 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 a new Strategy(). This object receives two important arguments:
secretOrKey: the JWT secret key.
jwtFromRequest: defines where the tokens will be sent in the response (header, querystring, body),
Inside the strategy callbacks, you can do any validation you want, in our case, we are just searching for the right user if the request sends the right payload.id. In the real world, you can write authentications to find users in 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 for 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 renders the authenticated user’s data. By 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 tokens 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
We have finished an extremely important implementation for a lot of kinds of applications, the authentication process.
Thanks to JWT, now we have a safe mechanism for user authentication between client and server using only JSON data.
Jscrambler
The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.
View All Articles