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
Related
I'm getting that error, and besides asking where it is on my code I would like to know if there is a better way to log errors in order to get to know where it is being generated on my code, since I can't figure it out by reading the error log. Here are the code and the error. Thanks! StackOF is making me add more details, I don't know what to write
/////// app.js
//Function one : setting up the LocalStrategy
passport.use(
new LocalStrategy((username, password, done) => {
User.findOne({ username: username }, (err, user) => {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: "Incorrect username" });
}
if (bcrypt.compare(password, user.password, (err, res) => {
if (res) {
// passwords match! log user in
return done(null, user)
} else {
// passwords do not match!
return done(null, false, { message: "Incorrect password" })
}
})) {
return done(null, false, { message: "Incorrect password" });
}
return done(null, user);
});
})
);
//Functions two and three: Sessions and serialization
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
//Route for logging in
app.post(
"/log-in",
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/"
})
);
app.use(function(req, res, next) {
res.locals.currentUser = req.user;
next();
});
app.get("/", (req, res) => {
res.render("index", { user: req.user });
});
app.get("/sign-up", (req, res) => res.render("sign-up-form"));
app.post("/sign-up", (req, res, next) => {
bcrypt.hash(req.body.password, 10, (err, hashedPassword) => {
// if err, do something
if (err) {
return next(err);
}
// otherwise, store hashedPassword in DB
// eslint-disable-next-line no-unused-vars
const user = new User({
username: req.body.username,
password: hashedPassword
}).save(err => {
if (err) {
return next(err);
}
return res.redirect("/");
});
});
});
app.get("/log-out", (req, res) => {
req.logout();
res.redirect("/");
});
app.listen(3000, () => console.log("app listening on port 3000!"));
And the error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:558:11)
at ServerResponse.header (C:\Users\Atom\Desktop\passportJSOdin\node_modules\express\lib\response.js:771:10)
at ServerResponse.location (C:\Users\Atom\Desktop\passportJSOdin\node_modules\express\lib\response.js:888:15)
at ServerResponse.redirect (C:\Users\Atom\Desktop\passportJSOdin\node_modules\express\lib\response.js:926:18)
at complete (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\middleware\authenticate.js:266:26)
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\middleware\authenticate.js:275:15
at pass (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:431:14)
at Authenticator.transformAuthInfo (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:453:5)
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\middleware\authenticate.js:272:22
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\http\request.js:52:7
at C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\sessionmanager.js:26:5
at pass (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:277:43)
at serialized (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:286:7)
at C:\Users\Atom\Desktop\passportJSOdin\app.js:63:5
at pass (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:294:9)
at Authenticator.serializeUser (C:\Users\Atom\Desktop\passportJSOdin\node_modules\passport\lib\authenticator.js:299:5)
There is a glaring issue in the logic of your code here:
if (bcrypt.compare(password, user.password, (err, res) => {
if (res) {
return done(null, user)
} else {
return done(null, false, { message: "Incorrect password" })
}
})) {
return done(null, false, { message: "Incorrect password" });
}
return done(null, user);
bcrypt.compare() is an asynchronous function, which means it does not return the result it produces to its calling code, but instead passes it to its callback function once available. So the continuation of the code - ie everything that should happen once the result is available - should be wrapped in its callback function.
You got this right. But then you are duplicating the logic of what happens in this callback function in the calling code based on the returned value of bcrypt.compare() - which is irrelevant.
The end result is that the done() function will end up being called twice.
You should just do:
bcrypt.compare(password, user.password, (err, res) => {
if (res) {
return done(null, user)
} else {
return done(null, false, { message: "Incorrect password" })
}
})
As far as logging errors, a stack trace is as good as it gets to help pinpoint the issue.
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();
});
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.
I am building app using MEAN STACK. I want to use passport-local authentication for my login form. But a the time of form submission i am getting POST http://localhost/login 404 (Not Found) please have a look of my code below This is my controller:
lyfee.controller('loginCtrl', ['$scope', '$http', function($scope, $http) {
$scope.user = {};
$scope.login = function() {
// var data = {User: $scope.user }
//console.log($scope.user);
console.log("login function call");
$http.post('/login', $scope.user);
console.log("login request send");
}
}]);
and this is my server.js :
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.getUserByusername(username, function(err, user) {
if (err) throw err;
if (!user) {
return done(null, false, {
message: 'Unknown USER'
});
}
User.comparePassword(password, user.password, function(err, isMatch) {
if () throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {
message: 'Invalid password'
});
}
});
});
}));
app.post('/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}),
function(req, res) {
res.redirect('/');
});
in which file should i write getUserByusername and comparePassword function ? and what is mistake i am doing please correct it and give me some suggestion.
In your model suppose that User.js write functions like this:
/**
* Check the user's password
*/
dbSchema.methods.comparePassword = function(candidatePassword, cb) {
var status = this.password.localeCompare(candidatePassword.trim());
if (status != 0) {
return cb(err);
}
cb(null, true);
};
then use the function like this
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({
username: username
}, function(err, user) {
if (err) throw err;
if (!user) {
return done(null, false, {
message: 'Unknown USER'
});
}
/**
* Check the user's password
*/
User.comparePassword(password, user.password, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {
message: 'Invalid password'
});
}
});
});
}));
I have struggled so much with Passport because the custom callback feature simply does not work. Here's how I initialize passport:
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var bodyParser = require('body-parser');
// Set up passport
passport.use('local', new LocalStrategy({
usernameField: 'userId',
passwordField: 'password'
}, function (userId, password, cb) {
users.findByUserId(userId, function (err, user) {
if (err) {
return cb(err);
}
if (!user) {
return cb(null, false);
} else {
if (user.password !== password) {
return cb(null, false);
}
return cb(null, user);
}
});
}));
passport.serializeUser(function (user, cb) {
cb(null, user.userId);
});
passport.deserializeUser(function (id, cb) {
users.findByUserId(id, function (err, user) {
if (err) { return cb(err); }
cb(null, user);
});
});
Then, this is how it's SUPPOSED to log a user in when a user posts to '/login':
exports.tryLogin = function (req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return res.status(500).json({ success : false, message : 'Internal server error: ' + err.message }); }
if (!user) { return res.status(500).json({ success : false, message : 'user not found'}); }
req.logIn(user, function(err) {
if (err) { return res.status(500).json({ success : false, message : 'Internal server error: ' + err.message }); }
return res.status(200).json({ success : true });
});
})(req, res, next);
}
Here's how I have to do it because it never detects the user. The 'user' in the above method is always undefined. I have to construct it from the request myself to get it to work:
exports.tryLogin = function (req, res, next) {
var user = {
userId: req.body.userId,
password: req.body.password
}
req.logIn(user, function (err) {
if (err) {
return res.status(500).json({ success : false, message : 'Internal server error: ' + err.message });
}
return res.status(200).json({ success : true, message : 'authentication succeeded' });
});
}
This works, but feels wrong because I'm never calling passport.authenticate. Is this ok or should I be banging my head against the wall trying to get the custom callback to work as defined in the documentation?
Sam
Yes this is wrong approach.
This code means:
exports.tryLogin = function (req, res, next) {
var user = {
userId: req.body.userId,
password: req.body.password
}
req.logIn(user, function (err) {
if (err) {
return res.status(500).json({ success : false, message : 'Internal server error: ' + err.message });
}
return res.status(200).json({ success : true, message : 'authentication succeeded' });
});
}
that You do not check permissions, You practically login anybody without password check.