Codementor Events

Authentication and Authorization in Node JS

Published Aug 14, 2020Last updated Feb 09, 2021
Authentication and Authorization in Node JS

User authentication & authorization is one of the important part of any web application. There are several kinds of way to handle authentication, we can rely on third party service like Passport. But in this article we will use very simple & self developed approach, which will help us to understand core part of authentication.

Technology Used

  • Database: MySQL
  • Password Hash: bcryptjs
  • Token: JWT

Basic Structure

To create basic structure for different kind of endpoints like registration or login we will use express as router. And we will create a folder for routers.

In routes folder we will create a file auth.js for authentication related routes.
routes/auth.js

const router = require('express').Router();
const userController = require('../controllers/user.controller');

// Register a new User
router.post('/register', userController.register);

// Login
router.post('/login', userController.login);

module.exports = router;

index.js

const express = require('express');
const app = express();

// Import Routes
const authRoute = require('./routes/auth');

// Route Middlewares
app.use('/api/users', authRoute);

app.listen(3005, () => console.log('Server is running'));

Registration / SignUp

We have already defined a route for register. Now we will create a controllers folder for controllers & models folder for models. This structures are not required, you may have different structure or you can do everything in a single js file.

So in controller register part should be look like this
controllers/user.controller.js

// Register a new User
exports.register = async (req, res) => {
    
    //Hash password
    const salt = await bcrypt.genSalt(10);
    const hasPassword = await bcrypt.hash(req.body.password, salt);

    // Create an user object
    const user = new User({
        mobile: req.body.mobile,
        email: req.body.email,
        name: req.body.name,
        password: hasPassword,
        status: req.body.status || 1
    });
    // Save User in the database
    try {
        const id = await User.create(user);
        user.id = id;
        delete user.password;
        res.send(user);
    }
    catch (err){
        res.status(500).send(err);
    }    
};

Here we have used bcryptjs package to hash the password. And used mysql as database.

models/user.model.js

User.create = async (newUser) => {
    let insert = await sql.query("INSERT INTO user SET ?", newUser);
    if( insert.insertId ) {
        return insert.insertId;
    }
    else {
        return;
    }
};

Login

Here we have to check user email or mobile against the provided password. If provided info is correct, then we have to generate a token & return to the client. So initially we will check for email or mobile in our database, if user exist, then will call bcrypt.compare(), function of bcryptjs. Then we will call jsonwebtoken for token & send back to client.

Now we will see implementation.

controllers/user.controller.js

// Login
exports.login = async (req, res) => {
    try {
        // Check user exist
        const user = await User.login(req.body.mobile_or_email);
        if (user) {
            const validPass = await bcrypt.compare(req.body.password, user.password);
            if (!validPass) return res.status(400).send("Mobile/Email or Password is wrong");

            // Create and assign token
            const token = jwt.sign({id: user.id, user_type_id: user.user_type_id}, config.TOKEN_SECRET);
            res.header("auth-token", token).send({"token": token});
            // res.send("Logged IN");
        }
    }
    catch (err) {
        if( err instanceof NotFoundError ) {
            res.status(401).send(`Mobile/Email or Password is wrong`);
        }
        else {
            let error_data = {
                entity: 'User',
                model_obj: {param: req.params, body: req.body},
                error_obj: err,
                error_msg: err.message
            };
            res.status(500).send("Error retrieving User");
        }
    }   
    
};

models/user.model.js

User.login = async (value) => {
    let row = await sql.query(`SELECT * FROM user WHERE mobile = ? OR email = ?`, [value, value]);
    if( row.length ) {
        return row[0];
    }
    else {
        throw new NotFoundError("User does not exist");
    }
};

Authentication & Authorization

We often used interchangeably, authentication and authorization, but those words represent fundamentally different functions.

In simple terms, authentication is the process of verifying who a user is, while authorization is the process of verifying what they have access to.

Initially we will just check token in the header of request for restricted routes, then allow or deny request. Then we will check logged in user's permitted routes to access.

To keep it simple, we are keeping a field user_type_id & considering the value
1 = Admin
2 = Customer/Normal user.
Now creating a middle-ware to do this job.
helpers/auth.middleware.js

const config = require("../config/config");
const jwt = require("jsonwebtoken");

exports.loggedIn = function (req, res, next) {
    let token = req.header('Authorization');
    if (!token) return res.status(401).send("Access Denied");

    try {
        if (token.startsWith('Bearer ')) {
            // Remove Bearer from string
            token = token.slice(7, token.length).trimLeft();
        }
        const verified = jwt.verify(token, config.TOKEN_SECRET); 
        if( verified.user_type_id === 2 ){ // Check authorization, 2 = Customer, 1 = Admin
            let req_url = req.baseUrl+req.route.path;
            if(req_url.includes("users/:id") && parseInt(req.params.id) !== verified.id){
                return res.status(401).send("Unauthorized!");
            }
        }
        req.user = verified;
        next();
    }
    catch (err) {
        res.status(400).send("Invalid Token");
    }
}

exports.adminOnly = async function (req, res, next) {
    if( req.user.user_type_id === 2 ){
        return res.status(401).send("Access Denied");
    }  
    next();
}

Full Source code

https://github.com/manashcse11/express/tree/master/auth

Discover and read more posts from Manash Kumar Chakrobortty
get started
post comments3Replies
Танк Перс
4 years ago

And how to correctly add the html page with registration here, please show

estewui
4 years ago

Really helpful. Thank you very much!

Orozco
4 years ago

This article will help you grasp different concepts behind Node.js and will empower you to create production ready applications. This article expects the reader to know Babel and how to set it up.