Passport isAuthenticated() always returns false? - node.js

So I am having a problem with Passport I've been trying to move from my original method of authentication because Passport supports other types like Google and GitHub. I'm trying to implement the local authentication and it doesn't seem to be working, even after looking up many articles and they all don't work.
This is at the top of the code:
const cookieExpirationDate = new Date();
cookieExpirationDate.setDate(cookieExpirationDate.getDate() + 7);
app.use(session({
secret: secret_key,
store: sessionStore,
resave: true,
saveUninitialized: true,
cookie: {
httpOnly: true,
sameSite: 'strict',
expires: cookieExpirationDate
}
}));
// PASSPORT //
app.use(passport.initialize());
app.use(passport.session());
passport.use('local', new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true //passback entire req to call back
}, async function (req, username, password, done) {
if (!username || !password) {
return done(null, false, {message: 'Please complete the form!'})
}
const reqBody = {
response: req.body['h-captcha-response'],
secret: captcha_key
}
let axiosResult = await axios.post('https://hcaptcha.com/siteverify', qs.stringify(reqBody), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
if (axiosResult.data.success === true) {
let results = await runQuery('SELECT * FROM accounts WHERE (username = ? OR email = ?)', [username, username])
const forwarded = req.headers['x-forwarded-for']
const ip = forwarded ? forwarded.split(/, /)[0] : req.connection.remoteAddress
if (!results.length) {
let amtLeft = await loginAttempts(ip);
if (amtLeft > 1) {
return done(null, false, {message: `Incorrect Username and/or Password! (${amtLeft} attempt(s) left)`});
} else {
return done(null, false, {message: `You must wait 15 minutes before trying again!`});
}
}
let user = results[0]
let isMatch = await bcrypt.compareSync(password, user.password)
if (!isMatch) {
let amtLeft = await loginAttempts(ip);
if (amtLeft > 1) {
return done(null, false, {message: `Incorrect Username and/or Password! (${amtLeft} attempt(s) left)`});
} else {
return done(null, false, {message: `You must wait 15 minutes before trying again!`});
}
} else {
if (user.activation_code === "activated") {
return done(null, user)
} else {
return done(null, false, {message: 'Check your email for an activation email!'})
}
}
} else {
return done(null, false, {message: `You must complete the captcha!`});
}
}
));
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(async function (usrid, done) {
let results = await runQuery('SELECT * FROM accounts WHERE id = ?', usrid)
done(results[0]);
});
Login API part:
app.post('/login_sys', regularFunctions, function (req, res, next) {
passport.authenticate('local', {failWithError: true}, function (error, user, info) {
if (error) {
return res.status(500).json(error);
}
if (!user) {
return res.status(401).json(info);
}
return res.status(200).send('Success')
})(req, res, next);
})
regularFunctions:
let regularFunctions = [
bodyParser.urlencoded({extended: true}),
bodyParser.json(),
function (req, res, next) {
console.log('Authenticated: ' + req.isAuthenticated())
if (req.isAuthenticated()) {
req.session.loggedin = true;
return next();
} else {
req.session.loggedin = false;
return next();
}
}
]
I need it to return some sort of notification to the client if it fails or succeeds because I have a little pop up that lets them know they are getting redirected if it works and to notify them of their attempts left. The problem is it works and says that it logged in but when I refresh the page it never did.

Ok just found out the answer after searching for a while, I had to use req.login inside the login_sys route.

Related

passport.deserializeUser() not being called and req.user is undefined

Alright, I've been racking my brain over this for hours now. When I call my 'sign-in' route the passport middleware works fine and returns with a req.user obj, but when I call another route after that, req.user for that other route is undefined. Where exactly have I messed up here? I'm not sure it matters, but I am calling my API routes from a react client.
auth
router.post(
"/sign-in",
passport.authenticate("local"),
async (req, res, next) => {
if (!req.user) console.log("NO USER!*******************");
try {
const user = _.get(req, "user", "");
res.status(200).json(user);
} catch (e) {
console.log({ e });
return res.status(400).json(false);
}
}
);
My Server
app.use(cors());
app.use(cookieparser());
app.use(logger("dev"));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(
session({
secret: "super",
resave: false,
saveUninitialized: true,
cookie: { secure: false, maxAge: 4 * 60 * 60 * 1000 }
})
);
require("./utils/passport");
app.use(passport.initialize());
app.use(passport.session());
./utils/passport
const _ = require("lodash");
const LocalStrategy = require("passport-local").Strategy;
const { PrismaClient } = require("#prisma/client");
const prisma = new PrismaClient();
const bcrypt = require("bcrypt");
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(async function(id, done) {
console.log("");
console.log("deserializeUser*************************");
console.log("");
try {
const user = await prisma.user.findOne({ where: { id } });
if (!user) {
return done(null, false);
}
return done(null, user);
} catch (e) {
done(e);
}
});
passport.use(
new LocalStrategy(
{
passReqToCallback: true,
usernameField: "email"
},
async (req, email, password, done) => {
try {
const user = await prisma.user.findOne({ where: { email } });
if (!user) {
return done(null, false);
}
await bcrypt.compare(
password,
_.get(user, "password", ""),
(err, result) => {
if (!result) {
done(null, false);
}
done(null, user);
}
);
} catch (e) {
done(e);
}
}
)
);
I suspect something is wrong with the portion where you have your post method with passport authenticate.
This portion may be deleted:
if (!req.user) console.log("NO USER!*******************");
I think the asynchronous may produce here odd results. Then for the sake of troubleshooting, include in your
const user = _.get(req, "user", "");
real data, such as real user data. Thus you will be able to verify that the data is passing through.

