I am using Sails.js and trying to use Passport.js with a REST API.
But I am facing the following error when I try to call my login function in my controller:
/Users/Michael/Development/DictationProject/sails/20151010/dictee/node_modules/passport/lib/http/request.js:44
if (!this._passport) { throw new Error('passport.initialize() middleware not in use'); }
^
Error: passport.initialize() middleware not in use
at IncomingMessage.req.login.req.logIn (/Users/Michael/Development/DictationProject/sails/20151010/dictee/node_modules/passport/lib/http/request.js:44:34)
at /Users/Michael/Development/DictationProject/sails/20151010/dictee/api/controllers/AuthController.js:20:17
at Strategy.strategy.success (/Users/Michael/Development/DictationProject/sails/20151010/dictee/node_modules/passport/lib/middleware/authenticate.js:194:18)
at verified (/Users/Michael/Development/DictationProject/sails/20151010/dictee/node_modules/passport-local/lib/strategy.js:83:10)
at /Users/Michael/Development/DictationProject/sails/20151010/dictee/config/passport.js:53:18
My config/http.js file is configured as below (Session is loaded before passportInit and passportSession):
passportInit : require('passport').initialize(),
passportSession : require('passport').session(),
flash : require('connect-flash'),
order: [
'startRequestTimer',
'cookieParser',
'session',
'passportInit',
'passportSession',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'$custom',
'flash',
'router',
'www',
'favicon',
'404',
'500'
],
Don't understand what is wrong...
EDIT:
It seems to be coming from passport.use() in config/passport.js :
passport.use('login', new LocalStrategy({
passReqToCallback : true,
usernameField: 'email',
passwordField: 'password'
},
function(req, email, password, done) {
console.log('In passport.use login');
// check in mongo if a user with email exists or not
User.findOne({ 'email' : email }, function (err, user) {
// In case of any error, return using the done method
if (err) {
console.log('Error passport.use login');
return done(err);
}
// Username does not exist, log error & redirect back
if (!user) {
console.log('User Not Found with email '+email);
return done(null, false, req.flash('message', 'User Not found.'));
}
console.log('One user matching this email address found');
// User exists but wrong password, log the error
bcrypt.compare(password, user.password, function (err, res) {
if (!res) {
console.log('Invalid Password');
return done(null, false, req.flash('message', 'Invalid Password'));
}
// User and password both match, return user from
// done method which will be treated like success
console.log('One user matching this email address and password found');
var returnUser = {
name: user.name,
email: user.email,
createdAt: user.createdAt,
id: user.id
};
console.log('Returning User: ' + returnUser);
return done(null, returnUser,
req.flash('message', 'Logged In Successfully')
);
});
});
}
));
Following the recommendations from Sails.js creator on passport: https://github.com/balderdashy/sails/pull/1224
Issue is solved by putting the Passport middleware in the config/policies.js file:
var passport = require('passport');
module.exports.policies = {
'*': [
// Initialize Passport
passport.initialize(),
// Use Passport's built-in sessions
passport.session()
]
}
I am using Sails.js and tryong to use Passport.js with a REST API
You do it wrong. You don't need to call initialize() and session() methods. If you really have REST API then it stateless and doesn't store information about users in session.
My middleware configuration looks like this and everything works great with passport and many other strategies like passport-facebook-token and passport-google-plus-token.
order: [
'compress',
'keepAlive',
'bodyParser',
'$custom',
'router',
'404',
'500'
]
Related
Assuming I have required passport, bcrypt, express-sessions and mysql correctly this code is supposed to add the user info to the session. I cannot for the life of me work out where it is going wrong.
App.js file
passport.use('local', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true //passback entire req to call back
}, function(req, email, password, done) {
connection.query("SELECT * FROM accounts WHERE email = ?", [email], async(err, rows, fields) => {
//if an error occurrs in mysql
if (err) {
return done(null, false)
connection.end()
}
//if there are no matching entries
if (!rows.length) {
console.log('invalid email')
return done(null, false)
connection.end()
}
//if the passwords don't compare
if (!(await bcrypt.compare(password, rows[0].password))) {
console.log('incorrect password')
done(null, false)
connection.end()
}
//if none of that happens
req.session.user = rows[0]
console.log('logged in')
connection.end()
});
}));
//handles de/serialization of the user data (all integrated from passport docs)
passport.serializeUser(function(user, done) {
return done(null, user.id)
console.log(user.id + "serialized")
});
passport.deserializeUser(function(id, done) {
connection.query("SELECT * FROM accounts WHERE id = " + id, function(err, rows) {
console.log('done' + rows[0])
return done(err, rows[0])
});
});
Auth route code
router.post('/auth', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/account'
}));
The code logs the user in, which is logged in the console, then logs them out again with a failure redirect to /account. I don't know where the authentication is going wrong, thanks for any replies in advance!
You can achieve the same using express-session..
install express-session: npm i express-session --save
Require it in your node application: var session = require('express-session');
Configure: app.use(session({ secret: "YourSecretKeyGoesHere", saveUninitialized: true, resave: true }));
And then check your session..
I am trying to send a welcome email after a user signs up with nodemailer and also adding the user to mongodb with passport.authenticate on the same post route. I am able to get this to work separately i.e. either sending email or adding the user to the database but can't seem to get them to work together. I am new to nodejs and would really appreciate any help. Here is the route I am trying to get to work:
router.post('/signup', function(req, res,next) {
async.waterfall([
function(done) {
passport.authenticate('signup', {
successRedirect: '/',
failureRedirect: '/signup',
failureFlash : true
});
},
function(user, done) {
var transporter = nodeMailer.createTransport({
service: 'SendGrid',
auth: {
user: 'user',
pass: 'password'
}
});
var mailOptions = {
to: user.email,
from: 'me#gmail.com',
subject: 'Welcome to the site',
html: '<p> This is html, did I render correctly?</p>'
};
transporter.sendMail(mailOptions, function(err){
done(err);
});
}
], function(err) {
res.redirect('/signup');
});
});
Here is the signup strategy with passport:
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport){
passport.use('signup', new LocalStrategy({
usernameField : 'email',
passReqToCallback : true
},
function(req, email, password, done) {
findOrCreateUser = function(){
// find a user in Mongo with provided username
User.findOne({ 'email' : email }, function(err, user) {
// In case of any error, return using the done method
if (err){
req.flash('error','Email Already Exists',err.message);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username:');
return done(null, false, req.flash('error','Email Already Exists'));
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.password = createHash(password);
newUser.email = req.param('email');
newUser.firstName = req.param('firstName');
newUser.lastName = req.param('lastName');
// save the user
newUser.save(function(err) {
if (err){
console.log('Error in Saving user: '+err);
return done(null, false, req.flash('error',err.message));
}
console.log('User Registration succesful');
return done(null, newUser);
});
}
});
};
// Delay the execution of findOrCreateUser and execute the method
// in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
}
Thanks in advance for the help!
Why don't you move the email sending logic to the passport signup strategy?
I am trying to implement passport local strategy in my project following this blog.
I have created the passportjs file in config .
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
console.log("User Authenticating...." + username + 'and' + password);
User.findOne({
username: username
}).exec(function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Unknown user ' + username
});
}
if (user.password != password) {
return done(null, false, {
message: 'Invalid password'
});
}
return done(null, user);
});
}
));
i have updated the httpjs as per suggestion
middleware: {
passportInit : require('passport').initialize(),
passportSession : require('passport').session(),
order: [
'startRequestTimer',
'cookieParser',
'session',
'passportInit',
'passportSession',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'router',
'www',
'favicon',
'404',
'500'
]
}
/***************************************************************************
* *
* The number of seconds to cache flat files on disk being served by *
* Express static middleware (by default, these files are in `.tmp/public`) *
* *
* The HTTP static cache is only active in a 'production' environment, *
* since that's the only time Express will cache flat-files. *
* *
***************************************************************************/
// cache: 31557600000
};
Now when I am trying to call local strategy from my Auth controller as
login: function (req, res) {
var user = req.body.user;
// check user
if (!user) {
//send bad request
return res.status(500).json({payload : {}, message : "Undefined user"});
}
var username = (user.username !== undefined)? user.username : false;
var password = (user.password != undefined)? user.password : false;
// Grab user fields.
if (!username || !password) {
//send bad request
return res.status(400).json({payload : {}, message : "Invalid username or password"});
}
console.log("before authenticated");
console.log("username="+username);
console.log("password="+password);
passport.authenticate('local', function(err, user, info) {
console.log("User Authenticated");
if (err) {
console.log("error=errpr");
return res.status(400).json({payload : {error: err}, message : info.message});
}
if (!user) {
console.log("error=user");
return res.status(400).json({payload : {error: err}, message : info.message});
}
console.log("User Authenticated2");
_authTokenRequestCb(user,req,res);
})(req,res);
},
It never authenticate the user and the console log statement in Paasportjs.
console.log("User Authenticating...."+username+'and'+password);
also never executed.I am not getting any error also .
Can anybody suggest where I am getting it wrong.
Update:I have found in this stack question "If 'email' or 'password' are not provided passport just automatically fails. I guess some documentation of that would have been useful!."
But as console log statement giving me the username and passwoprd (in the registerblock
console.log("before authenticated");
console.log("username="+username);
console.log("password="+password);
So I am sure this is not the case.
Thanks
Did you create a policy file to say which controller actions are to be authenticated? Passport doesn't have a policy to deny access to a controller. For this, you have to create another policy.
Just have a look at this answer: Passport authentication not working in Sails JS application
Let me know if this doesn't help.
I've asked a similar question before, but I noticed it was in the Javascript section.
I have more specific ideas of what might be going wrong now, as well.
Basically, req.session.passport is empty in my logs. Whenever I start navigating around my site, req.user becomes undefined because the session doesn't have Passport's logged in user anymore.
I would like to know if anyone knows how to solve this? Maybe it's just an error in the configuration of Passport, or the entire Express setup?
App.js:
var express = require("express"),
bodyParser = require("body-parser"),
mongodb = require("mongodb"),
mongoose = require("mongoose"),
uriUtil = require("mongodb-uri"),
morgan = require("morgan"),
session = require("express-session"),
passport = require("passport"),
flash = require("connect-flash"),
ip = "hidden",
port = process.env.PORT || 80
var app = express()
app.disable("x-powered-by")
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(morgan("dev")); // log every request to the console
// required for passport
app.use(session({
secret: "hidden",
key: 'asdasdasd',
cookie: { maxAge: 60000, secure: false },
resave: true,
saveUninitialized: false
})); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
app.set("view engine", "jade")
app.use(express.static(__dirname + "/views"))
require("./includes/passport")(passport)
require("./includes/subject")
require("./includes/user")
Passport.js:
var LocalStrategy = require("passport-local").Strategy,
User = require("./user"),
bCrypt = require('bcrypt-nodejs')
module.exports = function(passport) {
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user._id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// =========================================================================
// LOCAL SIGNUP ============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called "local"
passport.use('signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : "email",
passwordField : "password",
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function() {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ "email" : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash("message", "Dit e-mail-adres is al bezet"));
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.email = email;
newUser.password = createHash(password);
newUser.firstname = req.param('firstname');
newUser.lastname = req.param('surname');
newUser.year = parseInt(req.param('year'));
newUser.study = req.param('study');
newUser.courses = req.param('courses');
newUser.phone = req.param('phone');
newUser.availability = req.param('availability');
newUser.description = req.param('descText');
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use("login", new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : "email",
passwordField : "password",
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ "email" : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user) {
console.log('No user found with email ' + email)
return done(null, false, req.flash('message', 'Gebruiker niet gevonden')); // req.flash is the way to set flashdata using connect-flash
}
if (!isValidPassword(user, password)){
console.log('Incorrect Password');
return done(null, false, req.flash('message', 'Onjuist wachtwoord')); // redirect back to login page
}
// all is well, return successful user
return done(null, user);
});
}));
var isValidPassword = function(user, password){
return bCrypt.compareSync(password, user.password);
}
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
};
The routes:
api.post("/signup", passport.authenticate("signup", {
successRedirect: "/profile",
failureRedirect: "/",
failureFlash: true
}))
api.post("/login", passport.authenticate("login", {
successRedirect: "/profile",
failureRedirect: "/login"//,
failureFlash: true
}))
router.get("/", function(req, res) {
// serve index.html
res.render("index", {
title: 'Home',
user: req.user,
message: req.flash("message")
})
})
It works on the page that is accessed directly after logging in, which I control as follows:
router.get("/profile", isLoggedIn, function(req, res) {
res.render("profile", {
title: 'Gebruikersprofiel van ' + req.user.firstname + " " + req.user.lastname,
user: req.user // get the user out of session and pass to template
})
})
function isLoggedIn(req, res, next) {
console.log(req.session)
// if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next()
// if they aren't redirect them to the home page
res.redirect("/login")
}
So far, I've tried adding middleware to add req.user to req.session, and doing the same thing in the login POST. Also I've tried changing the order in which I initialize the middleware in app.js. I am using the new express-session version, without CookieParser, as I read that CookieParser is no longer needed.
If anyone can help me in any way, it would be much appreciated! I've been stuck for a while (as have others).
The problem was not anything I did wrong in setting up the session, or Passport in general, but rather in my links.
I read somewhere that someone was accidentally working in multiple domains (his platform was apparently multi-server), and that made me look through my links this morning.
Apparently, I was linking to my website with www. prefixed, but the session was initialized where there was no www. in front of the URL. I saw this in the cookies.
The solution was, therefore, to link through the website consistently, either having www. prefixed everywhere or nowhere.
i would like to test this method using mocha and i don't know where to start ?
the route :
app.post('/signup', passport.authenticate('local-signup', {
failureRedirect: '/#/',
failureFlash: true
}),
function(req, res) {
res.jsonp(req.user);
});
and here is the definition of my service :
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
pseudoField: 'pseudo',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, email, password, pseudo, done) {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function() {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({
'local.email': email
}, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
console.log('That email is already taken');
//var newUser = new User();
return done(404, null);
// return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
// if there is no user with that email
// create the user
console.log('creation new user');
var newUser = new User();
// set the user's local credentials
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.local.pseudo = pseudo;
console.log(pseudo);
console.log(newUser.local);
// save the user
console.log('going to save in bdd');
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
i just need some help to know how i could test this methode and how to call it.