Configuring Passport
Wiring the Framework
Section titled “Wiring the Framework”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:
- Initialize the
express-sessionstorage. - Initialize
passportto tap into that session. - Define the Serialization and Deserialization rules so Passport knows exactly how to pack and unpack our
Userobjects 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.
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 Middlewareapp.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-inapp.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.
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());});3. Rehydrating the User (Deserialization)
Section titled “3. Rehydrating the User (Deserialization)”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;4. Injecting the Configuration
Section titled “4. Injecting the Configuration”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.
// const passport = require("passport"); // <--- REMOVE THISconst passport = require("./middleware/passport"); // <--- ADD THISProfessor 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.
⏭ Next: The Registration Flow
Section titled “⏭ Next: The Registration Flow”Let’s build the sign-up mechanism.