Choosing Between Passport.js LocalStrategy and OAuth 2.0 Strategies for Node.js Authentication
Decide whether to store credentials locally or delegate login to an external provider when using Passport.js in a Node.js app.
16 Oct 2026, 21:00 UTC

Decision: Local vs OAuth Authentication with Passport.js
When building a Node.js web application, you must decide how users prove their identity. Passport.js lets you plug in different authentication "strategies". The two most common choices are:
- LocalStrategy – validates a username and password stored in your own database.
- OAuth 2.0 Strategy (e.g., Google, GitHub) – redirects the user to an external provider that confirms identity and returns a token.
Constraints to consider
- Do you want to store credentials?
- Do you need to support users who prefer not to create another password?
- Are you able to manage secrets (client ID/secret) for external providers?
- Do you have a session store configured?
Comparison table
| Aspect | LocalStrategy | OAuth 2.0 Strategy |
|---|---|---|
| Credential storage | Stored locally; you must hash passwords (e.g., with bcrypt). | No password stored; relies on provider. |
| User experience | Requires registration/login form. | One-click login via provider. |
| Implementation complexity | Medium – need verify callback, password hashing. | Low to medium – need client secret, redirect handling. |
| Dependency risk | Low – only your DB. | Medium – provider API changes or outage affect login. |
| Typical use case | Internal tools, SaaS where you control accounts. | Public apps, blogs, where social login improves conversion. |
Trade‑offs
If you choose LocalStrategy you gain full control over the user database but must implement secure password storage and handle forgotten‑password flows. OAuth reduces the attack surface for credentials but introduces reliance on third‑party services and requires you to protect client secrets.
Concrete implementation: LocalStrategy with bcrypt
Run the following commands in your project directory (you need npm install permission).
# Install dependencies
npm install express express-session passport passport-local bcrypt
# Create a file auth-local.js
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const app = express();
// Session middleware – requires a store; here we use memory for demo
app.use(session({ secret: 'change-me-in-prod', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
// Dummy user store – replace with your DB lookup
const users = [
{ id: 1, username: 'alice', password: '$2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW' } // hash of 'secret'
];
// Verify callback for LocalStrategy
passport.use(new LocalStrategy(
function(username, password, done) {
const user = users.find(u => u.username === username);
if (!user) { return done(null, false); }
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) { return done(err); }
if (!isMatch) { return done(null, false); }
return done(null, { id: user.id, username: user.username });
});
}
));
// Serialize and deserialize user for session support
passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser((id, done) => {
const user = users.find(u => u.id === id);
done(null, user ? user : null);
});
// Login route
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login?error=true' }),
(req, res) => {
// On success, req.user is set and session cookie is sent
res.redirect('/profile');
}
);
// Example protected route
app.get('/profile', (req, res) => {
if (!req.isAuthenticated()) { return res.redirect('/login'); }
res.send(`Hello ${req.user.username}`);
});
app.listen(3000, () => console.log('Listening on http://localhost:3000'));
Validation steps
- Start the server:
node auth-local.js. - Open a browser to
http://localhost:3000/login(you’ll need a simple login form that POSTs to/loginwith fieldsusernameandpassword). - Submit the credentials
alice/secret. - After a successful login, inspect the response headers for a
connect.sidcookie (or whatever your session cookie name is). - Visit
http://localhost:3000/profile; you should see a greeting with the username. - To confirm serialization, you can log
req.userin the deserialize callback or add a middleware that prints it to the console.
Limitations and practical checks
- The example uses an in‑memory user array and a memory session store; replace both with a persistent database and a production‑ready session store (e.g., Redis) before deploying.
- Password hash in the example is pre‑computed; in real code you would hash the password at registration time with
bcrypt.hash. - If you switch to an OAuth strategy, you would install
passport-google-oauth20(or similar), configure a Google Cloud project to obtain a client ID and secret, and add a verify callback that receives the provider’s profile. - Check that the session cookie is marked
HttpOnlyand, in production,Secureonly when served over HTTPS.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.