DEV Community

Cover image for NodeJS + Postgres DB + Passport JWT + Passport Local Login and Authentication
Harish Soni
Harish Soni

Posted on • Edited on

NodeJS + Postgres DB + Passport JWT + Passport Local Login and Authentication

Hi,

Here I am writing my first blog πŸ˜‡ about how do we connect a simple [NodeJS Application] 😎(https://www.npmjs.com/package/express) to Postgres and use PassportJS for authentication and authorization.

So the basic machine setup would look like this:

Node JS - v12 or above
pgAdmin for DB Connectivity
npm for creating and installing the dependencies

Step 1:

Let's create a simple npm module using npm init in the directory you want to create your application.

> npm init

It will ask you the below questions for the configuration of the

Image description

You can hit enter for every question or you can add your own configs, I am using the default ones.

Step 2:
Now we need the dependencies which needs to be installed for our application to use while we compile the code:

Here is how my package.json looks like:

Image description

Here is the command you can run to install the dependencies:

npm i --save bcrypt-nodejs cors express jsonwebtoken nodemon passport passport-jwt passport-local pg pg-hstore sequelize

Let's catch all the dependencies and their work for us:

  • bcrypt-nodejs: It will help us to encrypt and decrypt the password when we create a new user.

  • cors: To allow CROSS ORIGIN REQUESTS install if you want or need.

  • express: It will create a server for us to use it's routes.

  • jsonwebtoken: To create the JWT Token for the API Authorizations.

  • passport: For easy authentication of the User.

  • passport-jwt: For JWT Authorization.

  • passport-local: For LocalStrategy of the Login Authentication

  • pg pg-hstore sequelize: For Accessing the Postgres DB

Step 3:

Let's create a simple server to kick start our project:

here is what I have in my index.js file:

// project/index.js 


const express = require('express')
const db = require('./models')
var cors = require('cors')
const app = express()
const port = 3000
app.use(express.json());
app.use(cors())

db.sequelize.sync().then(() => {
    console.log("Synced")
}).catch(err => console.err(err))

app.get('/', (req, res) => {
    res.send('Hello World!')
})

require('./routes/user.route')(app)

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})
Enter fullscreen mode Exit fullscreen mode

What this statement does is:

db.sequelize.sync().then(() => {
    console.log("Synced")
}).catch(err => console.err(err))
Enter fullscreen mode Exit fullscreen mode

to check if the Postgres DB has been connected and is sequelizing it.

And then the routes which we are going to create on the very next step.

And our server start:

Comment out this line

require('./routes/user.route')(app)

and run npm run dev and see if the application is Synced and is running on Port 3000

if it shows below:

Image description

YAYYYY....!!! You have created a express server now.

Step 4:

The fun part begin here:

  • Let's create the routes
// project/routes/user.route.js


module.exports = app => {

    // Import of the controller
    const user = require('../controller/user.controller')

    // Creating the router instance
    const router = require('express').Router();

    // TO create the user
    router.post('/user', user.create)

    // To Login the user using Passport Local Strategy
    router.post('/user-passport-login', user.loginWithPassport)

    // Pass the router instance to the App.
    app.use('/api/v1', router)
}
Enter fullscreen mode Exit fullscreen mode

Each route has it's own defination, let's create our very first controller now:

// project/controller/user.controller.js

const db = require("../models");
const User = db.user;
const passportLocal = require('../config/passportLocal')

// To create a new user in the DB

function create(req, res) {
    const userdata = {
        username: req.body.username,
        password: req.body.password
    }
    User.create(userdata).then(data => {
        return res.send(data)
    }).catch(err => {
        console.warn(err)
    })
}


// To Login the user using Passport

async function loginWithPassport(req, res) {
    return await passportLocal.authenticate('local', function (err, response) {
        if (response) {
            return res.send({
                msg: "Login Success",
            })
        }
        if (!response) {
            return res.send({
                msg: "Failed"
            })
        }
    })(req, res)
}

Enter fullscreen mode Exit fullscreen mode

wait wait...!!

why this line:

    })(req, res)
Enter fullscreen mode Exit fullscreen mode

The loginWithPassport is a self calling function which would have the req and res as the parameters, since we need to return the calculated response from the controller to the API, we also need the request params.

Step 5:

Let's create our Models now:

// project/models/user.model.js

var bcrypt = require('bcrypt-nodejs');

