DEV Community

Mudacumura Brunoblaise
Mudacumura Brunoblaise

Posted on • Updated on

How to Authenticate and authorize in nodejs

Have you ever wanted to authenticate and authorize your users on your website? and you don't know-how. this can affect your web app as everyone is concerned about their privacy. There are several ways to handle authentication, we can rely on third-party services like Passport. But in this article, we will use a very simple & self-developed approach, which will help us to understand the core part of the authentication.

Requirements

  • Nodejs

  • database: PostgreSQL or other you know

Technology Used

  • Database: PostgreSQL

  • Password Hash: bcrypt

  • Token: JWT(jsonwebtoken)

  • Multer for uploading photos

  • Cloudinary for storing user photo

*npm(node package manager) to install
*

npm i bcrypt  cloudinary jsonwebtoken multer pg
Enter fullscreen mode Exit fullscreen mode

Basic Structure

First, make your basic express server.

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


const PORT = process.env.PORT || 5000


app.use(express.json());

 router.get("/",   async (req, res) => {
   res.send("hello")
  });


app.listen(PORT, ()=>{
  console.log(`server started ${PORT}`)
})
Enter fullscreen mode Exit fullscreen mode

Registration / Signup

First, make a file named register.js in your directory it should look like this.

const express = require("express");
const router = express.Router();
const bcrypt = require("bcrypt");
const pool = require("./db"); // database connection
const jwtGenerator = require("./jwtGenerator"); 
// make a file named multer.js in your directory
const upload = require('./multer') 
// make a file named cloudinary to keep your credentials 
const cloudinary = require('./cloudinary'); 


router.post("/register",upload, async (req, res) => {


  try {
    const { email, name, password } = req.body;


     const result = await cloudinary.uploader.upload(req.file.path);


     const photo =result.url
// it checks if the email is used or not
    const user = await pool.query("SELECT * FROM users WHERE user_email = $1", [
      email
    ]);

    if (user.rows.length > 0) {
      return res.status(401).json("User already exist!");
    }

    const salt = await bcrypt.genSalt(10);
    const bcryptPassword = await bcrypt.hash(password, salt);

    let newUser = await pool.query(
      "INSERT INTO users (user_name,user_photo, user_email, user_password) VALUES ($1, $2, $3,$4, $5) RETURNING *",
      [name, photo,  email, bcryptPassword]
    );

    const jwtToken = jwtGenerator(newUser.rows[0].user_id);

    return res.status(200).json({ jwtToken });
  } catch (err) {
    console.error(err.message);
    res.status(500).send("Server error");
  }
});


module.exports = router;

Enter fullscreen mode Exit fullscreen mode

Login

const express = require("express");
const router = express.Router();
const bcrypt = require("bcrypt");
const pool = require("./db");
const jwtGenerator = require("./jwtGenerator");


router.post("/login", async (req, res) => {
  const { email, password } = req.body;

  try {
    const user = await pool.query("SELECT * FROM users WHERE user_email = $1", [
      email
    ]);

    if (user.rows.length === 0) {
      return res.status(401).json("Invalid Credential");
    }

    const validPassword = await bcrypt.compare(
      password,
      user.rows[0].user_password
    );

    if (!validPassword) {
      return res.status(401).json("Invalid Credential");
    }
    const jwtToken = jwtGenerator(user.rows[0].user_id);
    return res.json({ jwtToken });
  } catch (err) {
    console.error(err.message);
    res.status(500).json("Server error");
  }
});

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

Verify
You can put it anywhere you want for example login.js or residter.js

// the file must be required in order to verify the user
const authorize = require("./authorize");

 router.post("/verify", authorize, (req, res) => {
    try {
      res.json(true);
    } catch (err) {
      console.error(err.message);
      res.status(500).json("Server error");
    }
  });
Enter fullscreen mode Exit fullscreen mode

jwtGenerator file

this file must exist in order to generate the token it should look like this.

const jwt = require("jsonwebtoken");


