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

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.

Related

Why wont passport.authenticate do anything despite it seeming to have no errors

I am writing an Express app using Passport and PostgreSQL for account creation and login.
All of the database parts are working fine but when it gets to passport.authenticate nothing seems to happen.
I've tried modifying my code to better match examples I found online but haven't been able to get anything back from any of the print statements.
Here's some of the code from my index.js file:
const express = require("express");
const bodyParser = require("body-parser");
const session = require("express-session");
const { response } = require("express");
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
const db = require("./dbManager.js")
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static(__dirname + "/Public"));
app.use(session({
secret: "example",
saveUninitialized: true,
resave: true
}));
app.use(passport.initialize());
app.use(passport.session());
//Uses pasport to authenticate user
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
}, function (email, password, done) {
console.log("Logging in ", email);
//This sends the query to the db
db.findUser(email).then(function (row) {
if (!row) {
return done(null, false, "Sorry that user doesn't exist");
} else {
//This compairs the passwords
bcrypt.compare(password, row.password, function (ex, result) {
//Checks if they match
if (result == true) {
//Sets up user
done(null, row.id);
} else {
return done(ex, false, "Incorrect password");
}
})
}
}).catch(function (ex) {
console.error(ex);
return done(ex, false);
});
}));
passport.serializeUser(function (user, done) {
console.log(user, done);
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
db.deserializeUser(id).then(function (row) {
console.log("User found id is:", row.id);
done(null, row.id)
}).catch(function (ex) {
console.error(ex);
})
});
app.listen(3000, function () {
console.log('Server running on port 3000');
});
app.get("/createAccount", function (req, res) {
res.sendFile(__dirname + "/Public/Create Account/createAccount.html");
});
app.post("/createAccount", function (req, res) {
const email = req.body.email;
const password = req.body.password;
bcrypt.hash(password, 10, function (ex, hashedPassword) {
if (ex != null) {
console.log(ex);
}
db.addUser(email, hashedPassword).then(function () {
console.log("New user added");
res.status(204).send("user added");
}).catch(function (ex) {
console.error(ex);
res.status(403).send(ex);
});
});
});
app.get("/logIn", function (req, res) {
res.sendFile(__dirname + "/public/Log In/login.html");
});
app.post("/logIn", function (req, res) {
console.log("Logging user in");
passport.authenticate("local", function (ex, user, info) {
console.log("Authentication");
if (ex) {
console.error(ex);
res.status(403).send(ex);
} else if (!user) {
console.log(info);
res.status(400).send(info);
} else {
req.logIn(user, function (ex) {
if (ex) {
console.error(ex);
res.status(400).send(ex);
} else {
console.log("User now authenticated");
res.status(200).send("Success");
}
})
}
});
});
Here is code from my createAccount.js script:
//This will run when the form is submitted
$("#submitBtn").click(function() {
//prepares data to be sent
body= {
email:$("#email")[0].value,
password:$("#password")[0].value
};
//This sends the post request to the server
$.post("/createAccount",body).done(accountCreated(body)).fail(accountNotCreated);
});
function accountCreated(body) {
console.log("User Added");
console.log(body.email);
//Sends post request to log user in
$.post("/logIn",body).done(function() {
console.log("You are now logged in");
window.location.replace("/");
}).fail(function(ex) {
console.error(ex);
});
}
function accountNotCreated(ex) {
console.error(ex);
}
The log output for this is as follows:
Logging user in
New user added
I'm not sure why this is in the wrong order either.
The problem here is that you are running the accountCreated function before you make the post to /createAccount. This line is the culprit
$.post("/createAccount",body).done(accountCreated(body)).fail(accountNotCreated);
This line of code is supposed to be setting up the accountCreated function to run after the call to $.post finishes successfully. However, because you're including the brackets after the function it's executing the function immediately and using whatever is returned as the handler for when they post completes.
You can fix this by wrapping the call to accountCreated in an anonymous function.
$.post("/createAccount",body).done(function() {
accountCreated(body)
}).fail(accountNotCreated);
I hope that helps.

Passport JS req.user is undefined