module.exports = (sequelize, DataTypes) => {

    // To get the feasiblity of the Sequelize ORM
    const User = sequelize.define("user", {
        username: {
            type: DataTypes.STRING,
            primaryKey: true
        },
        password: {
            type: DataTypes.STRING
        },
    });

    // It will convert each password into the Hashed String for maintaining the security
    User.beforeSave((user) => {
        if (user.changed('password')) {
            user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync(10), null)
        }
    })

    // It will compare the password to the passed string using the bcrypt algo, and will return the result
    User.prototype.comparePassowrd = function (pass, cb) {
        bcrypt.compare(pass, this.password, function (err, isMatch) {
            if (err) {
                return cb(err)
            }
            cb(null, isMatch)
        })
    }
    return User;
};
Enter fullscreen mode Exit fullscreen mode

We have created the Model but right now it is not being used it is just a kind of Schema, now let's do the tricky part, let's create a DB Table in the pgAdmin using the below code:

// project/models/index.js

const dbConfig = require('../db.config')
const Sequelize = require('sequelize')

const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
    host: dbConfig.HOST,
    operatorAliases: false,
    dialect: dbConfig.dialect,
    pool: dbConfig.pool
})

const db = {}

db.Sequelize = Sequelize
db.sequelize = sequelize
db.user = require('./user.model.js')(sequelize, Sequelize)

module.exports = db;
Enter fullscreen mode Exit fullscreen mode

dbConfig.js

// project/dbConfig.js

module.exports = {
    HOST: "localhost",
    USER: "harishsoni",
    PASSWORD: "admin",
    DB: "testDB",
    dialect: "postgres",
    pool: {
        max: 5,
        min: 0,
        acquire: 30000,
        idle: 10000
    }
}
Enter fullscreen mode Exit fullscreen mode

Here is the line from the index.js, which is using the above Sequelization and Syncing the DB:

const db = require('./models')
Enter fullscreen mode Exit fullscreen mode

Step 3:

Now the final Part let's create the passportLocal.js file which will contain the main business logic to check the use authentication.

// project/config/passportLocal.js

const passport = require('passport')
const LocalStratery = require('passport-local').Strategy
const db = require('../models')

// Creating the passport instance to be used from the controller.

passport.use(new LocalStratery({

    // if you use any different name for the username field, you can pass the key here
    usernameField: 'username'
}, async function (username, password, done) {
    // findByPk is a Sequelize function which returns the data if it finds the primary key with the value passed
    return await db.user.findByPk(username).then(async data => {

        // Check if the user is there in the DB:
        if (!data) {
            return done(null, null)
        }

        // If the user is correct, then let's see if he has entered the correct password.
        await data.comparePassowrd(password, (err, userData) => {
            return done(null, userData)
        })
    }).catch(err => { throw err })
}))


// For Storing the user id in the session {req.session.passport.user = {id: '..'}}
passport.serializeUser(function (user, cb) {
    cb(null, user)
})

// For checking if the user has an active session.
passport.deserializeUser(function (obj, cb) {
    cb(null, obj)
})

module.exports = passport
Enter fullscreen mode Exit fullscreen mode

Here is how the passport configuration will look to login the user

So combining all we will have something like this:

project
β”‚   index.js
β”‚   db.config.js
β”‚   package.json
β”‚
└───models
β”‚    user.model.js
β”‚    index.js
β”‚   
└───config
β”‚    passportLocal.js
β”‚   
└───controller
β”‚    user.controller.js
β”‚   
└───routes
      user.route.js


Enter fullscreen mode Exit fullscreen mode

πŸ₯³πŸ₯³πŸ₯³ We are done with the setup now the time to run and see if the code works (If works god knows how, and if not we need to know why πŸ˜‚ πŸ˜‚ πŸ˜‚ πŸ˜‚ πŸ˜‚)

🀞🀞 Here we gooooooo.....!!!!

Image description

🧐🧐🧐 It Worked::::::::::::

Let's check the API's NOW:

🀞🀞🀞🀞🀞🀞🀞🀞🀞🀞

Image description

Yeah, it worked: πŸ˜ŽπŸ˜ŽπŸ˜‡πŸ˜‡

Any suggestion are welcome:
Enter fullscreen mode Exit fullscreen mode

Repo Link: https://github.com/harish9312/passport-auth-node

Top comments (0)