Back to List

Passport.js β€” Authentication Middleware

Understand the structure of Passport.js and the Local Strategy, and implement login authentication in Express.

Intermediate
|
12min
|
Verified (2026-07)
Passport.jsAuthentication MiddlewareLocal StrategySerializationSession Authentication
Progress0/55 (0%)

Passport.js – Authentication Middleware

After completing this topic

You will understand why Passport.js is necessary, be able to implement ID/password authentication using the Local Strategy, and explain the roles of serialization and deserialization.


Why use Passport.js?

In the previous topic, we implemented session-based authentication ourselves. While it works, the code becomes more complex as the number of authentication methods increases.

  • ID/password login
  • Google login
  • GitHub login
  • JWT token authentication

Implementing each of these separately would scatter authentication logic throughout the routes. Passport.js is an authentication middleware that solves this problem. It provides 500+ authentication strategies as plug-in modules.


Core Concept – Strategy Pattern

Passport.js uses the Strategy pattern. It separates "how to authenticate" into Strategy objects.

text
Passport.js (framework)
β”œβ”€β”€ Local Strategy    β†’ ID/password
β”œβ”€β”€ Google Strategy   β†’ Google OAuth
β”œβ”€β”€ GitHub Strategy   β†’ GitHub OAuth
└── JWT Strategy      β†’ JSON Web Token

Each Strategy is an independent npm package. Install and use only what you need.


Installation

bash
npm install passport passport-local express-session
  • passport: Core library
  • passport-local: ID/password authentication strategy
  • express-session: Session management (used internally by Passport)

Setting up the Local Strategy

javascript
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const app = express();

// Mock user database
const users = [
  { id: 1, username: 'alice', password: 'pass123', displayName: 'Alice Kim' },
  { id: 2, username: 'bob', password: 'pass456', displayName: 'Bob Lee' }
];

// 1. Register the Strategy
passport.use(new LocalStrategy(
  (username, password, done) => {
    const user = users.find(u => u.username === username);
    if (!user) {
      return done(null, false, { message: 'User not found' });
    }
    if (user.password !== password) {
      return done(null, false, { message: 'Wrong password' });
    }
    return done(null, user);
  }
));

The three calling patterns for the done callback:

CallMeaning
done(null, user)Authentication successful. Pass the user object.
done(null, false, {message})Authentication failed. User not found or incorrect password.
done(err)System error. Database connection failure, etc.

Serialize / Deserialize

Storing the entire user object in the session wastes memory. Passport stores only the minimum identifying information (usually the ID) in the session and restores the user from that ID on each request.

javascript
// 2. Data to be stored in the session (once upon login)
passport.serializeUser((user, done) => {
  done(null, user.id);  // Store only user.id in the session
});

// 3. Restore the user from the session (on each request)
passport.deserializeUser((id, done) => {
  const user = users.find(u => u.id === id);
  done(null, user);     // Set the entire user object in req.user
});
text
On login:   user object β†’ serializeUser β†’ store id=1 in session
On each request:  id=1 from session β†’ deserializeUser β†’ restore user object β†’ req.user

By querying the database in deserializeUser, you can store only the ID in the session while still having access to the latest user information on each request.


Connecting the Middleware

javascript
// 4. Configure middleware (order is important!)
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(session({
  secret: process.env.SESSION_SECRET || 'dev-secret',
  resave: false,
  saveUninitialized: false
}));
app.use(passport.initialize());   // Initialize Passport
app.use(passport.session());      // Connect to the session

The order is important: express-session β†’ passport.initialize() β†’ passport.session(). If the order is incorrect, the session will not work properly.


Login / Logout Routes

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

// Or, if you want a JSON response
app.post('/api/login', (req, res, next) => {
  passport.authenticate('local', (err, user, info) => {
    if (err) return next(err);
    if (!user) {
      return res.status(401).json({ error: info.message });
    }
    req.logIn(user, (err) => {
      if (err) return next(err);
      res.json({ message: `Welcome, ${user.displayName}` });
    });
  })(req, res, next);
});

// Logout
app.post('/logout', (req, res) => {
  req.logout((err) => {
    if (err) return res.status(500).json({ error: 'Logout failed' });
    res.json({ message: 'Logged out' });
  });
});

passport.authenticate('local') executes the LocalStrategy that you registered earlier. If successful, serializeUser is called, and the user ID is stored in the session.


Protected Routes

javascript
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }
  res.status(401).json({ error: 'Login required' });
}

app.get('/dashboard', ensureAuthenticated, (req, res) => {
  res.json({
    message: `Hello, ${req.user.displayName}`,
    user: { id: req.user.id, username: req.user.username }
  });
});

req.isAuthenticated() is a method provided by Passport that returns true if there is a valid user in the session. req.user is the user object restored by deserializeUser.


Overall Flow Summary

text
1. Server starts
   passport.use(LocalStrategy)    β€” Register authentication method
   passport.serializeUser()       β€” Register session storage method
   passport.deserializeUser()     β€” Register session restoration method

2. POST /login
   passport.authenticate('local') β€” Execute Strategy
   β†’ Success: serializeUser β†’ Store ID in session β†’ Respond
   β†’ Failure: 401 response

3. GET /dashboard (authentication required)
   Check session cookie β†’ deserializeUser β†’ Set req.user
   ensureAuthenticated β†’ req.isAuthenticated() β†’ true β†’ Execute route

4. POST /logout
   req.logout() β†’ Remove user from session β†’ Respond

Common Mistakes

MistakeResultSolution
Call passport.session() before app.use(session(...))Session is not initializedexpress-session β†’ passport.initialize() β†’ passport.session() order
Store the entire user object in serializeUserSession size explodesStore only user.id
Ignore asynchronous errors in deserializeUserServer crashes on authentication failureCall done(err)
Forget to call done() in the Strategy callbackRequest is indefinitely pendingVerify that done() is called in all branches

Key Takeaways

ConceptSummary
Passport.jsAuthentication middleware. Supports various authentication methods using the Strategy pattern
StrategyObject that encapsulates the authentication method (Local, Google, JWT, etc.)
serializeUserDetermine the minimum information to be stored in the session (usually user.id)
deserializeUserRestore the user object from the session ID on each request
req.userUser object restored by deserializeUser
req.isAuthenticated()Check login status

The value of Passport.js is "standardization of authentication logic." When switching from Local to Google OAuth, you only need to replace the Strategy. serialize/deserialize, middleware chain, req.user – the rest of the code remains the same.

πŸ’¬ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...