In the previous tutorial, i showed how to upload form data with file using Multer & MYSQL in Express. This tutorial teaches you how to do authentication in Express using JWT (JSON Web Token) and MYSQL database.
Why we need authentication? The requirement is to allow APIs access to only authorized users.
A visitor creates a user account using username, email, and password. We are going to use bcrypt package to create an encrypted version of the password and to verify the password againts MYSQL database. Execute the following command to install bcrypt.
npm i bcrypt
Once the account is created successfully, he/she can login to the system. The login information is verified on Express server. If the log in information is correct, valid token will be created and returned to a client app. The client app that received the token need to pass the token to the server to get permission to access the APIs. The token has to be verified before granting permissions. The information in the token can be verified and trusted because it is digitally signed.
In Express, we use jsonwebtoken package to create, sign, and verify a token. Run the command below to install jsonwebtoken.
npm i jsonwebtoken
To create and verify a token, jsonwebtoken needs a secret key. Generally, a secret key is stored .env file. Thus, in the root folder of the Express app, create .env file and add a secret key as shown below.
JWT_KEY="secretOrKeyJWTRandom"
In Express, to read the secret key, we used dotenv package. Let install it.
npm i dotenv
Now we are ready to create routes to handle user registration and login. in the routes folder, create user.routes.js file:
require('dotenv').config();
var router = require('express').Router();
const bcrypt = require('bcrypt');
const KEY = process.env.JWT_KEY;
const jwt = require('jsonwebtoken');
const db = require("../db/models");
// handle user registration
router.post('/createuser', async function(req, res) {
if(req.body){
// Get post data
const { body } = req;
const { username, email, password } = body;
// created encrypted password
let password_encrypted= await bcrypt.hash(password, 10);
// add new user to database
await db.User.create({
username:username,
email:email,
password:password_encrypted,
}).then(newuser =>{ // return success result
return res.status(200).json({
message: 'success',
data: newuser,
});
}).catch(err => { // return err result
res.status(500).send({
message:
err.message || "Some error occurred while inserting new user"
});
});
}
});
// handle user log in
router.post('/auth', async function(req, res) {
// Get post data
const { username, password } = req.body;
/* Any how email or password is blank */
if (!username|| !password) {
return res.json({
message: 'Request missing username or password',
});
}
// Check user in database
await db.User.findOne({
where: { username: username},
attributes: ['id','username', 'email', 'password'],
limit: 1,
}).then(user =>{
if(!user) return res.json({
message: 'Invalid user!',
});
/* Define variables */
const dataUser = user.toJSON();
const userId = dataUser.id,
userEmail = dataUser.email,
userPassword = dataUser.password;
/* Check and compare password */
bcrypt.compare(password, userPassword).then(isMatch => {
if (isMatch) {
// User matched
// Create JWT Payload */
const payload = {
id: userId,
email: userEmail,
};
// Sign token
const token = jwt.sign(payload, KEY, {
expiresIn: '72h'
});
res.json({ message: 'success', token:token });
} else {
res.json({ message: 'Password incorrect' });
}
});
});
});
module.exports = router;
Then add the user.routes.js file to app.js.
....................
app.use('/api', require('./routes/user.routes'));
app.use('/api', require('./routes/product.routes'));
......................
Save the project and run node app.js to start Express server. Try create a new user using http://localhost:5000/api/crreateuser url. Then login to obtain a token using http://localhost:5000/api/auth url.
Protect APIs
To protect APIs or routes, you need a middleware to verify the submitted token. If the verification is successful, next handler method will be executed. Otherwise "unauthorized" error message will be returned to the client.
In the root folder of the project, create utils folder and add verifyToken.js file to the folder.
utils/verifyToken.js
require('dotenv').config();
const jwt = require('jsonwebtoken');
const KEY = process.env.JWT_KEY;
const verifyToken = function(req, res, next) {
const bearerHeader = req.headers['authorization'];
if (!bearerHeader) {
res.status(401).send('Unauthorized: No token provided');
} else {
let token = bearerHeader.split(' ')[1];
jwt.verify(token, KEY, function(err, decoded) {
if (err) {
res.status(401).send('Unauthorized: Invalid token');
} else {
req.email = decoded.username;
next();
}
});
}
}
module.exports = verifyToken;
To protect an API, you need to add the verifyToken middleware function to the handler method of the route. For example, to project routes to list products, add, and update product, update the product.routes.js file to include verifyToken middleware:
...................
const verifyToken = require('../utils/verifyToken');
router.get("/products", verifyToken, function(req,res){.....}
router.post("/products", verifyToken, function(req,res){......}
router.put("/products", verifyToken, function(req,res){.....}
Comments
Post a Comment