Passport.js

A Pluggable Auth Layer With 500+ Strategies

Passport.js

Passport.js is the long-standing auth middleware. Local, JWT, OAuth — same API for all.

4 min read Level 2/5 #express#passport#auth
What you'll learn
  • Configure a Passport strategy
  • Add local username/password auth
  • Know when to skip Passport

Passport.js is a long-standing auth library for Node. Around 500 community strategies cover everything from username/password to Google/GitHub/Facebook/Apple/LDAP/SAML.

Install

npm install passport passport-local

Configure a Strategy

import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import bcrypt from "bcrypt";

passport.use(new LocalStrategy(
  { usernameField: "email" },
  async (email, password, done) => {
    try {
      const user = await db.users.findByEmail(email);
      if (!user) return done(null, false);
      const ok = await bcrypt.compare(password, user.passwordHash);
      if (!ok) return done(null, false);
      done(null, user);
    } catch (err) {
      done(err);
    }
  }
));

passport.serializeUser((user, done)   => done(null, user.id));
passport.deserializeUser(async (id, done) => {
  done(null, await db.users.findById(id));
});

Wire It In

import session from "express-session";

app.use(session({ /* ... */ }));
app.use(passport.initialize());
app.use(passport.session());

A Login Route

app.post("/auth/login",
  passport.authenticate("local"),
  (req, res) => {
    res.json({ data: { id: req.user.id, email: req.user.email } });
  }
);

If credentials are wrong, Passport returns 401 automatically. Otherwise, req.user is set for downstream handlers.

Logout

app.post("/auth/logout", (req, res, next) => {
  req.logout((err) => {
    if (err) return next(err);
    res.status(204).end();
  });
});

Multi-Strategy

The big win: add Google, GitHub, etc. with the same API:

import { Strategy as GoogleStrategy } from "passport-google-oauth20";

passport.use(new GoogleStrategy({
  clientID:     process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL:  "/auth/google/callback",
}, async (accessToken, refreshToken, profile, done) => {
  let user = await db.users.findByGoogleId(profile.id);
  if (!user) user = await db.users.create({ googleId: profile.id, email: profile.emails[0].value });
  done(null, user);
}));

app.get("/auth/google",
  passport.authenticate("google", { scope: ["email", "profile"] })
);

app.get("/auth/google/callback",
  passport.authenticate("google", { failureRedirect: "/login" }),
  (req, res) => res.redirect("/")
);

We cover OAuth in the next lesson.

Should You Use It?

Pros:

  • Mature, battle-tested
  • Tons of strategies
  • One API for many providers

Cons:

  • Aging — async/await wasn’t around when it was designed
  • Callback-style sometimes feels old
  • Boilerplate for things modern libs do in 5 lines

Alternatives in 2026:

  • Auth.js (NextAuth) — modern, multi-framework
  • lucia-auth — minimal, TS-first
  • Roll your own (for very specific needs)
  • Hosted providers (Clerk, Auth0) — skip auth entirely

For a new project, look at the alternatives first. Passport is what you’ll see in older codebases.

OAuth →