How to Configure Passport Authentication in a Postgres Node and Application

As a developer, it is your responsibility to protect your users’ data through authentication. You can use Passport.js to authenticate users in a Node and Postgres application.


Start by creating a Node server with endpoints for registering, logging in, and logging out users. You can let Passport handle authentication to restrict unauthorized access to your app.


Creating a user table

For user authentication, you will use an email and password. This means that the users table must contain an email and a password field. In the psql command prompt, create a new database called nodeapp:

CREATE DATABASE nodeapp;

Next, create a table to store the users:

CREATE TABLE users (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email CHAR(128),
password CHAR(60)
);

This code will create a new table containing the email address, password, and an auto-generated login field.

Creating a Node Server

Node.js is a server-side JavaScript runtime environment that allows us to quickly build HTTP servers. To simplify the process of creating the server and the various HTTP routes, you can use Express, a Node.js web framework.

Run this command to create a new folder called postgres-auth:

mkdir postgres-auth

Next, initialize npm:

npm init -y

Finally, install Express:

npm install express

You can now create the Node web server.

In a new file named index.jsadd the following:

const express = require("express");
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.listen(3000, () => console.log("Listening on port 3000"));

Running this code will start the server and log the following to the console:

Listening on port 3000

Connection to PostgreSQL

To connect to PostgreSQL use node-postgres. node-postgres is a login driver that provides an interface between Node and Postgres.

Run the following command to install node-postrges via npm:

npm install pg

Once you have installed this library, create a new file called db.js and connect it to the database:

const { Client } = require("pg");
const { user, host, database, password, port } = require("./dbConfig");

const client = new Client({
user,
host,
database,
password,
port,
});

client.connect();
module.exports = client;

The node-postgres client method takes the details of the database you are connecting to. This program imports its login details from a file called dbConfig. Therefore, create this file and add the following code to it:

module.exports = {
user: "postgres",
host: "localhost",
database: "nodeapp",
password: "yourPassword",
port: 5432,
};

Create database helper functions

It is always recommended to use individual functions to interact with the database. They make it easier to write unit tests and improve reuse. For the registration endpoint, you need to create two functions:

  1. To check if the email is already registered.
  2. To create the user.

The goal is to register a user only if it does not exist in the database.

Create a new file called helper.js and import the database client from db.js:

const client = require("./db.js")

Next, add a new function called emailExists():

const emailExists = async (email) => {
const data = await client.query("SELECT * FROM users WHERE email=$1", [
email,
]);

if (data.rowCount == 0) return false;
return data.rows[0];
};

This function takes an email and checks if it is already in use. It does this by using the SELECT clause which returns a row containing an email field that matches the value provided by the registering user. If the email does not exist, it returns false.

To create a function that creates the user, add a function called createUser() to helper.js:

const createUser = async (email, password) => {
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(password, salt);

const data = await client.query(
"INSERT INTO users(email, password) VALUES ($1, $2) RETURNING id, email, password",
[email, hash]
);

if (data.rowCount == 0) return false;
return data.rows[0];
};

This function takes the values ​​of email and password. It uses the INSERT clause to create a new row with these details and if successful returns the newly created user. Note that before storing the password, you must hash it using bcrypt. It’s never a good idea to store passwords as plain text. If hackers had access to your user database, they could easily gain access to sensitive information.

Install bcryptjs to start using it:

npm install bcryptjs

In helper.js, import bcryptjs:

const bcrypt = require("bcryptjs")

By using Bcryptjs, the database only stores the encrypted password. Therefore, when logging in, you will need to compare the plaintext password given by the user and the hashed password in the database. For this, you can use the comparison method provided by Bcryptjs.

Create a function called matchPassword():

const matchPassword = async (password, hashPassword) => {
const match = await bcrypt.compare(password, hashPassword);
return match
};

It receives the cleartext password and the hash, then uses Bcrypt.compare() to determine if the supplied password is correct. If so, it returns true otherwise, it returns false.

These are all the functions we will use to interact with the database. Be sure to export them all at the end:

module.exports = { emailExists, createUser, matchPassword };

Configure Passport

Passport is a Node authentication middleware that provides over 500 authentication strategies such as social login, JSON Web Tokens (JWT), and email authentication. We will use the latter which the passport-local strategy provides.

Use the following command to install passport and passport-local:

npm install passport
npm install passport-local

Next, configure Passport to connect existing users and register new users.

Start by creating a new file passportConfig.js. Next, import the local Passport policy and database helper functions you just created:

const LocalStrategy = require("passport-local");
const { emailExists, createUser, matchPassword } = require("./helper");

In the same file, add the following to configure user registration:

module.exports = (passport) => {
passport.use(
"local-signup",
new LocalStrategy(
{
usernameField: "email",
passwordField: "password",
},
async (email, password, done) => {
try {
const userExists = await emailExists(email)

if (userExists) {
return done(null, false);
}

const user = await createUser(email, password);
return done(null, user);
} catch (error) {
done(error);
}
}
)
);
}

Since passport-local expects a username and password, and you are using email, set the username field to email. The user or rather the front end of this app will send the email and password in the request body. However, you don’t need to extract the values ​​yourself as Passport will take care of that in the background.

This program first checks if the email is already taken using the emailExists() function of helper.js. If the email does not exist in the database, it creates a new user with the createUser() function. Finally, it returns the user object.

To connect users, add the following to passportConfig.js:

module.exports = (passport) => {
passport.use(
"local-signup",
new LocalStrategy(
)
);
passport.use(
"local-login",
new LocalStrategy(
{
usernameField: "email",
passwordField: "password",
},
async (email, password, done) => {
try {
const user = await emailExists(email);
if (!user) return done(null, false);
const isMatch = await matchPassword(password, user.password);
if (!isMatch) return done(null, false);
return done(null, {id: user.id, email: user.email});
} catch (error) {
return done(error, false);
}
}
)
);
};

Here the program first checks if the email is registered. Otherwise, it returns false. If it finds the e-mail, it compares its password with that of the request. If the passwords match, it logs the user in and returns the user object.

The last step is to create the API endpoints:

  • POST /auth/registration
  • POST /auth/login

Both of these endpoints will receive an email and password in the request body. They will also include the passport authentication middleware functions we just configured.

Import and configure Passport in a new file named server.js:

const passport = require("passport");
require("./passportConfig")(passport);

Next, add the following routes:

app.post(
"/auth/signup",
passport.authenticate("local-signup", { session: false }),
(req, res, next) => {
res.json({
user: req.user,
});
}
);
app.post(
"/auth/login",
passport.authenticate("local-login", { session: false }),
(req, res, next) => {
res.json({ user: req.user });
}
);

Both of these routes return a JSON object containing the user if successful.

Verify your API using unit tests

You can use Passport to authenticate a Node application using a PostgreSQL application. You have created API endpoints to register and log in users.

Although you can use REST clients like Postman to test whether an API is working, writing unit tests is much easier. Unit tests allow you to test different parts of your application. This way, even if an endpoint fails, you can identify the exact point of failure. One of the tools you can use to test Node apps is Jest.


Source link

Comments are closed.