I created added some password reset functionality to my sign up flow that allows users to change their password, but when a user resets their password, they are unable to login with the new password and I receive a consoled Server Error. I'm wondering if this has to do with updating the user password outside of the passportjs logic or maybe because my password isn't hashed on the update?
Here is the console log:
GET /login 200 9.507 ms - 1793
GET /stylesheets/styles.css 304 1.093 ms - -
Database query triggered
Executing (default): SELECT `user_id`, `first_name` AS `firstName`, `last_name` AS `lastName`, `email`, `password`, `organization_id` AS `organizationId`, `reset_password_token` AS `resetPasswordToken`, `reset_password_expires` AS `resetPasswordExpires`, `createdAt`, `updatedAt` FROM `user` AS `user` WHERE `user`.`email` = 'test#gmail.com' LIMIT 1;
Server Error
POST /login 302 16.169 ms - 56
GET /login 200 10.926 ms - 1862
Here is my password update route:
var express = require('express');
var siteRoutes = express.Router();
var path = require('path');
var async = require('async');
var crypto = require('crypto');
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var moment = require('moment');
var url = require('url');
var passport = require(path.resolve(__dirname, '..', '..','./config/passport.js'));
var models = require('../models/db-index');
/*==== /RESET ====*/
siteRoutes.route('/reset/:token')
.get(function(req, res){
var urlPath = url.parse(req.url).pathname.split('/');
var urlToken = urlPath[2];
console.log(urlToken);
models.User.findOne({
where: {
resetPasswordToken: req.params.token,
resetPasswordExpires: {
$gt: moment().format('YYYY-MM-DD HH:mm:ss')
}
}
}).then(function(){
res.render('pages/app/reset-password.hbs',{
urlToken: urlToken
});
})
})
.post(function(req, res){
async.waterfall([
function(done){
models.User.update({
password: req.body.password,
resetPasswordToken: null,
resetPasswordExpires: null
}, { where: {
resetPasswordToken: req.body.token,
resetPasswordExpires: {
$gt: moment().format('YYYY-MM-DD HH:mm:ss')
}
}})
// Nodemailer
var transporter = nodemailer.createTransport(sgTransport(options));
var mailOptions = {
from: '"Tester" <test#test.com',
to: 'test#gmail.com', //Replace with Email
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + 'test#gmail.com' + ' has just been changed.\n'
};
transporter.sendMail(mailOptions, function(error, info){
if(error){
return console.log(error + 'During Post');
}
console.log('Message sent: ' + info.response);
})
}
])
res.redirect('/login');
});
Here is my login routing:
/*==== Login ====*/
siteRoutes.route('/login')
.get(function(req, res){
res.render('pages/site/login.hbs',{
error: req.flash('error')
});
})
.post(passport.authenticate('local', {
successRedirect: '/app',
failureRedirect: '/login',
failureFlash: 'Invalid email or password.'
}));
Here is the user model:
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true,
set: function(val) {
this.setDataValue('email', val.toLowerCase());
}
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
allowNull: true
},
resetPasswordToken: {
type: DataTypes.STRING,
field: 'reset_password_token'
},
resetPasswordExpires: {
type: DataTypes.DATE,
field: 'reset_password_expires'
}
}, {
freezeTableName: true,
classMethods: {
associate: function(db) {
User.belongsToMany(db.Organization, { through: 'member', foreignKey: 'organizationId'})
},
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
},
instanceMethods: {
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
},
});
return User;
}
Here is the passportjs logic:
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var models = require('../app/models/db-index');
/*==== Passport Configuration ====*/
// Serialize sessions
passport.serializeUser(function(user, done) {
console.log("User ID: " + user.user_id + " is serializing");
done(null, user.user_id);
});
passport.deserializeUser(function(user_id, done) {
models.User.find({where: {user_id: user_id}}).then(function(user){
console.log("User ID: " + user.user_id + " is deserializing");
done(null, user);
}).error(function(err){
done(err, null);
});
});
//Login logic
passport.use('local', new LocalStrategy({
passReqToCallback: true,
usernameField: 'email'
}, function(req, email, password, done) {
console.log("Database query triggered");
//Find user by email
models.User.findOne({
where: {
email: req.body.email
}
}).then(function(user) {
if (!user) {
done(null, false, { message: 'The email you entered is incorrect' }, console.log("Unknown User"));
} else if (!user.validPassword(password)){
done(null, false, console.log("Incorrect Password"));
} else {
console.log("User match");
done(null, user);
}
}).catch(function(err) {
console.log("Server Error");
return done(null, false);
});
}));
//Sign Up Logic
passport.use('local-signup', new LocalStrategy({
passReqToCallback: true,
usernameField: 'email'
}, function(req, email, password, done){
models.User.findOne({
where: {
email: email
}
}).then(function(existingUser){
if (existingUser)
return done(null, false, req.flash('error', 'Email already exists.'));
if (req.user) {
var user = req.user;
user.firstName = firstName;
user.lastName = lastName;
user.email = email;
user.password = models.User.generateHash(password);
user.save().catch(function(err){
throw err;
}).then(function(){
done(null, user, req.flash('error', 'All fields need to be filled in'));
});
} else {
var newUser = models.User.build({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: models.User.generateHash(password)
});
newUser.save().then(function(){
done(null, newUser);
}).catch(function(err){
done(null, false, console.log(err));
});
}
}).catch(function(e){
done(null, false, req.flash('error', 'All fields need to be filled in'),console.log(e.email + e.message));
})
}));
module.exports = passport;
Looks like you forget to generate your password again.
.post(function(req, res){
async.waterfall([
function(done){
models.User.update({
password: models.User.generateHash(req.body.password)
Related
I have some problem with passport and sequelize on my Node.js project.
In fact, when I want to signup, I'm using postman to call the /users/signup route and it show me : Unexpected '' and :
::1 - - [07/Aug/2018:09:20:19 +0000] "POST /users/signup HTTP/1.1" - - "-" "PostmanRuntime/7.2.0"
Executing (default): SELECT "user_id", "username", "name", "firstname", "email", "type", "location", "password", "createdAt", "updatedAt" FROM "users" AS "users" LIMIT 1;
There is the code :
/* CREATE an account */
app.post('/users/signup', (req, res) => {
db.users.find({ $or: [{ email: req.body.email }, { username: req.body.username }] }).then(user => {
if (err) {
return res.send(err);
}
if (user) {
if (user.email == req.body.email) {
return res.send("This email is already taken.")
}
return res.send("This username is already taken.")
}
else {
const data = {
username: req.body.username,
name: req.body.name,
firstname: req.body.firstname,
email: req.body.email,
location: req.body.location,
type: req.body.type,
password: req.body.password
};
db.users.create({
username: data.username,
name: data.name,
firstname: data.firstname,
email: data.email,
location: data.location,
type: data.type,
password: data.password
}).then(newUser => {
res.send("newUser saved to database")
// `req.user` contains the authenticated user.
//TODO : res.redirect('/profile/' + req.body.username);
})
.catch(err => {
console.log(err);
res.status(400).send("unable to save this newUser to database");
})
}
}).catch(err => {
console.log(err);
res.status(400).send("signup failed");
})
})
And my model (db.users) :
const bcrypt = require("bcrypt-nodejs");
module.exports = (sequelize, DataTypes) => {
// TABLE USERS
const Users = sequelize.define('users', {
user_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
firstname: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
type: {
type: DataTypes.STRING,
allowNull: false,
},
location: {
type: DataTypes.STRING
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
// methods ======================
// generating a hash
Users.generateHash = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
// checking if password is valid
Users.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
//hashing a password before saving it to the database
Users.beforeCreate('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
});
return Users;
};
My passport.js :
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
var db = require('../db')
// expose this function to our app using module.exports
module.exports = function (passport) {
var User = db.users;
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// 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.find({
where: { user_id: user_id }
})
.then(function (user) {
done(err, user);
}).catch(function (err) {
return done(err);
})
});
// =========================================================================
// 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('local-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)
return done(null, false, { message: 'User not found.' }); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, { message: 'Incorrect password.' }); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
return done(null, user);
});
}));
};
I can't find the source of the problem. I'm new with Node.js and I'm stuck since few hours. Is there someone here who can help me please?
After discussion, we found out that the first error comes from the beforeSave hook on the User db model, where a hash function was used missing a null parameter as third parameter, and thus bcrypt was throwing a "no callback passed" error. There is several other things going wrong, such as missuses of promises and callbacks, and I suggest going through a learning of promises, and double checking documentation and examples on how to use the libraries, such as Sequelize and bcrypt (how to generate and use a salt for example).
Previous answer
I think the problem comes from your 'local-login' strategy : passport uses callbacks, but sequelize uses promises, so your User.findOne callback is never called. Try something like that :
app.post("/users/signup", (req, res) => {
return db.users
.find({
$or: [
{
email: req.body.email
},
{
username: req.body.username
}
]
})
.then(user => {
if (user) {
if (user.email == req.body.email) {
return res.send("This email is already taken.");
}
return res.send("This username is already taken.");
} else {
return db.users
.create({
username: req.body.username,
name: req.body.name,
firstname: req.body.firstname,
email: req.body.email,
location: req.body.location,
type: req.body.type,
password: req.body.password
})
.then(newUser => {
res.send("newUser saved to database");
});
}
})
.catch(err => {
console.error(err);
res.status(400).send("unable to save this newUser to database");
});
});
Same thing for the route, and the deserialize :
app.post('/users/signup', (req, res) => {
db.users.find({ $or: [{ email: req.body.email }, { username: req.body.username }] })
.then(user=>{})
.catch(err=>{})
The Problem:
I'm trying to authenticate a user with passport-local strategy. I can successfully retrieve the user from the database, but when I try to redirect to ' / ' and initiate a new session, my server responds with 500 [object SequelizeInstance:Users]
Context:
I came across the 'connect-session-sequelize' node package and implemented it in my app.js:
const db = require('./models/db.js');
const userController = require('./controllers/user');
const myStore = new SequelizeStore({
db: db.sequelize,
table: 'Sessions'
});
app.use(cookieParser());
app.use(session({
secret: process.env.SESSION_SECRET,
store: myStore,
resave: false, // per the express-session docs this should be set to false
proxy: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/signup', userController.getSignup);
app.post('/signup', userController.postSignup);
db.sequelize.sync({
force: false,
}).then(() => {
app.listen(app.get('port'), () => {
console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
});
});
My routes handling the requests:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const db = require('../models/db.js');
const User = db.user;
passport.serializeUser((user, done) => {
console.log('serializing user: ', user);
done(null, user);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(user);
});
});
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
User.findOne({
where: { email: email.toLowerCase() },
}).then((user) => {
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
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.' });
});
});
}));
exports.postSignup = (req, res, next) => {
const errors = req.validationErrors();
const user = new User({
username: req.body.name,
email: req.body.email,
password: req.body.password
});
User.findOne({
where: { email: req.body.email }
}).then((existingUser) => {
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
user.save((err) => {
req.logIn(user, (err) => {
req.session.save(() => res.redirect('/'));
});
});
});
};
'Users' DB model:
const bcrypt = require('bcrypt-nodejs');
module.exports = (sequelize, DataTypes) => {
const Users = sequelize.define('Users', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
allowNull: false,
unique: true
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password_hash: {
type: DataTypes.STRING
},
password: {
type: DataTypes.VIRTUAL,
allowNull: false,
unique: false,
set(value) {
const that = this;
bcrypt.genSalt(10, (err, salt) => {
if (err) { return console.log('BCRYPT GEN SALT ERR:', err); }
bcrypt.hash(value, salt, null, (error, hash) => {
if (error) { return console.log('BCRYPT HASH ERR:', err); }
console.log('--> SEQ: BCRYPT hash SET', hash);
that.setDataValue('password', value);
that.setDataValue('password_hash', hash);
});
});
}
}
});
Users.prototype.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password_hash, (err, isMatch) => {
cb(err, isMatch);
});
};
return Users;
};
'Sessions' DB model:
module.exports = (sequelize, DataTypes) => sequelize
.define('Sessions', {
sid: {
type: DataTypes.STRING,
primaryKey: true
},
userId: DataTypes.STRING,
expires: DataTypes.DATE,
data: DataTypes.STRING(50000),
});
Server Response:
POST /login 302 234.474 ms - 46
Executing (default): SELECT "sid", "userId", "expires", "data", "createdAt",
"updatedAt" FROM "Sessions" AS "Sessions" WHERE "Sessions"."sid" =
'Jhmo9YA9MhwKEVa6zWxvvRQGdYoXmdSQ';
Executing (default): SELECT "id", "username", "email", "password_hash", "phone",
"age", "gender", "location", "createdAt", "updatedAt" FROM "Users" AS "Users"
WHERE "Users"."id" = 'c40d4cd6-4937-4a66-b785-d302e9fa6c40';
Executing (default): UPDATE "Sessions" SET "expires"='2018-05-10 06:31:42.797
+00:00',"updatedAt"='2018-05-09 06:31:42.797 +00:00' WHERE "sid" =
'Jhmo9YA9MhwKEVa6zWxvvRQGdYoXmdSQ'
[object SequelizeInstance:Users]
GET / 500 5.420 ms - -
The deserializeUser should call done with error first:
passport.serializeUser((user, done) => {
console.log('serializing user: ', user.id);
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(null, user);
}).catch(done);
});
Im trying to setup my Node JS API.
I have a User model :
// Dependencies
var restful = require('node-restful');
var mongoose = restful.mongoose;
var bcrypt = require('bcrypt');
// Schema
var userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true},
firstname: {
type: String,
required: true
},
lastname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
password: {
type: String,
required: true},
},
{
timestamps: true
});
// Saves the user's password hashed
userSchema.pre('save', function (next) {
var user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, 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();
});
});
} else {
return next();
}
});
// Use bcrypt to compare passwords
userSchema.methods.comparePassword = function(pw, cb) {
bcrypt.compare(pw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = restful.model('Users', userSchema);
I want to use passport with jwt for authentication :
// Dependencies
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var config = require('../config/database');
// Load models
var User = require('../models/user');
// Logique d'authentification JWT
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT');
opts.secretOrKey = config.secret;
opts.audience = 'localhost';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
Company.findById(jwt_payload._id, function(err, company) {
if (err) {
return done(err, false);
}
if (company) {
done(null, company);
} else {
done(null, false)
}
});
}));
};
And my route for authentication :
// User
router.post('/users/login', (req, res) => {
User.findOne({
email: req.body.email
}, (err, user) => {
if (err) throw err;
if (!user) {
res.json({success: false, message: 'Authentication failed. User not found.'});
} else {
// Check if passwords matches
user.comparePassword(req.body.password, (err, isMatch) => {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user, config.secret, {
expiresIn: 10080 // in seconds
});
res.json({success: true, token: 'JWT ' + token, user: {
id: user._id,
username: user.username,
email: user.email
}});
} else {
res.json({success: false, message: 'Authentication failed. Passwords did not match.'});
}
});
}
});
});
Everything work great on postman.
The token is correctly generated and signed with user's informations.
But i have a problem with the authentication on a protected route :
router.get('/users/profile', passport.authenticate('jwt', { session: false }), function(req, res) {
res.send('It worked! User id is: ' + req.user._id + '.');
});
Everytime, it gives me an "Unauthorized 401" Error.
I really dont know where is the problem, i think the problem is around jwtFromRequest, i also tried with Bearer but it also doesn't work...
I think a good option to avoid this kind of problems is to start from a base project that uses this authentication strategy, and after you have that working, modify it with your functionality.
Here you have an example with jwt authentication strategy and Refresh token implementation: https://solidgeargroup.com/refresh-token-autenticacion-jwt-implementacion-nodejs?lang=es
I am trying to make a Logged in or Signed In user to connect their account with facebook using passport.js facebook strategy, and save their profile photo, id, gender, timeline cover and token according the userSchema (as made in user.jsmodel shown below.
I tried many combinations but still either getting 500error from facebook, or if showing facebook auth, facebook can't return (the code combination, I tried) and save the object.
PS : I had entered correct callback URL in facebook
PPS: Please refer my UPDATED routes.js and updated passport.js below.
This is my routes.js file:
app.get('/auth/connect/facebook', passport.authenticate('facebook-connect', { authType: 'rerequest', scope: ['id', 'cover', 'gender', 'photos'] }));
app.get('/auth/connect/facebook/callback',
passport.authenticate('facebook-connect', {
successRedirect: '/profile/configure',
failureRedirect: '/profile/congigure'
// failureFlash: true
}));
My passport.js file of facebook-connect:
passport.use('facebook-connect', new FacebookStrategy({
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: configAuth.facebookAuth.callbackURL,
profileFields: ['id', 'cover', 'gender', 'photos'],
enableProof: true
},
function(token, refreshToken, profile, cb) {
process.nextTick(function() {
User.findOne({ 'local.facebook.id': profile.id }, function(err, user) {
if (err)
return cb(err);
if (user) {
return cb(null, false, req.flash('fbflash', 'This facebook user is already connected with an account at eBird.'));
} else {
user.local.facebook.id = profile.id;
user.local.facebook.token = token;
user.local.profile.gender = profile.gender;
user.local.profile.herobg = profile.cover;
user.local.profile.dp = user.local.profile.dp ? user.local.profile.dp : profile.photos[0].value;
if (user.local.profile.dp == '') {
if (user.local.profile.gender == 'male') {
user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487659283/an-av-3_jxrhwc.png';
}
if (user.local.profile.gender == 'female') {
user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487770814/female-avatar_vvyvtj.png';
}
}
user.save(function(err) {
if (err)
throw err;
return cb(null, user);
});
}
});
});
}));
My user.js model:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var DateOnly = require('mongoose-dateonly')(mongoose);
var shortid = require('shortid');
var uniqueValidator = require('mongoose-unique-validator');
var userSchema = mongoose.Schema({
_id: {
type: String,
default: shortid.generate
},
local: {
email: String,
username: { type: String, unique: true },
firstname: String,
surname: String,
name: String,
role: { type: String, default: 'user' },
department: String,
pno: Number,
password: String,
verified: { type: Boolean, default: false },
profile: {
dp: String,
createdAt: { type: Date, default: Date.now },
herobg: String,
location: String,
website: String,
gender: String,
birthday: DateOnly,
lastlogin: { type: Date },
notifications: {
name: String,
namedp: String,
type: { type: String },
date: { type: Date, default: Date.now },
read: { type: Boolean, default: false }
}
},
facebook: {
id: String,
token: String
}
}
});
userSchema.plugin(uniqueValidator, { message: '{Path}:{VALUE} is already taken.' });
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
// userSchema.methods.bellTimesAgo = function(date);
module.exports = mongoose.model('User', userSchema);
The error, it's throwing me:
The www.facebook.com page isn’t working
www.facebook.com is currently unable to handle this request.
HTTP ERROR 500
Any help would be appreciated,
Thanks.
UPDATE - 1
I read (& from passportjs docs) about passport.authorize() and updated my passport.js file accordig to passport.authorize() and also updated my routes, but still the same problem.
Here is my updated passport.js:
// Facebook Strategy Updated using authorize
passport.use(new FacebookStrategy({
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: configAuth.facebookAuth.callbackURL,
// profileFields: ['id', 'cover', 'gender', 'photos'],
// enableProof: true,
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
process.nextTick(function() {
if (!req.user) {
User.findOne({ 'local.facebook.id': profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
return done(null, false, req.flash('fbflash', 'This facebook user is already connected with an account at eBird.'));
} else {
user.local.facebook.id = profile.id;
user.local.facebook.token = accessToken;
user.local.profile.gender = profile.gender;
user.local.profile.herobg = profile.cover;
user.local.profile.dp = user.local.profile.dp ? user.local.profile.dp : profile.photos[0].value;
if (user.local.profile.dp == '') {
if (user.local.profile.gender == 'male') {
user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487659283/an-av-3_jxrhwc.png';
}
if (user.local.profile.gender == 'female') {
user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487770814/female-avatar_vvyvtj.png';
}
}
user.save(function(err) {
if (err)
throw err;
return done(null, user);
});
}
});
} else {
var user = req.user;
user.local.facebook.id = profile.id;
user.local.facebook.token = accessToken;
user.local.profile.gender = profile.gender;
user.local.profile.herobg = profile.cover;
user.local.profile.dp = user.local.profile.dp ? user.local.profile.dp : profile.photos[0].value;
if (user.local.profile.dp == '') {
if (user.local.profile.gender == 'male') {
user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487659283/an-av-3_jxrhwc.png';
}
if (user.local.profile.gender == 'female') {
user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487770814/female-avatar_vvyvtj.png';
}
}
user.save(function(err) {
if (err)
throw err;
return done(null, user);
});
}
});
}));
Here is my updated routes.js:
app.get('/auth/connect/facebook', passport.authorize('facebook', { authType: 'rerequest', scope: ['id', 'cover', 'gender', 'photos'] }));
app.get('/auth/connect/facebook/callback',
passport.authorize('facebook', {
successRedirect: '/profile/configure',
failureRedirect: '/profile/configure'
// failureFlash: true
})
);
Here is the snapshot of my app callback settings from Facebook:
Snapshot of the error, facebook keeps throwing in:
Passport.js documentation said:
Values for the scope option are provider-specific. Consult the provider's documentation for details regarding supported scopes.
If you check allowed permissions in Facebook documentation, you will not find such permissions as 'id', 'cover', 'gender', 'photos'. These items are part of a person's public profile.
So, you should change scope in routes.js from:
scope: ['id', 'cover', 'gender', 'photos']
to:
scope: ['public_profile']
or don't specify scope, because public_profile is default facebook permission.
P.S. I told about your "update 1" code version.
Taking inspiration from #anton-novik, I fixed the bug.
The problem was in my routes.js file. First have a look at my routes.js file above, and then follow the code below:
app.get('/auth/connect/facebook', ensureLoggedIn('/login'), passport.authorize('facebook', { authType: 'rerequest' }));
app.get('/auth/connect/facebook/callback',
passport.authenticate('facebook', {
successRedirect: '/profile',
failureRedirect: '/profile/settings',
failureFlash: true
})
);
There was no need of scope for the request I was making was already approved by Facebook for every app.
And then updated my passport.js file to look like this:
// // Facebook Strategy
passport.use(new FacebookStrategy({
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
profileFields: ['id', 'picture.type(large)', 'gender', 'cover'],
callbackURL: configAuth.facebookAuth.callbackURL,
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
process.nextTick(function() {
// User is not logged in yet
if (!req.user) {
User.findOne({ 'local.facebook.id': profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
if (!user.facebook.token) {
user.facebook.token = accessToken;
user.facebook.name = profile.displayName;
user.facebook.email = profile.emails[0].value;
user.save(function(err) {
if (err) throw err;
return done(null, user);
});
}
return done(null, user);
} else {
// User should be created here
// and saved to mongoose
}
});
}
//else user is logged in and needs to be merged
else {
console.log(profile); //display the returned json from fb
// Connect the user and save the details, since the user already exsists
var user = req.user;
user.local.facebook.id = profile.id;
user.local.facebook.token = accessToken;
user.local.profile.gender = profile.gender;
user.local.profile.dp = profile.photos[0].value;
user.local.profile.herobg = profile._json.cover.source;
user.save(function(err) {
if (err)
throw err;
return done(null, user);
});
}
});
}));
Hope, it may help someone. :)
I made an addition to my database modeling where I wanted to attach my users to a specific company. I followed the Sequelize documentation to create a one to one relationship between user and company, but it appears that it has screwed up my authentication a bit as the introduction of the code has been sending every submission of a login to the failureRedirect portion of my authentication code. Has anyone run into a similar issue?
User Model:
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.STRING,
unique: true
}
}, {
freezeTableName: true,
classMethods: {
associate: function(db) {
User.belongsTo(db.Organization);
}
}
});
return User;
}
Organization Model:
module.exports = function(sequelize, DataTypes) {
var Organization = sequelize.define('organization', {
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
autoIncrement: true,
primaryKey: true
},
organizationName: {
type: DataTypes.STRING,
field: 'organization_name'
},
admin: DataTypes.STRING,
members: DataTypes.STRING
},{
freezeTableName: true
});
return Organization;
}
Database Index:
var Sequelize = require('sequelize');
var path = require('path');
var config = require(path.resolve(__dirname, '..', '..','./config/config.js'));
var sequelize = new Sequelize(config.database, config.username, config.password, {
host:'localhost',
port:'3306',
dialect: 'mysql'
});
sequelize.authenticate().then(function(err) {
if (!!err) {
console.log('Unable to connect to the database:', err)
} else {
console.log('Connection has been established successfully.')
}
});
var db = {}
db.Organization = sequelize.import(__dirname + "/organization");
db.User = sequelize.import(__dirname + "/user");
db.FormApp = sequelize.import(__dirname + "/formapp");
db.User.associate(db);
db.FormApp.associate(db);
db.sequelize = sequelize;
db.Sequelize = Sequelize;
sequelize.sync();
module.exports = db;
Passport Authentication Routing:
var express = require('express');
var siteRoutes = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var models = require('../models/db-index');
/*==== Passport Configuration ====*/
// Serialize sessions
passport.serializeUser(function(user, done) {
console.log(user.user_id + "Serializing");
done(null, user.user_id);
});
passport.deserializeUser(function(user_id, done) {
models.User.find({where: {user_id: user_id}}).then(function(user){
done(null, user);
}).error(function(err){
done(err, null);
});
});
passport.use('local', new LocalStrategy({
passReqToCallback : true,
usernameField: 'email'
},
function(req, email, password, done) {
//Find user by email
models.User.findOne({
where: {
email: req.body.email,
password: req.body.password
}
}).then(function(user) {
return done(null, user);
}).catch(function(err) {
return done(null, false, req.flash('message', 'Email not found.'));
});
}
));
/*==== Index ====*/
siteRoutes.get('/', function(req, res){
res.render('pages/index.hbs');
});
siteRoutes.get('/flash', function(req, res){
req.flash('test', 'it worked');
res.redirect('/')
});
/*==== Login ====*/
siteRoutes.route('/login')
.get(function(req, res){
res.render('pages/login.hbs');
})
.post(passport.authenticate('local', {
successRedirect: '/app',
failureRedirect: '/login',
failureFlash: 'Invalid username or password.'
}));
siteRoutes.route('/sign-up')
.get(function(req, res){
res.render('pages/sign-up.hbs');
})
.post(function(req, res){
models.User.create({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: req.body.password
}).then(function() {
res.redirect('/app');
//res.redirect('/sign-up/create-organization');
}).catch(function(error){
res.send(error);
})
});
module.exports = siteRoutes;