Intro
Besides industry stundards using oauth2 Google or Github for authentication of users, you may need to have simple user/pass based authentication.
This may be implemented in many different ways, ie using Basic Authentication on the Web Server side.
If you would like to implement it using Passport on your backend, here are few point to consider: use HTTPS end to end, keep users and passwords in secure place, use crypto lib.
Implementation
This is basic setup without any crypto lib. It assumes Redis is running on the localhost:6379 to store sessions. Keep in mind the order in your index.js
## index.js
#
var express = require('express');
var passport = require('passport');
var LocalStrategy = require('passport-local');
var crypto = require('crypto');
const bodyParser = require('body-parser');
var session = require('express-session');
const {RedisStore} = require('connect-redis');
const { createClient } = require('redis');
var redisClient = createClient({url: 'redis://localhost:6379'});
redisClient.connect().catch(console.error);
const users = [
{ id: 1, username: 'admin', password: 'adminpass' },
{ id: 2, username: 'user1', password: 'secret' }
];
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(express.urlencoded({ extended: false }));
app.use(express.json()); // ← parse JSON body
function checkAthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.status(401).send('Unauthorized');
}
app.use(session({
store: new RedisStore({ client: redisClient, session}),
secret: 'secret-key',
resave: false,
saveUninitialized: false,
cookie: { secure: false } // Set to true if using HTTPS
}));
// Passport local strategy
passport.use(new LocalStrategy((username, password, done) => {
console.log("Searching for:", username);
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
console.log(`User ${username} not found or password incorrect`);
return done(null, false);
}
return done(null, user);
}));
app.post('/login/password', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
}))
passport.serializeUser((user, done) => {
done(null, user.id); // Store only user ID in session
});
passport.deserializeUser((id, done) => {
console.log("Deserializing user with ID:", id);
const user = users.find(u => u.id === id);
done(null, user || false);
});
app.use(passport.initialize());
app.use(passport.session());
app.get('/', function(req, res) {
console.log("Incoming Cookies:", req.headers.cookie);
console.log("req.session:", req.session);
console.log("req.user:", req.user);
if (req.isAuthenticated()) {
res.send('Hello ' + req.user.username);
} else {
res.send('Please log in');
}
});
app.listen(3000, function() {
console.log('Server is running on http://localhost:3000');
});
Test using curl
You can test login using curl by instructing it to save cookie into file and send the saved cookie with the next request to protected endpoint:
# Login
curl -i -c cookie.txt -X POST https://localhost:3000/login/password -H "Content-Type: application/json" -d {"username": "user1", "password": "secret"}
# Access protected endpoint
curl -b cookie.txt http://localhost:3000/
Issues
Documentation was not clear about using Redis as session store and docs in the Internet are showing wrong instructions. Using the examples from the Internet you may have an error to call:
app.use(session({
store: new RedisStore({ client: redisClient, session}),
...
}));
This is resolved by using require different than in docs:
const {RedisStore} = require('connect-redis');
instead of:
const RedisStore = require('connect-redis').default;