req.isAuthenticated() is returning true even after wrong password is entered

I am using passport local strategy to authenticate the users, here is my login code:
app.post("/login",function(req,res){
const user = new model({
username:req.body.username,
password:req.body.password,
});
req.login(user, function(err) {
if (err) {
res.render("login",{error: err});
} else {
passport.authenticate("local")(req, res, function() {
res.redirect("/dashboard");
});
}
});
});
Now if I enter an incorrect password then an unauthorized message comes and then if I go to my dashboard route then req.isAuthenticated() is true,
here is my dashboard code:
app.get("/dashboard",function(req,res){
if(req.isAuthenticated()){
//mywork
}
How to solve this problem and how/where to handle that unauthorized message?
passport.use(model.createStrategy());
passport.serializeUser(model.serializeUser());
passport.deserializeUser(model.deserializeUser());
and
app.use(session({
secret: "secret",
resave: false,
saveUninitialized: false,
}));
You're using req.login. Do you know what it does? Here is how you handle you'r issues, first you create a strategy ( obviously you have a user model ).
const User = require('../models/User');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
/**
* Sign in using Email and Password.
*/
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
User.findOne({ email: email.toLowerCase() }, (err, user) => {
if (err) { return done(err); }
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
if (!user.password) {
return done(null, false, { msg: 'Your account was registered using a sign-in provider. To enable password login, sign in using a provider, and then set a password under your user profile.' });
}
user.comparePassword(password, (err, isMatch) => {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
}
return done(null, false, { msg: 'Invalid email or password.' });
});
});
}));
Then in your controller you can create a login method:
/**
* POST /login
* Sign in using email and password.
*/
exports.postLogin = (req, res, next) => {
const validationErrors = [];
if (!validator.isEmail(req.body.email)) validationErrors.push({ msg: 'Please enter a valid email address.' });
if (validator.isEmpty(req.body.password)) validationErrors.push({ msg: 'Password cannot be blank.' });
if (validationErrors.length) {
req.flash('errors', validationErrors);
return res.redirect('/login');
}
req.body.email = validator.normalizeEmail(req.body.email, { gmail_remove_dots: false });
passport.authenticate('local', (err, user, info) => {
if (err) { return next(err); }
if (!user) {
req.flash('errors', info);
return res.redirect('/login');
}
req.logIn(user, (err) => {
if (err) { return next(err); }
req.flash('success', { msg: 'Success! You are logged in.' });
res.redirect(req.session.returnTo || '/');
});
})(req, res, next);
};
To make sure you'r routes are authenticated:
app.get('/', homeController.index);
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/logout', userController.logout);
app.get('/forgot', userController.getForgot);
app.post('/forgot', userController.postForgot);
app.get('/reset/:token', userController.getReset);
app.post('/reset/:token', userController.postReset);
app.get('/signup', userController.getSignup);
app.post('/signup', userController.postSignup);
app.get('/account/verify', passportConfig.isAuthenticated, userController.getVerifyEmail);
app.get('/account/verify/:token', passportConfig.isAuthenticated, userController.getVerifyEmailToken);
app.get('/account', passportConfig.isAuthenticated, userController.getAccount);
And your app settings for passport strategy session:
app.use(session({
resave: true,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
cookie: { maxAge: 1209600000 }, // two weeks in milliseconds
store: new MongoStore({
url: process.env.MONGODB_URI,
autoReconnect: true,
})
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use((req, res, next) => {
if (req.path === '/api/upload') {
// Multer multipart/form-data handling needs to occur before the Lusca CSRF check.
next();
} else {
lusca.csrf()(req, res, next);
}
});
app.use(lusca.xframe('SAMEORIGIN'));
app.use(lusca.xssProtection(true));
app.disable('x-powered-by');
app.use((req, res, next) => {
res.locals.user = req.user;
next();
});
app.use((req, res, next) => {
// After successful login, redirect back to the intended page
if (!req.user
&& req.path !== '/login'
&& req.path !== '/signup'
&& !req.path.match(/^\/auth/)
&& !req.path.match(/\./)) {
req.session.returnTo = req.originalUrl;
} else if (req.user
&& (req.path === '/account' || req.path.match(/^\/api/))) {
req.session.returnTo = req.originalUrl;
}
next();
});

Passport authentication not persisting between endpoints

I'm writing an Express application that uses passport.js for authentication and I'd like to utilise express-session so that the user stays logged in if I call another endpoint. passport.authenticate and req.logIn both work, but when I call another endpoint, req.isAuthenticated() returns false and req.user is undefined
Here's my setup code for passport:
passport.use(new LocalStrategy(
function(username, password, done) {
db.all("SELECT * from users WHERE username='" + username +"'", function(err, rows) {
if (err != null) {
return done(err);
}
if (rows.length == 0) {
return done(null, false, {message: "No user found"});
}
var row = rows[0]
bcrypt.compare(password, row.password, function(err, res) {
if (err) {
return done(err)
}
if (res) {
return done(null, username)
} else {
return done(null, false, {message: "Incorrect password"})
}
})
})
}
))
passport.serializeUser((username, done) => {
done(null, username);
});
passport.deserializeUser(function(id, done) {
db.all("SELECT * from users WHERE username='" + id + "'", function(err, rows){
if (err != null) {
return done(err)
}
if (rows.length == 0) {
return done(null, false)
}
return done(null, id)
})
})
// add & configure middleware
app.use(session({
store: new SQLiteStore,
secret: my-secret
resave: false,
saveUninitialized: true
}))
app.use(passport.initialize());
app.use(passport.session());
This is my login endpoint
app.post('/login', function(req, res) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return res.status(500).json({ err })
}
if (!user) {
return res.status(400).json({
err: info.message
})
}
req.logIn(user, function(err) {
if (err) {
console.log(err)
return res.status(500).json({ err })
}
res.status(200).json({
msg: 'Success'
})
});
})(req, res)
})
Everything is fine at that point. It outputs the correct user ID.
I then call this endpoint:
app.post('/check-log-in', function(req, res, next) {
console.log(req.user)
console.log(req.isAuthenticated())
if (req.user) {
res.status(200).json({
loggedIn: true
})
} else {
res.status(200).json({
loggedIn: false
})
}
})
req.user is undefined and req.isAuthenicated() returns false.
I'm sure I've set up passport somewhere incorrectly but I've looked over examples and even some of code from another project that has worked but nothing seems to be doing the trick. Any advice?
This answer fixed my issue. It was a problem with fetch, so I switched to axios

