As part of my She Codes Africa Mentoring Program (SCAMP) assessment, I was asked to build an ecommerce application with Node.js.
Now, an ecommerce application doesn't only deal with CRUD (Create, Read, Update, Delete) operations, it has to handle validations. Imagine that a user tries to sign into their account, how would you know it is really them that's accessing their accounts and not an impersonator?
Since users have logged in and added some items to their carts, how do you make sure the system recognizes each user when they log in hence displaying their account to them?
There are numerous things a developer has to put in place while developing an ecommerce application. But today, we would be looking at tokens. What are json web tokens? Why do we need them? How do we manage json web tokens? These are all the questions this article will explore.
What Are Json Web Tokens
Json web token (JWT) is an open standard that defines how information is sent on the web. This way, information is sent with a verified signature that can be encrypted if the information being sent is sensitive.
Why JWT
You could use other open standard to securely send information in your web applications like SWT (Simple Web Tokens) and SAML (Security Assertion Markup Language) tokens.
But, JWT is less verbose than XML (Extension Markup Language) therefore when JWT is encoded it is smaller than SAML tokens. This makes JWT the best choice when embedding tokens into your web applications as it won't cause a delay in response time.
At this point, I know you are beginning to wonder why you need a token in the first place. With JWT in your application you can easily implement authentication. All you need to do is to sign and verify user's payload data. Then, an identity token will be returned with the user's information. This is very useful for ecommerce web applications because the username of the user will be returned, to help fetch all they have in their cart.
Also, you can implement authorization with JWT. For cases like an ecommerce site, you can restrict users who don't have account or are not logged in from accessing the add to cart route. This is useful if you don't plan to create visitor buyers function into your application.
In some ecommerce applications, there may be need for a buyer to communicate to a seller. Developers creating these kind of applications need to be careful so that information isn't compromised by e-criminals. This is where JWT comes in, to ensure information is not tampered with. As, they are signed and have to be verified.
Since we have explored why we need JWT in our Node.js application, let's dive into the steps to add JWT to our application.
How to Create Tokens in Node.js application with JWT
In this case, I used JWT to sign users payload data. So, in this section, we would learn how to sign users payload data with JWT.
First, we would need to install this module and bcrypt. Bcrypt will hash and verify user's password while JWT will sign and create a token.
npm i bcrypt
npm i jsonwebtoken
Next, in our user controller file where user authentication and authorization logic will be, add the following codes
// Import modules
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const cors = require('cors');
var users = express.Router();
// Import user model
var { User } = require('../models/User');
users.use(cors())
// Post method to allow new users register
users.post('/register', (req, res) => {
// Add date a user registers to the database
const today = new Date()
// Create user data
const userData = {
first_name: req.body.first_name,
last_name: req.body.last_name,
email: req.body.email,
password: req.body.password,
created: today,
username: req.body.username,
}
// Compare user's email across database to ensure it hasn't already been registered
User.findOne({
email: req.body.email
})
.then(user => {
if (!user) {
// Hash user's password with bcrypt bcrypt.hash(req.body.password, 10, (err, hash) => {
userData.password = hash
// Register user
User.create(userData)
.then(user => {
res.json({ status: user.email + ' registered' })
})
.catch(err => {
res.send('error:' + err)
})
})
} else {
// If email has already been registered, throw error that address has been registered
res.json({ error: 'The email address ' + req.body.email + ' is registered with an account' })
}
})
.catch(err => {
res.send('error:' + err)
})
})
// Post method to allow users login
users.post('/login', (req, res) => {
// Find email address in database to ensure the email is registered with the system.
User.findOne({
email: req.body.email
})
.then(user => {
if (user) {
if
// Use bcrypt to compare user password to confirm it is correct. (bcrypt.compareSync(req.body.password, user.password)) {
const payload = {
_id: user._id,
first_name: user.first_name,
last_name: user.last_name,
email: user.email,
username: user.username
}
// Create token to sign user's payload data
let token = jwt.sign(payload, {
expiresIn: 1440
})
// When a user tries to login, send the token and payload data
res.send({ token, payload })
} else {
// If there password and email don't match, throw an error
res.json({ error: 'your email and password combination is wrong' })
}
} else {
res.json({ error: 'your email and password combination is wrong' })
}
})
.catch(err => {
res.send('error:' + err)
})
})
module.exports = users;
Now, if we start our application and try to register and login a user with API endpoint testers like postman, everything works fine. Bcrypt checks for password and we get a token like the image below after successful login.
Restricting Routes With JWT
Aside authentication, JWT is perfect for authorization. What this means is that you can restrict users from accessing some routes.
What we can do is restrict users from accessing some routes. For instance, we can say that users who are not logged in, cannot access the add to cart route.
To do this, we need to create an instance that will add access header to the token of all logged in users. If a user's token doesn't have the access header attached to it, they can't view the cart route.
// Import VerifyToken.js file
var VerifyToken = require('./VerifyToken');
// Get method to retrieve cart
router.get('/cart', 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);
});
});
Create a new file and name it VerifyToken.js
which we have already referenced in our cart controller. This file will validate the access token provided, to ensure it is correct.
// Import JWT module
var jwt = require('jsonwebtoken');
// Method to verify access token provided by user
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, 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();
});
}
If you try to navigate to the cart route after successful login, you will get the message "No token provided". To access the cart route freely, add access header to postman before login as shown in the image below
Conclusion
In this article, we have seen why we need tokens in our web applications. Also, we have explored why we should use JWT and how to create tokens with JWT in our Node.js application. We have looked at authentication and authorization with JWT. You can as well explore security with JWT by adding a secret key. Thank you so much for reading this article to the end.