passportjs +facebook + current User - javascript

Have some issues figuring out how to access the current user through my facebook login. I'm using passportJS, Node, express. I think that my 'user' is not staying logged in but I have no way to check. I'll upload what I have and thank you for anyone looking over it - really appreciate it.
route.js
app.get('/auth/facebook', passport.authenticate('facebook', { scope : ['email', 'public_profile', 'user_friends'] }));
// handle the callback after facebook has authenticated the user
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
successRedirect : '/profile',
failureRedirect : '/'
}));
// route for logging out
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
};
function isLoggedIn(req, res, next) {
// 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('/');
}
passport.js
passport.use(new FacebookStrategy({
// pull in our app id and secret from our auth.js file
clientID : configAuth.facebookAuth.clientID,
clientSecret : configAuth.facebookAuth.clientSecret,
callbackURL : configAuth.facebookAuth.callbackURL,
// profileFields: ['id', 'name','picture.type(large)', 'emails', 'username', 'displayName', 'about', 'gender']
},
// facebook will send back the token and profile
function(token, refreshToken, profile, done) {
// asynchronous
process.nextTick(function() {
// find the user in the database based on their facebook id
User.findOne({ 'facebook.id' : profile.id }, function(err, user) {
// if there is an error, stop everything and return that
// ie an error connecting to the database
if (err)
return done(err);
// if the user is found, then log them in
if (user) {
return done(null, user); // user found, return that user
} else {
// if there is no user found with that facebook id, create them
var newUser = new User();
// set all of the facebook information in our user model
newUser.facebook.id = profile.id; // set the users facebook id
newUser.facebook.token = token; // we will save the token that facebook provides to the user
newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; // look at the passport user profile to see how names are returned
newUser.facebook.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first
console.log(profile);
console.log(user);
console.log('it is working');
// save our user to the database
newUser.save(function(err) {
if (err)
throw err;
// if successful, return the new user
return done(null, newUser);
});
}
});
});
})); // end of FacebookStrategy
};
server.js
require('./config/passport')(passport); // pass passport for configuration
// // required for passport
app.use(session({ secret: 'ilovescotchscotchyscotchscotch' })); // 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
This is my first stackoverflow post so apologies in advanced if I have insulted anyone with the format.

Your user should be serialized some how. For example:
// set up cookie parser and session
var cookieParser = require('cookie-parser');
var session = require('express-session');
app.use(cookieParser());
app.use(session({
secret: 'mysecret',
resave: true,
saveUninitialized: false
}));
// passport init
app.use(passport.initialize());
app.use(passport.session());
// Lets user information be stored and retrieved from session
passport.serializeUser(function(user, done) {
done(null, user.facebook.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err,user){
if(err) done(err);
done(null,user);
});
});
Then you can access the user object via req.user. For example a test route could be:
app.get('/user', function(req, res, next) {
res.send(req.user);
});
Good luck!

You can also do it in another way :
router.get('/auth/facebook', function(req, res, next) {
passport.authenticate('facebook', { scope : ['email', 'public_profile', 'user_friends'] } , function(err, user, info) {
if(err) return res.status(400).send(err);
if(user._id){
req.logIn(user, function(err) {
if (err) { return next(err); }
//redirect where you want
return res.redirect("");
});
}
})(req, res, next);
})
req.logIn is a function which is required with user obj to create session and maintain . Otherwise passport will never able to maintain session of user.

Related

Passport callback isn't called

