Securing Node.js RESTful APIs with JSON Web Tokens

Let’s start writing some code, shall we?

git clone https://github.com/adnanrahic/nodejs-restful-api.git
> user
- User.js
- UserController.js
- db.js
- server.js
- app.js
- package.json
var mongoose = require('mongoose');
mongoose.connect('mongodb://wally:theflashisawesome@ds147072.mlab.com:47072/securing-rest-apis-with-jwt', { useMongoClient: true });

Finally, some code.

// config.js
module.exports = {
'secret': 'supersecret'
};
// AuthController.jsvar express = require('express');
var router = express.Router();
var bodyParser = require('body-parser');
router.use(bodyParser.urlencoded({ extended: false }));
router.use(bodyParser.json());
var User = require('../user/User');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
var config = require('../config');
npm install jsonwebtoken --save
npm install bcryptjs --save
router.post('/register', function(req, res) {

var hashedPassword = bcrypt.hashSync(req.body.password, 8);

User.create({
name : req.body.name,
email : req.body.email,
password : hashedPassword
},
function (err, user) {
if (err) return res.status(500).send("There was a problem registering the user.") // create a token
var token = jwt.sign({ id: user._id }, config.secret, {
expiresIn: 86400 // expires in 24 hours
}); res.status(200).send({ auth: true, token: token });
});
});
router.get('/me', function(req, res) {  var token = req.headers['x-access-token'];
if (!token) return res.status(401).send({ auth: false, message: 'No token provided.' });

jwt.verify(token, config.secret, function(err, decoded) {
if (err) return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });

res.status(200).send(decoded);
});
});
// add this to the bottom of AuthController.js
module.exports = router;
// app.js
var AuthController = require('./auth/AuthController');
app.use('/api/auth', AuthController);module.exports = app;

Let’s test this out. Why not?

// in AuthController.js change this line
res.status(200).send(decoded);// to
User.findById(decoded.id, function (err, user) {
if (err) return res.status(500).send("There was a problem finding the user.");
if (!user) return res.status(404).send("No user found.");

res.status(200).send(user);
});
User.findById(decoded.id, 
{ password: 0 }, // projection
function (err, user) {
if (err) return res.status(500).send("There was a problem finding the user.");
if (!user) return res.status(404).send("No user found."); res.status(200).send(user);
});

Did someone say login?

router.post('/login', function(req, res) {  User.findOne({ email: req.body.email }, function (err, user) {
if (err) return res.status(500).send('Error on the server.');
if (!user) return res.status(404).send('No user found.'); var passwordIsValid = bcrypt.compareSync(req.body.password, user.password);
if (!passwordIsValid) return res.status(401).send({ auth: false, token: null }); var token = jwt.sign({ id: user._id }, config.secret, {
expiresIn: 86400 // expires in 24 hours
}); res.status(200).send({ auth: true, token: token });
});});
// AuthController.js
router.get('/logout', function(req, res) {
res.status(200).send({ auth: false, token: null });
});

Do you have permission to be here?

router.get('/me', function(req, res, next) {  var token = req.headers['x-access-token'];
if (!token) return res.status(401).send({ auth: false, message: 'No token provided.' });

jwt.verify(token, config.secret, function(err, decoded) {
if (err) return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });

User.findById(decoded.id,
{ password: 0 }, // projection
function (err, user) {
if (err) return res.status(500).send("There was a problem finding the user.");
if (!user) return res.status(404).send("No user found."); // res.status(200).send(user); Comment this out!
next(user); // add this line
});
});
});// add the middleware function
router.use(function (user, req, res, next) {
res.status(200).send(user);
});
var jwt = require('jsonwebtoken');
var config = require('../config');function verifyToken(req, res, next) {
var token = req.headers['x-access-token'];
if (!token)
return res.status(403).send({ auth: false, message: 'No token provided.' }); jwt.verify(token, config.secret, function(err, decoded) {
if (err)
return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' }); // if everything good, save to request for use in other routes
req.userId = decoded.id;
next();
});
}module.exports = verifyToken;
// AuthController.jsvar VerifyToken = require('./VerifyToken');// ...router.get('/me', VerifyToken, function(req, res, next) {  User.findById(req.userId, { password: 0 }, function (err, user) {
if (err) return res.status(500).send("There was a problem finding the user.");
if (!user) return res.status(404).send("No user found.");

res.status(200).send(user);
});});// ...

Wrapping your head around everything.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store