I've seen this question posted else where, however, none of those solutions appeared to work for me. Upon trying to console log the req.session and req.user, I have a return of the session as shown below, however, the req.user returns as undefined. I believe there is an issue when serializing the user as VS code alerts me that the "'id' property does not exist on type user" in the passport.serialUser function, but it console logs user.id properly (the object id of my user document).
If anyone is aware of what might be cause of the user.id property not existing/the user being undefined I would appreciate it.
passport.js file:
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
const User = require("../models/User");
const verifyCallback = (username, password, done) => {
User.findOne({ username: username })
.then((user) => {
if (!user) {
return done(null, false);
}
// Validate Password
bcrypt.compare(password, user.password).then((isMatch) => {
if (isMatch) {
return done(null, user);
} else {
return done(null, false);
}
});
})
.catch((err) => {
done(err);
});
};
const strategy = new LocalStrategy(verifyCallback);
passport.use(strategy);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((userId, done) => {
User.findById(userId)
.then((user) => {
done(null, user);
})
.catch((err) => done(err));
});
index.js file:
session({
secret: process.env.SECRET,
resave: false,
saveUninitialized: true,
store: sessionStore,
cookie: {
maxAge: 1000 * 60 * 60 * 24,
},
})
);
// Passport Auth Middleware
const passportConfig = require("./config/passport");
// Initialize Passport and Use Session for Serialize/Deserialization
app.use(passport.initialize());
app.use(passport.session());
app.use((req, res, next) => {
console.log(req.session);
console.log(req.user);
next();
});
console log:
cookie: {
path: '/',
_expires: 2021-11-17T02:08:23.650Z,
originalMaxAge: 86400000,
httpOnly: true
}
}
undefined
User document example:
{"_id":{"$oid":"6186c13beb18d33d5088f7b2"},
"username":"coolguy9",
"password":"$2b$13$4p5apH8Q8k8hP4WpCNt6/O40M9I0jlkG.LXIE3d/V89Kmtmk1plxa",
"firstname":"Bob",
"lastname":"Woodhull",
"team":"Warehouse",
"createdAt":{"$date":{"$numberLong":"1636221243904"}},
"updatedAt":{"$date":{"$numberLong":"1636221243904"}},
"__v":{"$numberInt":"0"}}```
Solved the issue. In my loginAPI.js file on my front end, which contains the axios instance, I needed to include the option withCredentials: true.
Example:
baseURL: "http://localhost:8000/api/",
timeout: 1000,
withCredentials: true,
});

Passport isAuthenticated() always returns false?

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.

PassportJs session not working when wrapping athenticate function

so i have this problem with passportJS local strategy
it can't persist logins when i wrap passport.athenticate inside a function
when i try to login on the route /loginT then go to /whoami it works just fine
but when i use /login it responds with user object , but when i hit /whoami it log undefined
i think i made a mistake somewhere but i can't find what's wrong with it
thank you
here's passport config
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy
const UserDB = require('../models/users')
const { UserStatus } = require('../middleWares')
const signIn = new LocalStrategy (
function( username , password , callback) {
UserDB.findOne(
{$or : [{username:username},{email:username}]},
async function(err,user){
if (err) callback({
success:false,
code:403,
message:err,
}, false) ;
if (!user)
callback({
success:false,
code:403,
message:'Username not found',
}, false)
if (!(await user.verifyPassword(password)))
callback ({
success:false,
code:403,
message:'Username or Password is incorrect',
},false)
if(user.status === UserStatus.PENDING_EMAIL_ACTIVATION) {
callback({
success:false,
code:93,
message:'Please Activate your account first',
},false,)
}
callback(null,user)
}
)
}
)
passport.use('local-signin',signIn)
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
UserDB.findById(id, function (err, user) {
done(err, user);
});
});
module.exports = passport;
userRoute file
router.post('/loginT',passport.authenticate('local-signin',{
failureRedirect:'/Login',
successRedirect:'/'
}))
router.get('/whoami',async (r,res) => {
console.log(r.user)
return res.send({user:r.user})
})
router.post('/login',async (r,s)=>{
passport.authenticate('local-signin',{},function(err,_user,info){
if(err) s.send(err)
if(_user) s.send({
success:true,
code:200,
data:{..._user._doc,
password:null,
_id:null
}})
})(r,s)
})
server.js
const passport = require('./passport');
app.use(require('express').json())
app.use(cookieParser());
app.use(require('express-session')({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
*** SOLVED ***
i forgot to use request.logIn(user, function)
so i had to make the login route as next
router.post('/login',async function (r,s){
passport.authenticate('local-signin',{},function(err,_user,info){
if(err) s.send(err)
r.logIn(_user,function(error) {
if(error) return s.send({
success:false,
code:403,
message:error,
})
return s.send({
success:true,
code:200,
data:{..._user._doc,
password:null,
_id:null
}})
})
})(r,s)
})

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.

Resources