How to Set Up Passport Authentication in a Node and Postgres Application

As a developer, it is your responsibility to safeguard 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 to register, sign in, and sign out users. You can let Passport handle authentication to restrict unauthorized access to your application.

Creating a Users Table

For user authentication, you will use an email and a password. This means 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 email, password, and an auto-generated ID field.

Creating a Node Server

Node.js is a server-side JavaScript runtime environment that allows us to create HTTP servers quickly. To simplify the process of creating the server and different 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 called index.js, add 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 in the console:

 Listening on port 3000 

Connecting to PostgreSQL

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

Execute the following to install node-postrges via npm:

 npm install pg

Once you’ve installed that 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 client method from node-postgres takes the details of the database you are connecting to. This program imports its connection details from a file called dbConfig. Therefore, create that 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’s always good practice to use individual functions to interact with the database. They make it easy to write unit tests and improve reusability. For the signup 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 only register a user if they don’t 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 that returns a row that has 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 email and password values. 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 should hash it using bcrypt. It is never a good idea to store passwords as plain text. If hackers got access to your user database they could easily access 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, during login, you will need to compare the plain text password given by the user and the hashed password in the database. For this, you can use the compare 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 plain password and the hash and then uses Bcrypt.compare() to determine if the password provided is correct. If it is, it returns true otherwise, it returns false.

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

 

module

.exports = { emailExists, createUser, matchPassword };

Configure Passport

Passport is a Node authentication middleware that provides over 500 authentication strategies like social login, JSON Web Tokens (JWT), and email authentication. We will be using 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 login existing users and register new users.

Start by creating a new file passportConfig.js. Then, import the Passport local strategy and the 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 set up user sign-up:

 

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 a password, and you are using an email, set the username field to an email. The user or rather the frontend part of this application will send the email and password in the request body. However, you don’t need to extract the values yourself as Passport will handle that in the background.

This program first checks whether the email is already taken using the emailExists() function from 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 login 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 whether the email is registered. If not, it returns false. If it finds the email, it compares its password with the one from the request. If the passwords match, it logs in the user and returns the user object.

The final step is to create the API endpoints:

  • POST /auth/signup
  • 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 set up Passport in a new file named server.js:

 

const

passport =

require

(

"passport"

);

require

(

"./passportConfig"

)(passport);

Then, 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.

Check Your API Using Unit Tests

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

While you can use REST clients like Postman to test how well an API works, writing unit tests is much simpler. Unit tests allow you to test the individual parts of your application. This way, even if an endpoint fails, you can pinpoint the exact point of failure. One of the tools you can use to test Node applications is Jest.