Not able to solve passport-saml req.isAuthenticated() false issue

I'm new to saml and using Nodejs + Express + passport-saml + okta identity provider. I know this is a duplicate question but somehow I am not able to solve this by looking lot of threads on the internet.
I used yeoman express generator for project. Here are my settings:
Server is behind ngnix using https. So, if I hit https://mywebsite.com, it redirects internally to localhost:3000 on that server.
express.js
var samlUtil = require('./saml-util.js');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
limit: '2mb',
extended: true
}));
app.use(compress());
app.use(cookieParser(config.SERVER_KEYS.SERVER_SECRET));
app.use(express.static(config.root + '/public'));
app.use(methodOverride());
app.use(session({
secret: config.SERVER_KEYS.SERVER_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
expires: false,
secure: true
}
}));
app.use(samlUtil.initialize());
app.use(samlUtil.session());
app.get('/saml/response', samlUtil.protected, function(req, res) {
res.end("Hello " + req.session.passport.user);
});
app.get('/saml/invalid', function(req, res) {
res.end("Authentication failed");
});
app.post('/saml/callback', samlUtil.authenticate('saml', {
failureRedirect: '/saml/response/',
failureFlash: true
}), function(req, res) {
req.session.save(function() {
res.redirect('/saml/response/');
})
});
app.get('/saml/login', samlUtil.authenticate('saml', {
failureRedirect: '/saml/response/',
failureFlash: true
}), function(req, res) {
res.redirect('/saml/response/');
});
saml-util.js
var path = require('path');
var passport = require('passport');
var root = path.normalize(__dirname + '/../..');
var constant = require(root + '/app/util/constants.js');
var config = require(constant.APP_CONFIG_FILE);
var SamlStrategy = require('passport-saml').Strategy;
var users = [];
function findByEmail(email, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
if (user.email === email) {
return fn(null, user);
}
}
return fn(null, null);
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
console.log('serializing');
done(null, user.email);
});
passport.deserializeUser(function(id, done) {
console.log('de-serializing');
findByEmail(id, function(err, user) {
done(err, user);
});
});
passport.use(new SamlStrategy({
issuer: config.SAML.ISSUER_URL,
path: config.SAML.PATH,
entryPoint: config.SAML.ENTRY_POINT,
cert: config.SAML.CERTIFICATE,
}, function(profile, done) {
console.log('got profile');
console.log(profile);
if (!profile.email) {
return done(new Error("No email found"), null);
}
process.nextTick(function() {
console.log('Finding by email');
findByEmail(profile.email, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
console.log('new user');
users.push(profile);
return done(null, profile);
}
console.log('existing user');
return done(null, user);
})
});
}));
passport.protected = function protected(req, res, next) {
console.log('is isAuthenticated =' + req.isAuthenticated());
if (req.isAuthenticated()) {
return next();
}
res.redirect('/saml/invalid');
};
exports = module.exports = passport;
What is happening:
I can hit the URL: /saml/login
Gets redirected to the okta login page (where I have identity settings)
I login successfully
I'm redirected to the URL: /saml/callback with response:
{issuer:
{ _: 'http://www.okta.com/exkctyzcknbMikNjl0h7',
'$':
{ Format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity',
'xmlns:saml2': 'urn:oasis:names:tc:SAML:2.0:assertion' } },
sessionIndex: '_3acb290873febaf825cd',
nameID: 'ashutosh#myemail.com',
nameIDFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
nameQualifier: undefined,
spNameQualifier: undefined,
firstName: 'Ashutosh',
lastName: 'Pandey',
email: 'ashutosh#myemail.com',
getAssertionXml: [Function] }
In the /saml/callback URL, I can see value returned in req.user but
req.isAuthenticated() in saml-util is always returning false.

