Skip to content

Configuring Passport

Now that we have our User data model prepared and our dependencies installed, it’s time to actually configure the middleware sequence in server.js. This is where all the disparate parts—Express, sessions, and Passport—are glued together.

There are three primary components to complete before we can authenticate anyone:

  1. Initialize the express-session storage.
  2. Initialize passport to tap into that session.
  3. Define the Serialization and Deserialization rules so Passport knows exactly how to pack and unpack our User objects into the session cookie.

T.A. Watts Note: Middleware order in your server.js file is critical. The session middleware must be mounted before the passport session middleware, and all of this must occur before your routers.

1. The Session and Passport Initialization

Section titled “1. The Session and Passport Initialization”

In our root server.js file, we need to require the necessary packages and integrate them into our middleware stack.

server.js
const express = require("express");
const session = require("express-session");
const passport = require("passport"); // We will change this to our custom file shortly!
const app = express();
// ... (other middleware like body-parsers and static files) ...
// 1. Configure the Session Middleware
app.use(
session({
secret: "super_secret_dev_key", // In production, this must be an environment variable!
resave: false,
saveUninitialized: false,
}),
);
// 2. Initialize Passport and its Session tie-in
app.use(passport.initialize());
app.use(passport.session());
// ... (your routers go here) ...

Professor Solo: The secret parameter is what Express uses to cryptographically sign the session ID cookie. This prevents malicious users from simply faking a cookie string and hijacking someone else’s session.

2. The Identity Transformer (Serialization)

Section titled “2. The Identity Transformer (Serialization)”

When a user successfully logs in, Passport needs to store a reference to that user in the newly created session. But storing the entire user document (including the hashed password) in the session memory is wasteful and potentially dangerous.

Professor Solo: Why just the _id? If we stored the whole User object in the session, any changes made to the user in the database (like updated permissions) wouldn’t be reflected until they logged out and back in! Although this may seem less performant, security is our greater concern here. By only storing the ID, we force Passport to fetch the freshest version of the User from the database on every single request.

Serialization is the process of extracting the smallest possible unique identifier (usually the Mongo _id) and packing that into the session object.

Let’s create a dedicated file for all of our Passport logic so server.js doesn’t get cluttered.

middleware/passport.js
const passport = require("passport");
const _userOps = require("../data/users"); // Assuming we added operations here
// Serialize: What should we put IN the session cookie?
passport.serializeUser((user, done) => {
// We only store the user's stringified _id
done(null, user._id.toString());
});

On every subsequent request from the browser, Express reads the incoming cookie and finds the corresponding session object in memory. Passport then takes over and sees the _id string we serialized earlier.

Deserialization is the process of taking that _id from the session, querying the database to find the full User document, and attaching it to req.user for the remainder of the request lifecycle.

// middleware/passport.js (continued)
// ...
// Deserialize: How do we unpack the cookie and get the whole User back?
passport.deserializeUser(async (id, done) => {
try {
// We haven't written this data method yet, but we will!
const user = await _userOps.getUserById(id);
done(null, user);
// ^ This user object is now globally available at req.user
} catch (err) {
done(err);
}
});
module.exports = passport;

Now that our local Passport file knows how to handle serialization, we need to instruct server.js to use this customized configuration instead of the raw, unconfigured package.

Head back to server.js and replace the original passport import with our new custom passport middleware.

server.js
// const passport = require("passport"); // <--- REMOVE THIS
const passport = require("./middleware/passport"); // <--- ADD THIS

Professor Solo: You might be wondering why server.js can call passport.initialize() and passport.session() when we never explicitly wrote those methods inside our new custom middleware/passport.js file! This works seamlessly because Node.js dynamically caches module require statements. When our middleware file independently imports passport, configures it, and then directly exports it utilizing module.exports = passport;, we are passing back the genuine, fully authentic native Passport object. Therefore, all of the powerful built-in library methods inherently come along for the ride automatically!

With the infrastructure pipeline connected and injected, the bouncer now knows where to stand and how to handle the VIP wristbands. Next, we need to actually start letting people sign up to be on the list.

Let’s build the sign-up mechanism.