A Pluggable Auth Layer With 500+ Strategies
Passport.js
Passport.js is the long-standing auth middleware. Local, JWT, OAuth — same API for all.
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 →