I build an application using Passport lib using this tutorial (part of it).
Note, I don't need a registration, only login form.
One of the issues is that my LocalStrategy callback is never called. For storing I use mongo:
mongoose.connect(dbConfig.url, {
useMongoClient: true
});
//dbConfig
module.exports = {
'url' : 'mongodb://localhost/passport'
}
Login route looks like this:
module.exports = function(app, passport) {
app.get('/login', function(req, res) {
res.render('login', {
message: req.flash('loginMessage')
});
});
app.post('/login', passport.authenticate('login', {
successRedirect: '/', // redirect to the secure profile section
failureRedirect: '/login', // redirect back to the signup page if there is an error
failureFlash: true // allow flash messages
}));
}
Passport logic is:
module.exports = function(passport) {
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('login', new LocalStrategy({
passReqToCallback: true
}, function(req, username, password, done) {
console.log('start'); // never called
User.findOne({
'local.email': email
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, req.flash('loginMessage', 'No user found.'));
}
if (!user.validPassword(password)) {
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
}
return done(null, user);
});
}));
};
console.log('start'); is never called, although passport.authenticate('login' ...) is called.
What can be an issue?
I finally fixed it and everything works. In case anyone face the same issues I'm posting here several problems and solutions.
The req.body was empty in my app.post, because I didn't add body parser. Fixed it with:
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
Username field was empty all the time because I named it as email and passport expected username. Fixed with:
new LocalStrategy({
usernameField: 'email', // this parameter
passReqToCallback: true
}

Express Passport.js Success redirect doesn't load page, request keeps pending

I am trying to do a basic username/password authentication using passport.js and passport local.
While failureRedirect does exactly what it is supposed to do, (redirects to a specified page), successRedirect keeps pending with the request for its specified page, and after some time, it returns empty response.
http://www.deviantpics.com/VdG
As you can see in this picture, when it is requesting dashboard, it says that its size is 0B, but when I go on that dashboard without redirecting it says it has 1.6B.
I have looked all over Stackoverflow, and I couldn't find an answer that would help me.
Could you please check my code and suggest something before I go berserk?
This is passport load code
//set expression
var expressSession = require('express-session');
app.use(expressSession({
secret: credentials.session.secret
}));
//set passport
var passport = require('passport');
var localStrategy = require('./strategies/auth/local.js');
passport.use('local', localStrategy);
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.use(passport.initialize());
app.use(passport.session());
routes.js
module.exports = function(app) {
//main
app.get('/', main.home);
app.get('/login', main.login);
app.get('/signup', main.signup);
app.post('/login', auth.loginLocal);
app.post('/signup', main.checkSignup);
//user
app.get('/user/dashboard', user.dashboard);
app.get('/user/addmemory', user.addMemory);
app.get('/user/memory', user.memory);
login function
exports.loginLocal = passport.authenticate('local', {
successRedirect: '/user/dashboard',
failureRedirect: '/login'
});
local strategy
var localAuthStrategy = new LocalStrategy(function(username, password, done) {
User.findOne({
username: username
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Incorrect username'
});
}
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Incorrect password'
});
}
return done(null, user);
});
});
dashboard function
exports.dashboard = function(req, res) {
res.render('user/dashboard', {
layout: 'user'
});
};
I found an answer to my question, the problem was that the User model was not defined in the script where serializeUser and deserializeUser were defined.
I could not figure out what was going on, because I did not define any action in catch all handler, so a thing to remember, make sure to have catch all handler defined to know what is happening
app.use(function(err, req, res, next) {
console.log(err);
});
I was facing the same problem as your a few days back and what I found was that I forgot to put parenthesis at the end of the serializeUser and deserializeUser functions.
I was actually using the passport-local-mongoose package for the respective functions. But it must be noted that in app.use() the functions are called and executed for all the template files so, we do use parenthesis with the names of the functions.

req.session.passport is empty: req.user undefined

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.

Passport authorization callback redirects to beginning (Box)