function jwtGenerator(user_id) {
  const payload = {
    user: {
      id: user_id
    }
  };



  return jwt.sign(payload, yourJwtSecret, { expiresIn: "2h" });
}

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

multer file

const multer = require('multer');
const path = require('path')
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './uploads/')
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + file.originalname)
  }
})

const upload = multer({ 
      storage: storage,
      limits:{fileSize: 100000000000},
      fileFilter: function(req, file, cb){
        checkFileType(file, cb);
      }
}).single('photo')

function checkFileType(file, cb){
  // Allowed ext
  const filetypes = /jpeg|jpg|png|gif/;
  // Check ext
  const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
  // Check mime
  const mimetype = filetypes.test(file.mimetype);

  if(mimetype && extname){
    return cb(null,true);
  } else {
    cb({message: 'Error: Images Only!'}, false);
  }
}

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

cloudinary file

In this file, your will need to register in cloudinary the URL:https://cloudinary.com/users/register/free

After doing so go to dashboard and copy and paste your credentials it will be like this

require('dotenv').config();
const cloudinary = require('cloudinary').v2;
cloudinary.config({
    cloud_name: //CLOUDINARY_NAME,
    api_key: //CLOUDINARY_API_KEY,
    api_secret: //CLOUDINARY_API_SECRET,
});

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

authorize file

const jwt = require("jsonwebtoken");
require('dotenv').config();

//this middleware will on continue on if the token is inside the local storage

module.exports = function(req, res, next) {
  // Get token from header
  const token = req.header("jwt_token");

  // Check if not token
  if (!token) {
    return res.status(403).json({ msg: "authorization denied" });
  }

  // Verify token
  try {
    //it is going to give use the user id (user:{id: user.id})
    const verify = jwt.verify(token, //jwtSecret);

    req.user = verify.user;
    next();
  } catch (err) {
    res.status(401).json({ msg: "Token is not valid" });
  }
}
Enter fullscreen mode Exit fullscreen mode

db file

const Pool = require("pg").Pool;

const pool = new Pool({
  host: "localhost",
  user: "postgres",
  password: "//your password",
  port: 5432,
  database: "//name of your database"
});

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

Now in server or main file should look like this

const express = require("express");
const app = express();
const pool = require("./db");
const authorize = require("./authorize");

const PORT = process.env.PORT || 5000


app.use(express.json());

app.use(express.urlencoded({extended: false}))

app.use('/uploads', express.static('uploads'))

app.use("/", require("./routes/register"));

app.use("/", require("./login"));

 router.get("/",   async (req, res) => {
   res.send("hello")
  });



router.get("/profile", authorize, async (req, res) => {
  try {
   const user = await pool.query(
       "SELECT * FROM user WHERE user_id = $1",
      [req.user.id]
     );



    res.json(user.rows);
  } catch (err) {
    console.error(err.message);
    res.status(500).send("Server error");
  }
});

app.listen(PORT, ()=>{
  console.log(`server started ${PORT}`)
})
Enter fullscreen mode Exit fullscreen mode

Important folder

  • uploads folder(should make it so, that multer can access it)

Additional
for database

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE users(
  user_id UUID DEFAULT uuid_generate_v4(),
  user_photo VARCHAR(255) NOT NULL,
  user_name VARCHAR(255) NOT NULL,
  user_email VARCHAR(255) NOT NULL UNIQUE,
  user_password VARCHAR(255) NOT NULL,
  PRIMARY KEY (user_id)
);
Enter fullscreen mode Exit fullscreen mode

As a conclusion you can take this further, and it was written to help people out there who have no clue or have but have never tried and take this article as personal experience and I am not an expert but there are millions of ways to make this work if you face any problem, please tell me I will be happy to help you. you can contact me on my email: brunoblaise94@gmail.com and if it is scary to you and you do not where to start, please let me know if you need a version of this article in a video and I post here mainly pern (PostgreSQL(database), expressjs(server-side), reactjs(client/frontend), nodejs(backend) staffs and errors during the deployment every two weeks. I have a nice day.

Top comments (0)