Passport doesn't save the session

I have read a lot about this issue but any answer doesn't work for me. I am working with React, Express and Passport to manage my authenticate routine. The authentication routine is fine, and it makes the redirect that I want. But, when I refresh any route, it says me that I am not authenticate. It seems that Passport doesn't save the session. Here my code:
Server.js
const lisaApp = express();
lisaApp.use(bodyParser.json())
lisaApp.use(bodyParser.urlencoded({ extended: false }))
lisaApp.use(cookieParser())
lisaApp.use(session({
secret: config.secret,
resave: false,
saveUnitialized: false
}))
lisaApp.use(passport.initialize())
lisaApp.use(passport.session())
passport.use(auth.localStrategy);
passport.serializeUser(auth.serializeUser);
passport.deserializeUser(auth.deserializeUser);
lisaApp.post('/login', (req, res, next) => {
const validationResult = validateLoginForm(req.body);
if (!validationResult.success) {
return res.status(400).json({
success: false,
message: validationResult.message,
errors: validationResult.errors
});
}
return passport.authenticate('local', (err, userData) => {
if (err) {
if (err.response.statusText === 'Unauthorized') {
return res.status(400).json({
success: false,
message: 'The password is not right'
});
}
return res.status(500).json({
success: false,
message: 'Wrong data'
});
}
console.log('is authenticated?: ' + req.isAuthenticated()) // Here always is false
return res.json({
success: true,
token,
message: 'Successful Login',
user: userData.data
});
})(req, res, next);
});
// I am using this function as a middleware to check if the user is authenticated, always is false. No matter if I put right data in the login form
function ensureAuth (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.status(401).send({ error: 'not authenticated' })
}
auth/index.js(passport routine)
var LocalStrategy = require('passport-local').Strategy;
var LisaClient = require('pos_lisa-client');
var config = require('../config');
var ClientApi = LisaClient.createClient(config.lisaClient);
exports.localStrategy = new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: false,
passReqToCallback: true
}, (req, username, password, done) => {
var authData = {
username,
password
}
// Here is a custom API, I pass the data that I need. This works fine
ClientApi.authenticate(authData, (err, token) => {
if (err) {
return done(err)
}
var token = token.data
ClientApi.getClient(username, (err, user) => {
if (err) {
return done(err)
}
user.token = token
return done(null, user)
})
})
})
exports.serializeUser = function (user, done) {
// The app never enters here
done(null, {
username: user.username,
token: user.token
})
}
exports.deserializeUser = function (user, done) {
// The app never enters here
ClientApi.getClient(user.username, (err, usr) => {
if (err) {
return done(null, err)
} else {
usr.token = user.token
done(null, usr)
}
})
}
Where I am wrong?
If you're using a custom authentication callback, you have to call req.logIn() to establish a session (or you can create one manually):
// Add this where you are console.log'ing `req.isAuthenticated()`
req.logIn(userData, function(err) {
if (err) return next(err);
console.log('is authenticated?: ' + req.isAuthenticated());
return res.json({
success: true,
token,
message: 'Successful Login',
user: userData.data
});
});
This is documented here (scroll down to "Custom Callback"):
Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response.

Resources