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.
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.
Server returning "Error: Failed to serialize user into session" when attempting to use register user using Passport.js and AJAX. The code works when not using AJAX from the registration page, but I want to have a popup that processes the registration too.
passport.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local.popsignup', new LocalStrategy({
usernameField: 'popUsername',
passwordField: 'popPassword',
passReqToCallback: true
}, (req, email, password, done) => {
User.findOne({'email':email}, (err, user) => {
if(err){
return done(err);
}
if(user){
return done(null, false, req.flash('error', 'Email already exists, please login.'))
}
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.preferences = preferences;
newUser.save((err) => {
return done(null, newUser);
});
});
}));
user router
module.exports = (app, passport) => {
app.post('/popover', function(req, res, next) {
passport.authenticate('local.popsignup', function(err, user, info) {
if (user !== undefined) {loggedIn = true} else {loggedIn = false};
var errors = req.flash('error')
req.login(user, function(err) {
if (err) return next(err);
res.status(200).send({errors: errors, loggedIn: loggedIn});
});
})(req, res, next);
});
}
view ajax js
$.ajax({
url: "/popover",
type: "POST",
data: $('#popform-tag').serialize(),
success: function(data){
if(data.loggedIn == false) {
$("#email_alert").text(data.errors[0]).show();
} else {
$('#popover').modal('hide')
localStorage.setItem('preferences', 1);
}
//console.log(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log("Status: " + textStatus);
console.log("Error: " + errorThrown);
}
})
Before adding ajax, send some parameters via PostMan to check if your API request-response is working as expected. If yes, only then use Ajax based on API type (post, put etc..)
Also instead of serialize() try using Ajax in this way -
var data = {
someKey: 'someValue'
}
$.ajax({
url: '/popover', // route path
type: 'POST',
data: JSON.stringify(data),
headers: {
'Content-Type': "application/json",
},
'success': function (data, textStatus, request) {
// do something
},
'error': function (err) {
// do some error handling
}
});
I am persistently getting 'unauthorized' error while authenticating using a JWT. Below is my controller code:
exports.loginPost = async (req, res) => {
winston.info('Calling loginPost()...');
passport.authenticate('local', { session: false }, (err, user, info) => {
if (err) {
return utils.errorHandler(res, err);
} else if (!user) {
return utils.errorHandler(res, {
statusCode: 403,
message: 'Incorrect username or password.'
});
}
const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
//req.user = user;
return res.json({ user, token });
// req.login(user, { session: false }, (err) => {
// if (err) {
// res.send(err);
// }
// // generate a signed json web token with the contents of user object and return it in the response
// const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
// //req.user = user;
// return res.json({ user, token });
// });
})(req, res);
};
exports.isUserLoggedIn = async (req, res) => {
let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(login);
//return res.status(200).json(req.user);
};
and passport.js strategy script is as follows:
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, async function (username, password, cb) {
//this one is typically a DB call. Assume that the returned user object is pre-formatted and ready for storing in JWT
try {
let user = await userService.getUserWithPassword(username, password);
console.log("passport.js: ",user);
if (!user || user.walletKey !== password) {
throw { statusCode: 403, message: 'Incorrect username or password.' };
}
// // purge password field
// delete user.currentPassword;
return cb(null, user, { message: 'Logged In Successfully' });
} catch (err) {
cb(err);
}
}));
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: sharedSecret,
passReqToCallback: true
},
async function (req, jwtPayload, cb) {
// Return user object from the JWTPayload
try {
let user = await userService.getUserWithPassword(jwtPayload.walletName, jwtPayload.walletKey);
console.log("passport.js: ",user);
req.user = user
return cb(null, user); //jwtPayload
} catch(err){
return cb(err,false);
}
}
));
I am able to generate token successfully, however, on calling isUserLoggedIn method using Bearer Token, it's prompting me unauthorized error. I am not making an traditional db call to login, instead I am just creating account in a Hyperledger-Indy pool nodes. Using swagger express middleware on a Node.js app.
Adding isUserLoggedIn method script below:
exports.isUserLoggedIn = async (req, res) => {
//let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(req.user);
//return res.status(200).json(req.user);
};
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
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.