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,
});
Related
I am trying to login an user (by a .js file, not via postman or browser) and maintain the session throughout the code. The login function seems to work fine, passport.authenticate, passportConfig and serializeUser get called, the response status is 200 OK, but when I try to call a function like sendFunds, the response is 'User is not authenticated'.
It works if I post the requests via postman. It seems like the login request gets a cookie and sends it automatically with the sendFunds request.
I guess that I need to do the same in my accountGenerator.js file, that means call the login method, get the cookies and send them with the sendFunds request, but I can't figure it out. How do I get them and do I need to manually add them to the express-session ?
Please :)
accountGenerator.js
async function loginUser(userLogin) {
return post('http://localhost:3002/api/user/login', userLogin)
}
function sendFunds(transferDetails) {
post('http://localhost:3002/api/user/sendFunds', transferDetails)
.then((res) => {
console.log(`Status: ${res.status}`);
}).catch((err) => {
console.error(err);
});
}
const loginResponse = await loginUser(userLogin);
export function loginUser(req, res) {
if (req.isAuthenticated()) {
res.status(200).send({
message: 'User is authenticated'
});
return;
}
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/error"
// failureRedirect: "/login"
})(req, res, next);
}
export function sendFunds(req, res) {
if (!req.isAuthenticated()) {
res.status(401).send({
message: 'User is not authenticated'
});
return;
}
req.body.secretKey = AUTHENTICATOR_SECRET_KEY;
post(SEND_FUNDS_API_URL, req.body)
.then((response) => {
res.status(200).send(response.data);
}).catch((err) => {
console.error(err);
res.status(500).send(err);
});
}
export function passportConfig() {
passport.use('local', new LocalStrategy(
async (username, password, done) => {
const response = await User.findOne({ name: username });
if (!response) {
return done(null, false, { message: 'Incorrect username.' });
}
const isValidPassword = await compare(password, response.password);
if (!isValidPassword) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, response);
}
))
}
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user)
})
})
app.use(expressSession({
store: new MongoStore({
mongoUrl: 'mongodb://127.0.0.1:27017/accountBankApp',
// mongooseConnection: mongoose,
ttl: 1 * 24 * 60 * 60, // = 365 days.
}),
secret: 'secret',
resave: true,
saveUninitialized: true,
}));
What worked for me was combining express-session with cookie-parser, even if cookie-parser is not required anymore in order for express-session to work.
Since version 1.5.0, the cookie-parser middleware no longer needs to be used for this module to work. This module now directly reads and writes cookies on req/res. Using cookie-parser may result in issues if the secret is not the same between this module and cookie-parser.
I'm trying to build an app with Node.js and Passport.js, but I don't want to continue if I can't nail down authentication. Right now I have passport-local set up and once I log in, by all accounts I should be getting an authenticated user on other routes. But I'm not. It looks like the session cookie being sent when logged in has the user info, but every other route sends a cookie that lacks the user info. I can't for the life of me figure out what I'm doing wrong.
here is how app is set up:
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
secure: false,
},
store: new FirestoreStore({
dataset: new Firestore(),
kind: "express-sessions",
}),
})
);
app.use(passport.initialize());
app.use(passport.session());
My route:
userRouter
.route("/api/v1/signup")
.post(jsonParser, async (req, res, next) => {
// console.log(req.body);
addUser(req.body);
res.status(200).json({ message: "success" });
});
userRouter.route("/api/v1/login").post(
jsonParser,
passport.authenticate("local", {
session: true,
}),
async (req, res, next) => {
res.status(200).json({ message: "success" });
}
);
and my passport.js
const LocalStrategy =
require("passport-local").Strategy;
const argon2 = require("argon2");
const {
getUserByEmail,
getUserById,
} = require("../database/users");
const initialize = (passport) => {
const authenticateUser = async (
email,
password,
done
) => {
const user = await getUserByEmail(email);
if (user === null) {
return done(null, false, {
message: "No user with that email",
});
}
// argon2.verify(user.email, password);
try {
const verifiedPassword =
await argon2.verify(
user.password,
password
);
if (verifiedPassword) {
// password match
console.log("success");
return done(null, user);
} else {
// password did not match
console.log("failure");
return done(null, false, {
message: "Password incorrect",
});
}
} catch (error) {
// internal failure
console.log(error, "error");
return done(error);
}
};
passport.use(
new LocalStrategy(
{
usernameField: "email",
},
authenticateUser
)
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
return done(null, getUserById(id));
});
};
module.exports = initialize;
When I login, my session gets this cookie:
Session {
cookie: {
path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: false
},
passport: { user: 1657255017610 }
}
On every other route when I want to check if the user is authenticated or not, I get this:
Session {
cookie: {
path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: false
}
}
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)
})
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.
I am having an issue with my app with the req.user persisting. After a successful login/serializeUser etc, I can see the req.user in the saved, and the application works as desired for about 1-2 minutes. After that, the req.user clears to undefined. I'm using currently using react and calling a method to the server to confirm there is a req.user on componentDidMount. I have no idea why and I'm pretty new to this.
In my server.js:
app.use(bodyParser.json())
// Sessions
app.use(
express-session({
secret: 'feedmeseymour',
cookie: { maxAge: 60000 },
store: new MongoStore({ mongooseConnection: dbConnection }),
resave: false,
saveUninitialized: false
})
)
// MIDDLEWARE
app.use(morgan('dev'))
app.use(
bodyParser.urlencoded({
extended: false
})
)
app.use(bodyParser.json())
app.use(express.static('public'));
app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
// Passport
app.use(passport.initialize())
app.use(passport.session())
My login route:
router.post(
'/',
function (req, res, next) {
console.log('Received login information. Username: ' + req.body.username)
const {errors, isValid } = validateLoginInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
next()
},
passport.authenticate('local', {failWithError: true }),
function (req, res, next) {
console.log('req.user in the backend: ' + req.user);
var userInfo = req.user
res.send(userInfo);
},
function (err, req, res, next) {
res.status(401).send({ success: false, message: err })
}
)
passport.serializeUser/deserialize methods:
passport.serializeUser((user, done) => {
console.log('*** serializeUser called, user: ')
console.log(user) // the whole raw user object!
console.log('---------')
done(null, { _id: user._id })
})
// user object attaches to the request as req.user
passport.deserializeUser((id, done) => {
console.log('DeserializeUser called')
User.findOne(
{ _id: id },
'username',
(err, user) => {
console.log('*** Deserialize user, user:')
console.log(user)
console.log('--------------')
done(null, user)
}
)
})
called on componentDidMount:
getUser() {
axios.get('/users').then(response => {
if (response.data.user) {
this.setUser(true, response.data.username, response.data.super);
}
else {
console.log('no user is logged in')
this.setUser(false, null, false);
}
})
}
Which calls this route in the back:
router.get('/', (req, res, next) => {
console.log('req.user:');
console.log(req.user);
console.log('------------');
console.log('req.session:');
console.log(req.session);
console.log('------------');
if (req.user) {
User.findOne({ _id: req.user._id }, (err, user) => {
if (err) {
console.log('logged user retrieval error: ', err)
} else if (user) {
console.log('found user from _id: ' + user);
res.json({ user: req.user, super: user.super })
}
})
} else {
res.json({ user: null })
}
})
req.user exists in the back for about 1-2 minutes and then it goes to undefined. I am storing the user in a store in mongodb, and I can see the session still exists there too.
the req.user is saved with information. In a minute, this will change to undefined:
req.user:
{ _id: 5b7ded93525742053a6dd155, username: 'admin' }
------------
req.session:
Session {
cookie:
{ path: '/',
_expires: 2018-09-09T07:10:22.902Z,
originalMaxAge: 60000,
httpOnly: true },
passport: { user: { _id: '5b7ded93525742053a6dd155' } } }
------------
cookie: { maxAge: 60000 }
That's 60.000 milliseconds, or 60 seconds. Exactly the 1 minute you're describing.
Try storing the user in the session object of the request. This way it works for me.