Recently I have been involved in various discussions on how to make the REST APIs as secure as SOAP.
First of all, let me start with a very basic statement, about security, it doesn't depend on your Webservice type, be it REST or SOAP, your design decisions depicts whether they can be made secure or not.
In this example blog, I will use "Micro CRUD services for Oracle Database Cloud" APIs and implement (read attach) Passport's "local" authentication strategy, to make them secure.
Code in Github : LeasifyAPIs with Passport
What is Passport JS? (from Documentation)
Passport is authentication middleware for Node. It is designed to serve a singular purpose: authenticate requests. When writing modules, encapsulation is a virtue, so Passport delegates all other functionality to the application. This separation of concerns keeps code clean and maintainable, and makes Passport extremely easy to integrate into an application.
What are "strategies" in Passport JS?
"Strategies" are different techniques to authenticate an user, who is accessing the hosted REST APIs. Be it, authenticating using LDAPs, Identity stores, OAuth, social (Facebook, Google or LinkedIn) etc. Passport provides around 307 different strategies to choose from. Check out their website for more info : http://passportjs.org/
What is it I'm going to "attach"?
I mentioned "attach", not implement. Because, I am going to :
Authentication mechanism
Let me explain, my authentication mechanism a bit.
"salt" and "hash"
I am going to authenticate the users, from database. I have a table containing all the users. But I don't store passwords, instead, I store "salt" and "hash".
The "salt" is a string of characters unique to each user.
The "hash" is created by combining the password provided by the user and the salt, and then applying one-way encryption.
As the hash cannot be decrypted, the only way to authenticate a user is to take the password, combine it with the salt and encrypt it again. If the output of this matches the hash, then the password must have been correct. Resulting in a simple yet very secure implementation.
JSON Web Token
Once, I authenticate the users, via an API, I return a JSON Web Token. Which has a validity of 7 days, and needs to used in the header for all other API calls.
What is JSON Web Token, aka JWT?
Instead of supplying credentials such as a username and password with every request, we can allow the client to exchange valid credentials for a token. This token gives the client access to resources on the server. Tokens are generally much longer and more obfuscated than a password. For example, the JWTs we’re going to be dealing with are on the order of ~150 characters. Once the token is obtained, it must be sent with every API call.
Think of the token like a security pass. You identify yourself at the front desk of a restricted building on arrival (supply your username and password), and if you can be successfully identified you’re issued a security pass. As you move around the building (attempt to access resources by making calls to the API) you are required to show your pass, rather than go through the initial identification process all over again.
So my implementation consists of these 3 major parts :
users.js
A simple JS model for user object.
Some of the important implementation : "setPassword"
var crypto = require('crypto');
User.prototype.setPassword = function(password){
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};
"validate password" :
User.prototype.validPassword = function(password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
return this.hash === hash;
};
Nothing needs installing as crypto ships as part of Node. Crypto itself has several methods; we’re interested in
"generate JWT" :
var jwt = require('jsonwebtoken');
User.prototype.generateJwt = function() {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
return jwt.sign({
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000),
}, "MY_SECRET");
};
To create the JWT we’ll use a module called
This module exposes a
It is important that your secret ("MY_SECRET") is kept safe – only the originating server should know what it is. It is best practice to set the secret as an environment variable, and not have it in the source code, especially if your code is stored in version control somewhere.
passport configurations
To use Passport, first install it and the strategy, saving them in
create a file "passport.js" and put the following code
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('./users');
var oracledb = require('oracledb');
var dbutil = require('../dbutil');
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(username, password, done) {
dbutil.executeDatabaseOperation(function(error) {
return done(err);
}, function(connection) {
var selectStatement = "SELECT NAME, EMAIL, HASH, SALT FROM USERS WHERE EMAIL= :userEmail";
connection.execute( selectStatement
, [username], {outFormat: oracledb.OBJECT // Return the result as Object
}, function (err, result) {
if (err) {
console.log('Error in execution of select statement'+err.message);
return done(err);
} else {
console.log('db response is ready '+result.rows);
if(result.rows.length === 0) {
return done(null, false, {
message: 'User not found'
});
}
dbutil.doRelease(connection);
var user = new User(result.rows[0].NAME, result.rows[0].EMAIL);
user.setHash(result.rows[0].HASH);
user.setSalt(result.rows[0].SALT);
// Return if password is wrong
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Password is wrong'
});
}
// If credentials are correct, return the user object
return done(null, user);
}
}
);
});
}
));
The above code, will retrieve the "user" from database using the email, and then will validate the password passed to this function, eventually return the user object (which we'll use a little bit late to return the JWT).
So, now I have a implemented the "local" strategy to validate a username and password. Time to write an "authenticator" which will use this strategy to validate users.
authentication.js
var passport = require('passport');
var User = require('./users');
var sendJSONresponse = function(res, status, content) {
res.status(status);
res.json(content);
};
module.exports.login = function(req, res) {
if(!req.body.email || !req.body.password) {
sendJSONresponse(res, 400, {
"message": "All fields required"
});
return;
}
passport.authenticate('local', function(err, user, info){
var token;
// If Passport throws/catches an error
if (err) {
console.log(err);
res.status(404).json(err);
return;
}
// If a user is found
if(user){
token = user.generateJwt();
res.status(200);
res.json({
"token" : token
});
} else {
console.log(info);
// If user is not found
res.status(401).json(info);
}
})(req, res);
};
Now I have to link this to /login endpoint. So, in "/routes/index.js", I will add the following :
var ctrlAuth = require('./authenticate/authentication');
router.post('/login', ctrlAuth.login);
The "login" will return the JWT in the response. Time to secure rest of the REST apis.
First initialize passport in "app.js"
var passport = require('passport');
require('./routes/authenticate/passport');
app.use(passport.initialize());
Then, in "/routes/index.js", initialize JWT module.
var jwt = require('express-jwt');
var auth = jwt({
secret: 'MY_SECRET',
userProperty: 'payload'
});
Again, don’t keep the secret in the code! Even though I did :P
To apply this middleware simply reference the function in the middle of the route to be protected, like this:
router.get('/api/getUser', auth, function(req, res, next)...
So, the endpoints which were defined earlier as :
router.<method>('/endpoint', function(req, res, next) {})...
Will be now :
router.<method>('/endpoint', auth, function(req, res, next) {})...
Also, add a "unauthorized" error handler, to validate our implementation
app.use(function(err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401);
res.json({"message" : err.name + ": " + err.message});
} else {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
}
});
All done. You should run the application and check the endpoint "/api/catalogue". It has all the details on how to invoke all the apis.
Ideally :
1. Invoke /addUser : to create a new user.
2. Invoke /login : to generate the JWT token.
3. Invoke other APIs using that JWT token.
Happy coding!!
References :
First of all, let me start with a very basic statement, about security, it doesn't depend on your Webservice type, be it REST or SOAP, your design decisions depicts whether they can be made secure or not.
In this example blog, I will use "Micro CRUD services for Oracle Database Cloud" APIs and implement (read attach) Passport's "local" authentication strategy, to make them secure.
Code in Github : LeasifyAPIs with Passport
What is Passport JS? (from Documentation)
Passport is authentication middleware for Node. It is designed to serve a singular purpose: authenticate requests. When writing modules, encapsulation is a virtue, so Passport delegates all other functionality to the application. This separation of concerns keeps code clean and maintainable, and makes Passport extremely easy to integrate into an application.
What are "strategies" in Passport JS?
"Strategies" are different techniques to authenticate an user, who is accessing the hosted REST APIs. Be it, authenticating using LDAPs, Identity stores, OAuth, social (Facebook, Google or LinkedIn) etc. Passport provides around 307 different strategies to choose from. Check out their website for more info : http://passportjs.org/
What is it I'm going to "attach"?
I mentioned "attach", not implement. Because, I am going to :
- Add 2 Node Modules
- Initialize passport for a "strategy"
- Add a /login API for users to authenticate
- Add one extra parameter to the existing API definitions to make them secure.
Authentication mechanism
Let me explain, my authentication mechanism a bit.
"salt" and "hash"
I am going to authenticate the users, from database. I have a table containing all the users. But I don't store passwords, instead, I store "salt" and "hash".
The "salt" is a string of characters unique to each user.
The "hash" is created by combining the password provided by the user and the salt, and then applying one-way encryption.
As the hash cannot be decrypted, the only way to authenticate a user is to take the password, combine it with the salt and encrypt it again. If the output of this matches the hash, then the password must have been correct. Resulting in a simple yet very secure implementation.
JSON Web Token
Once, I authenticate the users, via an API, I return a JSON Web Token. Which has a validity of 7 days, and needs to used in the header for all other API calls.
What is JSON Web Token, aka JWT?
Instead of supplying credentials such as a username and password with every request, we can allow the client to exchange valid credentials for a token. This token gives the client access to resources on the server. Tokens are generally much longer and more obfuscated than a password. For example, the JWTs we’re going to be dealing with are on the order of ~150 characters. Once the token is obtained, it must be sent with every API call.
Think of the token like a security pass. You identify yourself at the front desk of a restricted building on arrival (supply your username and password), and if you can be successfully identified you’re issued a security pass. As you move around the building (attempt to access resources by making calls to the API) you are required to show your pass, rather than go through the initial identification process all over again.
So my implementation consists of these 3 major parts :
- /addUser : API which adds a new user to the database. This API is also responsible to generates salt and hash based on the password provided, and stores it in the DB.
- DB Table : Users, with 3 important columns. Email (my username), salt and hash.
- /login : API which takes email and password and returns JSON Web Token, this needs to be in the header for all the calls to secured APIs.
users.js
A simple JS model for user object.
Some of the important implementation : "setPassword"
var crypto = require('crypto');
User.prototype.setPassword = function(password){
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};
"validate password" :
User.prototype.validPassword = function(password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
return this.hash === hash;
};
Nothing needs installing as crypto ships as part of Node. Crypto itself has several methods; we’re interested in
randomBytes
to create the random salt and pbkdf2Sync
to create the hash (there’s much more about Crypto in the Node.js API docs)."generate JWT" :
var jwt = require('jsonwebtoken');
User.prototype.generateJwt = function() {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
return jwt.sign({
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000),
}, "MY_SECRET");
};
To create the JWT we’ll use a module called
jsonwebtoken
which needs to be installed in the application (use npm install for this).This module exposes a
sign
method that we can use to create a JWT, simply passing it the data we
want to include in the token, plus a secret that the hashing algorithm
will use. The data should be sent as a JavaScript object, and include an
expiry date in an exp
property.It is important that your secret ("MY_SECRET") is kept safe – only the originating server should know what it is. It is best practice to set the secret as an environment variable, and not have it in the source code, especially if your code is stored in version control somewhere.
passport configurations
To use Passport, first install it and the strategy, saving them in
package.json
.npm install passport --save
npm install passport-local --save
create a file "passport.js" and put the following code
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('./users');
var oracledb = require('oracledb');
var dbutil = require('../dbutil');
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(username, password, done) {
dbutil.executeDatabaseOperation(function(error) {
return done(err);
}, function(connection) {
var selectStatement = "SELECT NAME, EMAIL, HASH, SALT FROM USERS WHERE EMAIL= :userEmail";
connection.execute( selectStatement
, [username], {outFormat: oracledb.OBJECT // Return the result as Object
}, function (err, result) {
if (err) {
console.log('Error in execution of select statement'+err.message);
return done(err);
} else {
console.log('db response is ready '+result.rows);
if(result.rows.length === 0) {
return done(null, false, {
message: 'User not found'
});
}
dbutil.doRelease(connection);
var user = new User(result.rows[0].NAME, result.rows[0].EMAIL);
user.setHash(result.rows[0].HASH);
user.setSalt(result.rows[0].SALT);
// Return if password is wrong
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Password is wrong'
});
}
// If credentials are correct, return the user object
return done(null, user);
}
}
);
});
}
));
The above code, will retrieve the "user" from database using the email, and then will validate the password passed to this function, eventually return the user object (which we'll use a little bit late to return the JWT).
So, now I have a implemented the "local" strategy to validate a username and password. Time to write an "authenticator" which will use this strategy to validate users.
authentication.js
var passport = require('passport');
var User = require('./users');
var sendJSONresponse = function(res, status, content) {
res.status(status);
res.json(content);
};
module.exports.login = function(req, res) {
if(!req.body.email || !req.body.password) {
sendJSONresponse(res, 400, {
"message": "All fields required"
});
return;
}
passport.authenticate('local', function(err, user, info){
var token;
// If Passport throws/catches an error
if (err) {
console.log(err);
res.status(404).json(err);
return;
}
// If a user is found
if(user){
token = user.generateJwt();
res.status(200);
res.json({
"token" : token
});
} else {
console.log(info);
// If user is not found
res.status(401).json(info);
}
})(req, res);
};
Now I have to link this to /login endpoint. So, in "/routes/index.js", I will add the following :
var ctrlAuth = require('./authenticate/authentication');
router.post('/login', ctrlAuth.login);
The "login" will return the JWT in the response. Time to secure rest of the REST apis.
First initialize passport in "app.js"
var passport = require('passport');
require('./routes/authenticate/passport');
app.use(passport.initialize());
Then, in "/routes/index.js", initialize JWT module.
var jwt = require('express-jwt');
var auth = jwt({
secret: 'MY_SECRET',
userProperty: 'payload'
});
Again, don’t keep the secret in the code! Even though I did :P
To apply this middleware simply reference the function in the middle of the route to be protected, like this:
router.get('/api/getUser', auth, function(req, res, next)...
So, the endpoints which were defined earlier as :
router.<method>('/endpoint', function(req, res, next) {})...
Will be now :
router.<method>('/endpoint', auth, function(req, res, next) {})...
Also, add a "unauthorized" error handler, to validate our implementation
app.use(function(err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401);
res.json({"message" : err.name + ": " + err.message});
} else {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
}
});
All done. You should run the application and check the endpoint "/api/catalogue". It has all the details on how to invoke all the apis.
Ideally :
1. Invoke /addUser : to create a new user.
2. Invoke /login : to generate the JWT token.
3. Invoke other APIs using that JWT token.
Happy coding!!
References :
Hey, here's a nice article to do this think without using passport.js https://medium.com/@spyna/how-really-protect-your-rest-api-after-social-login-with-node-js-3617c336ebed
ReplyDeletehow to do this authentication with angular as front end?
ReplyDeleteI am a regular reader of your blog and being students it is great to read that your responsibilities have not prevented you from continuing your study and other activities. Love
ReplyDeletedevops online training
aws online training
data science with python online training
data science online training
rpa online training
I truly like how your class timings of your online journal. I delighted in perusing your online journal and it is both instructional and intriguing.
ReplyDeleteBest Mobile App Development Company
Web App Development Company
Blockchain Development
This is very great thinks. It was very comprehensive post and powerful concept. Thanks for your sharing with us. Keep it up..
ReplyDeleteOracle Training in Chennai | Oracle Training Institutes in Chennai
Hey, would you mind if I share your blog with my twitter group? There’s a lot of folks that I think would enjoy your content. Please let me know. Thank you.
ReplyDeleteJava Training in Chennai | J2EE Training in Chennai | Advanced Java Training in Chennai | Core Java Training in Chennai | Java Training institute in Chennai
Many thanks to you for offering the learned blog to us. I trust that you will post a lot more sites with us. We give passport login best administrations in the entire world as a diplomatic visa available to be purchased.
ReplyDeleteGreat learning resource with segregated source codes.
ReplyDeleteJeevitha from Way2Smile - Most trusted Mobile App Development Company in Chennai.
Great Blog .Feeling happy to read this useful post
ReplyDeleteMCSE Training in chennai | MCSE Course in chennai
Rpa training in chennai | RPA training course chennai
Nice blog information I liked it
ReplyDeleteNode JS Online training
Node JS training in Hyderabad
Your management skill is your spokesperson for the company cursos de ti online
ReplyDeleteHere All the information's are Inspiring...This Informative Content about Java are Really Amazing...
ReplyDeleteJava training in chennai | Java training in annanagar | Java training in omr | Java training in porur | Java training in tambaram | Java training in velachery
The blog was absolutely fantastic! Lot of great information which can be helpful in some or the other way. putlocker
ReplyDeletePretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts.
ReplyDeleteAWS training in Chennai
AWS Online Training in Chennai
AWS training in Bangalore
AWS training in Hyderabad
AWS training in Coimbatore
AWS training
AWS online training
This is very great thinks. It was very comprehensive post and powerful concept. Thanks for your sharing with us. Keep it up..
ReplyDeleteDigital Marketing Training in Chennai
Digital Marketing Training in Velachery
Digital Marketing Training in Tambaram
Digital Marketing Training in Porur
Digital Marketing Training in Omr
Digital MarketingTraining in Annanagar
Thanks for sharing useful information.. we have learned so much information from your blog..... keep sharing
ReplyDeleteDevOps Training in Chennai
DevOps Course in Chennai
we have provide the best ppc service.
ReplyDeletePiano Tiles 2 Mod
A very well detailed post on passports
ReplyDelete. I like your effort. Please keep posting these types of post
Nice article. Have learned a lot from you, a wealth of experience. What do you think is the best form of on a very small budget.
ReplyDeleteWeb Design Company in Chennai
App development company in chennai
mobile app developers in chennai
website development company in chennai
website Re-design in chennai