Passport.js
Passport.js is a popular authentication middleware for Node.js, designed to simplify the process of implementing user authentication in web applications. It's highly flexible, modular, and integrates seamlessly with Express-based apps, supporting over 500 authentication strategies, including local username/password, OAuth (e.g., Facebook, Google, Twitter), JWT, and more
Key Features of Passport.js:
- Modular Design: Uses "strategies" as plugins to handle different authentication methods, allowing developers to pick and choose based on their needs.
- Express-Compatible: Works as middleware in Express or Connect-based applications, requiring passport.initialize() and optionally passport.session() for persistent login sessions.
- Session Management: Supports session-based authentication by serializing/deserializing user data, typically storing the user ID in the session and retrieving user details on subsequent requests.
Strategies
A strategy is a plugin that tells Passport how to authenticate a user. Each strategy represents a different way of logging in — like via username/password, Google, Facebook, or JWT tokens.
Example Strategies: passport-local(Username + password login), passport-jwt (JWT token-based login APIs), passport-google-oauth20, passport-facebook e.t.c we can explore on passportjs website.
To use specific strategy install it: npm i strategy_name. e.g - npm i passport-local. We can use multiple strategies also to give different authentication methods.
passport-local-mongoose
passport-local-mongoose is a Mongoose plugin that simplifies using Passport.js with Mongoose for local authentication (i.e., username & password).
What It Does:
- Adds username + hashed password fields to your schema.
- Adds methods like: .register() - to create users, .authenticate() - for login, .serializeUser() / .deserializeUser() - for session handling
- Automatically hashes passwords using bcrypt under the hood.
npm i passport-local-mongoose
//How to Use:
//User Schema with Plugin:
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const UserSchema = new mongoose.Schema({});
UserSchema.plugin(passportLocalMongoose); //use plugin
//full example - login/signup with passport-local:
npm install express mongoose passport passport-local passport-local-mongoose express-session connect-flash ejs
//Mongoose User Model (models/user.js):
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const UserSchema = new mongoose.Schema({
email: String
});
UserSchema.plugin(passportLocalMongoose); // Adds username + password hashing
module.exports = mongoose.model('User', UserSchema);
//app.js:
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const flash = require('connect-flash');
const passport = require('passport');
const LocalStrategy = require('passport-local');
const User = require('./models/user');
const app = express();
// MongoDB connection
mongoose.connect('mongodb://127.0.0.1:27017/authDemo')
.then(() => console.log('MongoDB connected'))
.catch(err => console.log(err));
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: true }));
// Session(using session is must) & flash config
app.use(session({ secret: 'secretkey123', resave: false, saveUninitialized: false }));
app.use(flash());
// Passport config
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// Global flash + user
app.use((req, res, next) => {
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
// Routes
app.get('/register', (req, res) => {
res.render('register');
});
app.post('/register', async (req, res) => {
try {
const { username, email, password } = req.body;
const user = new User({ username, email });
const registeredUser = await User.register(user, password); //pass password here
req.login(registeredUser, err => {
if (err) return next(err);
req.flash('success', 'Welcome!');
res.redirect('/');
});
} catch (err) {
req.flash('error', err.message);
res.redirect('/register');
}
});
app.get('/login', (req, res) => {
res.render('login');
});
app.post('/login', passport.authenticate('local', { failureFlash: true, failureRedirect: '/login' }), (req, res) => {
req.flash('success', 'Welcome back!');
res.redirect('/');
});
app.get('/logout', (req, res, next) => {
req.logout(err => {
if (err) return next(err);
req.flash('success', 'Logged out successfully.');
res.redirect('/');
});
});
// Middleware to protect routes
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) return next();
req.flash('error', 'You must be logged in');
res.redirect('/login');
}
app.get('/secret', isLoggedIn, (req, res) => {
res.send('This is a secret page. Only for logged-in users!');
});
app.listen(3000, () => console.log('Server started on http://localhost:3000'));