Back to blog
Jun 18, 2025
4 min read

Setting Up a Custom Authentication System with Node and Passport.js

Learn how to implement a secure, flexible authentication system in your Node.js application using Passport.js. A step-by-step guide for customizing login and user verification.

Setting Up a Custom Authentication System with Node and Passport.js

Authentication is a fundamental part of any modern web application. Whether you’re building a personal project or a scalable SaaS platform, you need a system that securely verifies users and protects sensitive data. While there are many third-party authentication providers, sometimes you need more control. That’s where Passport.js shines.

Passport.js is a popular Node.js middleware that simplifies the process of adding authentication to your app. It supports a wide range of strategies (local, OAuth, JWT, etc.) and is flexible enough to fit custom logic and database integration.

This guide walks through how to set up a custom authentication system using Node.js, Express, and Passport.js, with a local strategy and session-based authentication.


Why Choose Passport.js?

  • Modular: Use only the strategies you need
  • Extensible: Customize how users are authenticated and stored
  • Compatible: Works seamlessly with Express and other middleware
  • Community-supported: Wide range of plugins and examples available

Project Setup

1. Install Dependencies

Create a new project and install the required packages:

npm init -y
npm install express passport passport-local express-session bcryptjs body-parser

2. File Structure

You can use a simple file structure like this:

/auth-app
  ├── server.js
  ├── auth/
  │   ├── passport-config.js
  └── users.js

Step-by-Step Implementation

1. Configure the Express App (server.js)

const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const passport = require('passport');
const initializePassport = require('./auth/passport-config');
const users = require('./users');

const app = express();

initializePassport(passport);

app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
  secret: 'secret-key',
  resave: false,
  saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());

app.post('/login', passport.authenticate('local', {
  successRedirect: '/dashboard',
  failureRedirect: '/login'
}));

app.get('/dashboard', (req, res) => {
  if (req.isAuthenticated()) {
    res.send('Welcome to your dashboard!');
  } else {
    res.redirect('/login');
  }
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

2. Create the Passport Config (auth/passport-config.js)

const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcryptjs');
const users = require('../users');

function initialize(passport) {
  const authenticateUser = async (username, password, done) => {
    const user = users.find(u => u.username === username);
    if (!user) return done(null, false, { message: 'No user found' });

    try {
      if (await bcrypt.compare(password, user.password)) {
        return done(null, user);
      } else {
        return done(null, false, { message: 'Incorrect password' });
      }
    } catch (e) {
      return done(e);
    }
  };

  passport.use(new LocalStrategy(authenticateUser));
  passport.serializeUser((user, done) => done(null, user.id));
  passport.deserializeUser((id, done) => {
    const user = users.find(u => u.id === id);
    return done(null, user);
  });
}

module.exports = initialize;

3. Simulate a User Database (users.js)

const bcrypt = require('bcryptjs');

const users = [];

(async () => {
  const hashedPassword = await bcrypt.hash('password123', 10);
  users.push({ id: 1, username: 'johndoe', password: hashedPassword });
})();

module.exports = users;

This simulates a single user. In a production app, you’d use a real database (e.g., MongoDB, PostgreSQL).


Enhancing the System

Once the basic setup works, you can extend it with:

  • User registration endpoint with email validation
  • Flash messages for login errors
  • Rate limiting and brute-force protection
  • CSRF protection for sensitive requests
  • JWT tokens for stateless authentication in APIs
  • OAuth strategies (Google, GitHub, etc.) via passport-google-oauth20 or similar packages

Conclusion

By using Passport.js with Node.js and Express, you can build a custom authentication system that balances security, flexibility, and control. Whether you’re handling login sessions or planning to scale with tokens and social logins, Passport gives you a reliable foundation for managing user identity.

With a secure setup in place, you can now focus on building user features that truly matter , all while knowing that your app’s gatekeeping is solid.


Disclaimer

Article written with the help of AI.

Read the full Disclaimer HERE