I'm attempting to authenticate a user using Box.com OAuth2.0. I make the initial call and login which redirects to my callback url with the authorization code. At this point my server handles the callback using passport but for some reason it returns a 302 and redirects to the beginning of the oauth authentication process.
//box authentication routes
app.get('/api/box', passport.authorize('box'));
// the callback after box has authorized the user
app.get('/api/box/callback', passport.authorize('box', {
successRedirect: '/',
failureRedirect: '/login'
})
);
I verified that my route is being called by using my own handler and the request data seems to be correct. Box returns a 200 and the url contains the authorization code.
app.get('/api/box/callback', function(req, res) {
console.log('auth called')
});
This is my passport strategy:
passport.use(new BoxStrategy({
clientID: config.box.clientID,
clientSecret: config.box.clientSecret,
callbackURL: config.box.callbackURL,
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
process.nextTick(function() {
if(!req.user) {
// try to find the user based on their google id
User.findOne({ 'box.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
// if a user is found, log them in
return done(null, user);
} else {
// if the user isnt in our database, create a new user
var newUser = new User();
// set all of the relevant information
newUser.box.id = profile.id;
newUser.box.accessToken = accessToken;
newUser.box.refreshToken = refreshToken;
newUser.box.name = profile.name;
newUser.box.email = profile.login;
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
} else {
// user already exists and is logged in, we have to link accounts
var user = req.user;
// update the current users box credentials
user.box.id = profile.id;
user.box.accessToken = accessToken;
user.box.refreshToken = refreshToken;
user.box.name = profile.name;
user.box.email = profile.login;
// save the user
user.save(function(err) {
if (err)
throw err;
return done(null, user);
});
}
});
}
));
Would appreciate any insight as to what might be causing this redirect behavior.
It ended up being an instance of PeerServer that was somehow causing the redirect.

How can I log an end user into my single page application, and redirect them to the Single page application?

How can I log an end user into my single page application, and redirect them to the Single page application, using Backbone.js. Express.js and passport.js.
I have passport, mongo.db, express, and backbone all working on my application. However, upon successful login, I want to load up the single page, backbone js, web application. As of right now, I have the login system working fine. But when I go to redirect the user to the application, after successful login, for some reason it just redirects right back to the login page. I have used console log to make sure that there is nothing wrong with the login code on the server side and everything is working fine. The strange thing is that when I open chrome developer tools, and look at the headers and response, I'm getting the correct index page back, but it's not loading in the browser and the Url remains http://localhost:3000/login.
Here is the code that I suspect must somehow be the culprit:
See edit #2
I've tried both res.sendfile(__dirname + '/public/index.html'); and res.render('index', { user: req.user }); in my base ('/') route but neither of them seems to be loading the index.html. Note that public/index.html and the ejs index are essentially the same files. I started off having my entire app load up on the client side but now I'm trying to move the main index.html file over to the server side so that it can be password protected.
Please let me know if there is any questions at all that will help me better explain this problem, or if there is anymore code that you would like to see. I really want to get this thing figured out.
Edit As you can see with this screenshot of my browser, I have nothing more than simple form. When that form is submitted, I do get back the desired page in the response, but it's not loading up in the browser. I'm also getting a failed to load resource error in the console, but it's failing to load /login for some reason - even though I'm trying to load index.
Edit #2 As much as I hate to paste endless blocks of code, I think the only way to resolve this issue is to paste endless blocks of code.
So here is server.js in all it's glory - minus some of the api routes that are irrelevant:
var express = require('express')
, http = require('http')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy
, bcrypt = require('bcrypt')
, SALT_WORK_FACTOR = 10
, mongoose = require('mongoose')
, path = require('path');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
server.listen(3000);
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs-locals'));
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use( express.errorHandler({ dumpExceptions: true, showStack: true }));
});
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
//Connect to database
var db = mongoose.connect( 'mongodb://localhost/attorneyapp' );
/*
|-------------------------------------------------------------------------------
| Schemas
|-------------------------------------------------------------------------------
*/
var userSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true},
});
// Bcrypt middleware
userSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if(err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
});
// Password verification
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
});
};
var Client = new mongoose.Schema({
first_name: String,
last_name: String,
status: String,
});
var Case = new mongoose.Schema({
case_name: String,
status: String,
});
/*
|-------------------------------------------------------------------------------
| Models
|-------------------------------------------------------------------------------
*/
var User = mongoose.model( 'User', userSchema );
var ClientModel = mongoose.model( 'Client', Client );
var CaseModel = mongoose.model( 'Case', Case );
// Seed a user
// var user = new User({ username: 'bob', email: 'bob#example.com', password: 'secret' });
// user.save(function(err) {
// if(err) {
// console.log(err);
// } else {
// console.log('user: ' + user.username + " saved.");
// }
// });
// 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) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) {
console.log('error: ', err);
return done(err);
}
if (!user) {
console.log('Unknown user ', username);
return done(null, false, { message: 'Unknown user ' + username });
}
user.comparePassword(password, function(err, isMatch) {
if (err){
console.log('error: ', err);
return done(err);
}
if(isMatch) {
console.log('it is a match');
return done(null, user);
} else {
console.log('invalid password');
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
/*
|-------------------------------------------------------------------------------
| User
|-------------------------------------------------------------------------------
*/
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
//
/***** This version has a problem with flash messages
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
*/
// POST /login
// This is an alternative implementation that uses a custom callback to
// acheive the same functionality.
app.get('/', function(req, res){
console.log('base router is being called');
// res.sendfile(__dirname + '/public/index.html');
res.render('index');
});
app.get('/login', function(req, res) {
return res.render('login');
});
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
console.log('NOT USER', info.message);
req.session.messages = [info.message];
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) { return next(err); }
console.log('YES USER is loged in');
// return res.sendfile(__dirname + '/public/index.html');
return res.redirect('/');
});
})(req, res, next);
});
app.get('/users', function(req, res){
return User.find( function( err, users ) {
if( !err ) {
return res.send( users );
} else {
return console.log( err );
}
});
});
Also, not sure if this is relevant, but here is my directory and file structure.
Try to return empty value. (undefined, actually)
I mean
res.redirect('/');
return;
instead of
return res.redirect('/');
And don't use res.render('index'... and res.sendfile(... in login! It response data from index.html to client on /login page!

Categories