Adding Username/Password Login to an Express App with Passport.js Local Strategy
Step‑by‑step guide to add username/password login with Passport.js local strategy, session handling, and route protection in a Node.js Express app.
06 Feb 2026, 08:14 UTC

Desired outcome
You will have a working Express application that authenticates users with a username and password using Passport.js' local strategy, maintains login state via express‑session, and protects specific routes so only logged‑in users can access them.
Prerequisites
- Node.js (≥14) and npm installed.
- A basic Express project (created with
npm init -yandnpm install express). - Access to a terminal in the project directory.
- No special permissions are required; standard user rights suffice.
Focused procedure
1. Install required packages
Run the following command in your project root:
npm install passport passport-local express-session bcrypt
This adds Passport, the local strategy, session middleware, and bcrypt for password hashing.
2. Configure session middleware
Create or edit app.js (or your main server file) and place the session setup before Passport initialization:
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const app = express();
app.use(session({
secret: 'replace‑with‑a‑strong‑random‑string', // store this securely in production
resave: false,
saveUninitialized: false,
cookie: { httpOnly: true, secure: false } // set secure:true when using HTTPS
}));
app.use(passport.initialize());
app.use(passport.session());
3. Implement the local strategy verify callback
For demonstration we keep an in‑memory user list. In a real app replace this with a database lookup.
const bcrypt = require('bcrypt');
// Example user store – replace with your DB layer
const users = [
{ id: 1, username: 'alice', password: '$2b$10$e0mi5YvVfZ2G0l8K9XyUOeUeV9R6tG5J6yZ7b8c9d0e1f2g3h4i5j' } // password: 'secret123'
];
function findUserByUsername(username) {
return users.find(u => u.username === username);
}
passport.use(new (require('passport-local').Strategy)(
{ usernameField: 'username', passwordField: 'password' }, // matches default body keys
async (username, password, done) => {
const user = findUserByUsername(username);
if (!user) { return done(null, false); }
const match = await bcrypt.compare(password, user.password);
if (!match) { return done(null, false); }
return done(null, { id: user.id, username: user.username });
}
));
// Serialize and deserialize user instances for the session
passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser((id, done) => {
const user = users.find(u => u.id === id);
return done(null, user);
});
4. Define login and protected routes
app.use(express.urlencoded({ extended: true })); // for form bodies
app.use(express.json()); // for JSON bodies
// Show a simple login form (GET)
app.get('/login', (req, res) => {
res.send(`
Log in
`);
});
// Process login (POST)
app.post('/login',
passport.authenticate('local', {
failureRedirect: '/login',
failureMessage: true
}),
(req, res) => {
// On success, redirect to a protected page
res.redirect('/profile');
}
);
// Example protected route
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
app.get('/profile', ensureAuthenticated, (req, res) => {
res.send(`Hello, ${req.user.username}!
`);
});
// Logout endpoint
app.get('/logout', (req, res, next) => {
req.logout(err => {
if (err) { return next(err); }
res.redirect('/login');
});
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
5. Test the flow
- Start the server:
node app.js. - Open
http://localhost:3000/loginin a browser. - Enter username
aliceand passwordsecret123; you should be redirected to/profileseeing a greeting. - Visit
http://localhost:3000/profiledirectly after logging out; you should be redirected back to/login. - Try invalid credentials; the form should redisplay with an error (if you enable flash messages) or simply redirect to the login page.
Expected checks
- After a successful login,
req.isAuthenticated()returnstrueon subsequent requests. - The session cookie (
connect.sid) is present in the browser’s developer tools. - Calling
/logoutremoves the session andreq.isAuthenticated()becomesfalse.
Limitations and practical verification
This example uses an in‑memory array for users, which is not persistent across server restarts and unsuitable for production. Replace the users array and findUserByUsername function with a database query (e.g., using Sequelize, Mongoose, or a simple PostgreSQL client).
To verify password storage safety, ensure that the password stored in the user record is a bcrypt hash (as shown). Never store or compare plaintext passwords.
Session security: in production set secure: true on the session cookie and serve the app behind HTTPS. Also use a strong, randomly generated secret stored in an environment variable, not hard‑coded.
Rollback / recovery
If you need to revert the changes:
- Remove the added code blocks from
app.js(or your server file). - Uninstall the packages:
npm uninstall passport passport-local express-session bcrypt. - Restart the server; the application will return to its pre‑authentication state.
These steps only affect files you modified; they do not alter any external databases.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.