I have tried almost every method known to stack Overflow but my issue is persisting as the days goes by and I'm really hoping for some help here. As you can tell from the title, Passport-local does not call deserializeUser for some odd reason. My files are set up as shown below.
User.js
const express = require('express');
const app = express();
const router = express.Router();
// const db = require('../config/db');
const session = require('express-session');
const SqlDbStore = require('express-mysql-session')(session);
const passport = require('passport');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const cookieParser = require('cookie-parser')
//----------------------------------------- BEGINNING OF PASSPORT MIDDLEWARE AND SETUP ---------------------------------------------------
app.use(session({
key: 'session_cookie_name',
secret: 'session_cookie_secret',
store: new SqlDbStore({
host: 'localhost',
port: 3306,
user: 'xxxxxxxxxx',
password: 'xxxxxxxxx',
database: 'xxxxxxxxxx',
}),
resave: false,
saveUninitialized: false,
cookie:{
maxAge:1000*60*60*24,
secure: false
}
}));
app.use(passport.initialize());
app.use(passport.session());
require('../config/ppc.js')(passport);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
/*passport middleware*/
function genPassword(password) {
var salt=crypto.randomBytes(32).toString('hex');
var genhash=crypto.pbkdf2Sync(password, salt, 10000, 60, 'sha512').toString('hex');
return {salt:salt, hash:genhash}
};
function checkAuthentication(req,res,next){
if(req.isAuthenticated()){
//req.isAuthenticated() will return true if user is logged in
next();
} else{
res.redirect("/login");
}
};
//----------------------------------------- END OF PASSPORT MIDDLEWARE AND SETUP ---------------------------------------------------
router.post('/register', (req, res) => {
const firstName = req.body.firstName;
const lastName = req.body.lastName;
const email = req.body.email;
const saltHash = genPassword(req.body.password);
const salt = saltHash.salt;
const hash = saltHash.hash;
db.query('SELECT * FROM Users WHERE UsersEmail = ? ', [email], (err, results) => {
if (err){
console.log(err)
} else if (results.length > 0) {
res.json({ message: 'Email is already registered!' });
} else {
db.query('INSERT INTO Users (UsersFirstName, UsersLastName, UsersEmail, UsersPasswordHash, UsersPasswordSalt) VALUES (?, ?, ?, ?, ?)', [firstName, lastName, email, hash, salt], (err, results) => {
if (err){
console.log(err);
};
res.send(results);
});
}
})
});
router.post('/login', function(req, res, next){
// console.log(req.body);
passport.authenticate('local', function(err, user){
// console.log('Error: ' + err);
// console.log('User ID: ' + user.id + '\nUser Email: ' + user.email + '\nUser Salt: ' + user.salt + '\nUser Hash: ' + user.hash);
// console.log(err, user);
if(err) {
res.json({message: err.message});
}
if(!user) {
res.json({message: info.message});
}
req.logIn(user, (err) => {
if(err) {
res.json({message: err.message});
} else {
return res.redirect('/');
}
});
})(req, res, next);
});
module.exports = router;
PPC.js
module.exports = function (passport) {
const LocalStrategy = require('passport-local').Strategy;
const db = require('./db');
const crypto = require('crypto');
db.connect((err) => {
if (!err) {
console.log("BD Connected");
} else {
console.log("BD Conection Failed");
console.log(err.message);
res.json({message: err.message})
}
});
const customFields={
usernameField: 'email',
passwordField:'password',
};
/*Passport JS*/
const verifyCallback=(email,password,done)=>{
db.query('SELECT * FROM Users WHERE UsersEmail= ?', [email], function(error, results, fields) {
if (error) {
console.log('query error: ' + error);
return done(error);
}
if(results.length==0) {
return done(null,false, { loggedIn: false, message: 'Account is not recognized.'});
}
const isValid=validPassword(password, results[0].UsersPasswordHash, results[0].UsersPasswordSalt);
user={id:results[0].UsersID, email:results[0].UsersEmail, hash:results[0].UsersPasswordHash, salt:results[0].UsersPasswordSalt};
if(isValid) {
return done(null,user, { loggedIn: true, email: email});
} else{
return done(null,false, { loggedIn: false, message: 'Password is incorrect.'});
}
});
};
const strategy = new LocalStrategy(customFields, verifyCallback);
passport.use(strategy);
passport.serializeUser((user,done)=>{
console.log('Inside serialize');
done(null, user.id);
});
passport.deserializeUser((userId, done) => {
console.log('Inside deserialize');
db.query('SELECT * FROM Users WHERE UsersID = ?', [userId], function(error, results) {
done(null, results[0]);
});
});
function validPassword(password, hash, salt){
const hashVerify=crypto.pbkdf2Sync(password, salt, 10000, 60, 'sha512').toString("hex");
return hash === hashVerify;
};
}
I am totally unsure as to what I did wrong here. One thing I will say is, when I change all app.use() to router.use() in my User.Js, I got a consider upgrading MySQL error when I am using mysql2. Regardless, I don't think mysql is my issue because I am able to register to the DB fine using mysql2 and app.use. I believe my issue most likely lies in the router.post(/login).
I ended up just moving to my own encryption method which uses bcrypt. It was the easiest and securest way to go about this process especially with out wasting to much time. If I ever find the solution in the future, I’ll post it on GitHub and will post the link here.
More information on Bcrypt.
Related
Although I am pretty new to software development, I have an intermediate understanding of exporting and importing data between files. However, for some reason, when trying to create a config file for Passport.js and trying to import that file into my Index.js file, I hit a brick wall. Something tells me that it could be a rookie mistake, but I'm almost sure that I probably need to move some functions over. Any advice would genuinely do. My code is below for a visual view.
Passport.JS
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const mysql = require('mysql2');
const crypto = require('crypto');
const session = require('express-session');
const SqlDbStore = require('express-mysql-session')(session);
const db = require('./db');
app.use(session({
key: 'session_cookie_name',
secret: 'session_cookie_secret',
store: new SqlDbStore({
host: 'localhost',
port: 3306,
user: 'root',
password: 'xxxxxxxxxx',
database: 'xxxxxxxxxx',
}),
resave: false,
saveUninitialized: false,
cookie:{
maxAge:1000*60*60*24,
}
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.json());
app.use(express.static('public'));
app.use(express.static(__dirname + '/views'));
db.connect((err) => {
if (!err) {
console.log("BD Connected");
} else {
console.log("BD Conection Failed");
console.log(err.message);
}
});
const customFields={
firstNameField: 'usersFirstName',
lastNameField: 'usersLastName',
emailField: 'usersEmail',
passwordField:'usersPassword',
confirmPasswordField:'usersConfirmedPassword'
};
/*Passport JS*/
const verifyCallback=(email,password,done)=>{
connection.query('SELECT * FROM USER WHERE usersEmail= ?', [email], function(error, results, fields) {
if (error) {
console.log('query error: ' + error);
return done(error);
}
if(results.length==0) {
return done(null,false, {message: 'Account is not recognized.'});
}
const isValid=validPassword(password, results[0].EncryptHash, results[0].EncryptPassword);
user={id:results[0].ID, email:results[0].usersEmail, hash:results[0].EncryptHash, password:results[0].EncryptPassword};
if(isValid) {
return done(null,user);
} else {
return done(null,false, {message: 'Password is incorrect.'});
}
});
};
const strategy = new LocalStrategy(customFields, verifyCallback);
passport.use(strategy);
passport.serializeUser((user,done)=>{
console.log("Inside serialize");
done(null, user.id);
});
passport.deserializeUser(function(userId, done) {
console.log('deserializeUser');
connection.query('SELECT * FROM User WHERE ID = ? ', [userId], function(error, results) {
done(null, results[0]);
});
});
/*middleware*/
function validPassword(password, hash, salt){
const hashVerify=crypto.pbkdf2Sync(password, salt, 10000, 60, 'sha512').toString("hex");
return hash === hashVerify;
};
function genPassword(password) {
var salt=crypto.randomBytes(32).toString('hex');
var genhash=crypto.pbkdf2Sync(password, salt, 10000, 60, 'sha512').toString('hex');
return {salt:salt, hash:genhash}
};
function checkAuthentication(req,res,next){
if(req.isAuthenticated()){
//req.isAuthenticated() will return true if user is logged in
next();
} else {
res.redirect("/login");
}
};
Index.JS
const express = require('express');
const router = express.Router();
const db = require('../config/db');
const passport = require('../config/passport');
const routes = require('')('passport');
router.post('/register', (req, res) => {
const firstName = req.body.firstName;
const lastName = req.body.lastName;
const email = req.body.email;
const password = req.body.password;
const saltHash = genPassword(password);
const salt = passport.saltHash.salt;
const hash = passport.saltHash.hash;
db.query('SELECT * FROM Users WHERE UsersEmail = ? ', [email], (err, results) => {
if (err){
console.log(err)
} else if (results.length > 0) {
res.json({ message: 'Email is already registered!' });
} else {
db.query('INSERT INTO Users (UsersFirstName, UsersLastName, UsersEmail, UsersPasswordHash, UsersPasswordSalt) VALUES (?, ?, ?, ?, ?)', [firstName, lastName, email, hash, salt], (err, results) => {
if (err){
console.log(err);
};
res.send(results);
});
}
})
});
router.post('/login', passport.authenticate('local'));
module.exports = {router, passport};
Update:
Question is reposted with more information on Export and Import Passport.JS Issues.
More information is located on Export and Import Passport.JS Issues. I assumed since this post was getting no attention at all, I may have either been lacking details of my issue and explaining everything else or just had not details at all.
I am using passport.js specifically the local-strategy for authentication with my next.js application.
Making requests to my data store is fine, and auth, authorization works as well. But I'd like access to the req.user for another route so I can have access to that users._id.
As the passport docs indicate:
By default, if authentication fails, Passport will respond with a 401
Unauthorized status, and any additional route handlers will not be
invoked. If authentication succeeds, the next handler will be invoked
and the req.user property will be set to the authenticated user.
This is my Next.js app's server.js file:
var express = require('express');
require('dotenv').config();
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var cors = require('cors');
var nextJS = require('next');
var session = require('express-session');
var mongoose = require('mongoose');
var MongoStore = require('connect-mongo')(session);
var path = require('path');
var bodyParser = require('body-parser');
var auth = require('./lib/auth');
var HttpStatus = require('http-status-codes');
var compression = require('compression');
var helmet = require('helmet');
var PORT = process.env.PORT || 8016;
var { isBlockedPage, isInternalUrl } = require('next-server/dist/server/utils');
function NODE_ENVSetter(ENV) {
var environment,
environments = {
production: () => {
environment = process.env.MONGODB_URI;
console.log(`We are currently in the production environment: ${environment}`);
return environment;
},
test: () => {
environment = process.env.TEST_DB_DSN;
console.log(`We are currently in the test environment: ${environment}`);
return environment;
},
default: () => {
environment = process.env.DEVELOPMENT_DB_DSN;
console.log(`We are currently in the development environment: ${environment}`);
return environment;
}
};
(environments[ENV] || environments['default'])();
return environment;
}
var db = NODE_ENVSetter('development');
function start() {
const dev = process.env.NODE_ENV !== 'production';
const app = nextJS({ dev });
const server = express();
// const proxy = createProxyMiddleware(options);
app
.prepare()
.then(() => {
mongoose.connect(db, {
useNewUrlParser: true,
useUnifiedTopology: true
});
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${db}`);
})
.on('error', err => {
console.log(`Connection error: ${err.message}`);
});
})
.catch(err => {
console.error(err);
});
server.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', '*'); // enables all the methods to take place
return next();
});
server.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', true);
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
next();
});
server.use(morgan('dev'));
server.set('view engine', 'html');
server.use(express.static(path.join(__dirname + 'uploads')));
server.use('/uploads', express.static('uploads'));
server.use(cors());
server.use(cookieParser());
server.use(bodyParser.json());
server.use(
session({
secret: 'very secret 12345',
resave: true,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
})
);
server.use(bodyParser.urlencoded({ limit: '50mb', extended: false }));
server.use(auth.initialize);
server.use(auth.session);
server.use(compression());
server.use(helmet());
server.use('/users', require('./users'));
Here I've included a log to check the req.user object
server.use((req, res, next) => {
console.log('req.user', req.user);
next();
});
// Redirect all requests to main entrypoint pages/index.js
server.get('/*', async (req, res, next) => {
try {
// #NOTE code duplication from here
// https://github.com/zeit/next.js/blob/cc6fe5fdf92c9c618a739128fbd5192a6d397afa/packages/next-server/server/next-server.ts#L405
const pathName = req.originalUrl;
if (isInternalUrl(req.url)) {
return app.handleRequest(req, res, req.originalUrl);
}
if (isBlockedPage(pathName)) {
return app.render404(req, res, req.originalUrl);
}
// Provide react-router static router with a context object
// https://reacttraining.com/react-router/web/guides/server-rendering
req.locals = {};
req.locals.context = {};
const html = await app.renderToHTML(req, res, '/', {});
// Handle client redirects
const context = req.locals.context;
if (context.url) {
return res.redirect(context.url);
}
// Handle client response statuses
if (context.status) {
return res.status(context.status).send();
}
// Request was ended by the user
if (html === null) {
return;
}
app.sendHTML(req, res, html);
} catch (e) {
next(e);
}
});
// eslint-disable-next-line func-names
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
server.all('*', (req, res, next) => {
next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
});
if (process.env.NODE_ENV === 'production') {
server.use(express.static('.next/static'));
server.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '.next/static', 'index.html'));
});
server.listen(PORT, err => {
if (err) throw err;
console.log(
`> Ready and listening on PORT:${PORT} in the ${process.env.NODE_ENV} environment`
);
});
} else {
server.listen(PORT, err => {
if (err) throw err;
console.log(`> Ready and listening on http://localhost:${PORT}`);
});
}
}
start();
And this is my auth file:
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('../models/UserModel');
passport.use(
new LocalStrategy(
{ usernameField: 'username', passwordField: 'password' },
async (username, password, done) => {
try {
const user = await UserModel.findOne({ username: username }).exec();
if (!user) {
return done(null, false, { message: 'Invalid username!' });
}
const passwordOk = await user.comparePassword(password);
if (!passwordOk) {
return done(null, false, {
message: 'Invalid password!'
});
}
return done(null, user);
} catch (err) {
return done(err);
}
}
)
);
// eslint-disable-next-line no-underscore-dangle
passport.serializeUser((user, done) => done(null, user._id));
passport.deserializeUser(async (id, done) => {
try {
const user = await UserModel.findById(id).exec(); //exec is used to get a real Promise
console.log('user deserialUser', user);
return done(null, user);
} catch (err) {
return done(err);
}
});
module.exports = {
initialize: passport.initialize(),
session: passport.session()
};
And it my /login route:
router.route('/login').post((req, res, next) => {
passport.authenticate('local', (err, user) => {
console.log('user 60', req.user);
console.log('user ', user);
// console.log('res.locals.user ', res.locals.user);
if (!user) {
return res.status(404).send({
msg: [
`We were unable to find this user.`,
`This email and/or password combo may be incorrect.
Please confirm with the "Forgot password" link above or the "Register" link below!`
]
});
}
if (user.isVerified === false) {
return res.status(401).send({
msg: [
'Your username has not been verified!',
'Check your email for a confirmation link.'
]
});
} else {
I made a log to see if user would be on the req object
console.log('req.user in success login route', req.user);
return res.status(200).send({
msg: [`Your have successfully logged in;`, `Welcome to Hillfinders!`]
});
}
})(req, res, next);
});
It appears what fixed my problem was the way I was declaring my routes.
This works:
router.post('/login', passport.authenticate('local'), function(req, res) {
var user = req.user;
if (!user) {
return res.status(404).send({
msg: [
`We were unable to find this user.`,
`This email and/or password combo may be incorrect.
Please confirm with the "Forgot password" link above or the "Register" link below!`
]
});
}
if (user.isVerified === false) {
return res.status(401).send({
msg: [
'Your username has not been verified!',
'Check your email for a confirmation link.'
]
});
} else {
return res.status(200).send({
msg: [`Your have successfully logged in;`, `Welcome to Hillfinders!`]
});
}
});
I was declaring it like this:
router.route('/login').post((req, res, next) => {
passport.authenticate('local', (err, user) => {
console.log('user ', user);
// console.log('res.locals.user ', res.locals.user);
if (!user) {
res.status(404).send({
msg: [
`We were unable to find this user.`,
`This email and/or password combo may be incorrect.
Please confirm with the "Forgot password" link above or the "Register" link below!`
]
});
return;
}
if (user.isVerified === false) {
res.status(401).send({
msg: [
'Your username has not been verified!',
'Check your email for a confirmation link.'
]
});
return;
} else {
res.status(200).send({
msg: [`Your have successfully logged in;`, `Welcome to Hillfinder!`]
});
return;
}
})(req, res, next);
});
Think I saw this syntax in the Express docs and just thought it was cool because of the chaining:
router.route('/login').post((req, res, next)=>{})
There must be some reason to declare it like this instead of the normal way...
Anyway thats' what eventually got me the user from passport to be on the req object \o/, This might also help other libraries that add objects of value to the req object too...
I was trying out passport local authentication by following a tutorial. Everything seems fine but I'm getting this error when I make a request using Postman:
[nodemon] 1.18.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node server.js`
body-parser deprecated bodyParser: use individual json/urlencoded middlewares server.js:17:10
(node:6336) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
Started listening on PORT: 8080
events.js:167
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'password' of undefined
at model.userSchema.methods.validPassword.password [as validPassword] (F:\Web Projects\LocalAuth\userModel.js:20:50)
at F:\Web Projects\LocalAuth\passport.js:34:21
at F:\Web Projects\LT1Kqob5UDEML61gCyjnAcfMXgkdP3wGcgGdBcFel.js:4672:16
at F:\Web Projects\LT1Kqob5UDEML61gCyjnAcfMXgkdP3wGcgGdBcFry.js:4184:12
at process.nextTick (F:\Web Projects\LT1Kqob5UDEML61gCyjnAcfMXgkdP3wGcgGdBcFry.js:2741:28)
at process._tickCallback (internal/process/next_tick.js:61:11)
Emitted 'error' event at:
at F:\Web Projects\LT1Kqob5UDEML61gCyjnAcfMXgkdP3wGcgGdBcFel.js:4674:13
at F:\Web Projects\LT1Kqob5UDEML61gCyjnAcfMXgkdP3wGcgGdBcFry.js:4184:12
at process.nextTick (F:\Web Projects\LT1Kqob5UDEML61gCyjnAcfMXgkdP3wGcgGdBcFry.js:2741:28)
at process._tickCallback (internal/process/next_tick.js:61:11)
[nodemon] app crashed - waiting for file changes before starting...
Here is my user schema:
const mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const Config = require ('./config');
mongoose.connect (Config.dbUrl);
let userSchema = new mongoose.Schema({
local : {
email: String,
password: String,
},
});
userSchema.methods.generateHash = password => {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
userSchema.methods.validPassword = password => {
return bcrypt.compareSync(password, this.local.password);
};
module.exports = mongoose.model('User', userSchema);
And this is my server.js file:
const express = require ('express');
const session = require ('express-session');
const mongoose = require ('mongoose');
const bodyParser = require ('body-parser');
const cookieParser = require ('cookie-parser');
const morgan = require ('morgan');
const flash = require ('connect-flash');
const passport = require ('passport');
const PassHandler = require('./passport');
const app = express ();
const port = process.env.PORT || 8080;
app.use (morgan ('dev'));
app.use (bodyParser ({extended: false}));
app.use (cookieParser ());
app.use (
session ({secret: 'borkar.amol', saveUninitialized: true, resave: true})
);
//Initialize Passport.js
app.use (passport.initialize ());
app.use (passport.session ());
app.use (flash ());
//Global Vars for flash messages
app.use((req, res, next) => {
res.locals.successMessage = req.flash('successMessage');
res.locals.errorMessage = req.flash('errorMessage');
res.locals.error = req.flash('error');
next();
});
PassHandler(passport);
//Middleware to check if the user is logged in.
const isLoggedIn = (req, res, next) => {
if(req.isAuthenticated()) {
return next();
}
res.status(400).json({ message: 'You are not authenticated to acces this route.' });
}
app.get('/', (req, res) => {
res.json({ message: 'Local Auth API v0.1.0'});
});
app.post('/signup', passport.authenticate('local-signup', {
successRedirect: '/user',
failureRedirect: '/signup',
failureFlash: true,
}));
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/user',
failureRedirect: '/',
failureFlash: true,
}));
app.get('/user', isLoggedIn, (req, res) => {
res.json({ user: req.user, message: "User is logged in."});
});
app.listen (port, () => {
console.log (`Started listening on PORT: ${port}`);
});
Here is the Passport strategy I'm using:
passport.use (
'local-login',
new LocalStrategy (
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
},
function (req, email, password, done) {
User.findOne ({'local.email': email}, function (err, user) {
if (err) return done (err);
if (!user)
return done (
null,
{message: 'User not found.'},
req.flash ('errorMessage', 'No user found.')
);
if (!user.validPassword (password))
return done (
null,
{message: 'Invalid email or password.'},
req.flash ('errorMessage', 'Oops! Wrong password.')
);
// all is well, return successful user
return done (null, user);
});
}
)
);
I've no idea whats going wrong to be honest. please help.
**Update:**The signup route and signup strategy is working fine. Only the /login route is giving problem.
I faced the same exact issue. I solved mine by moving the validatePassword method from the user schema to the passport strategy. It seemed the password was not being passed to the validatePassword method in the UserSchema.
Here is my UserSchema.js
// Pulling in required dependencies
const mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const Schema = mongoose.Schema;
//Creat UserSchema
const UserSchema = new Schema({
local: {
email: String,
password: String
},
role: {
type: String,
default: 'user',
},
books_downloaded: {
booksId: {
type: Array,
required: false,
},
},
books_needed: {
type: Object,
default: null,
},
created_at: {
type: Date,
default: Date.now,
},
});
// methods=====================================================
// generating a hash
UserSchema.methods.generateHash = (password) => {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
}
// expose User model to the app
module.exports = mongoose.model('User', UserSchema);
And here is my passport strategy
// load all the things we need
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt-nodejs');
//load up the user model
const User = require('../models/User');
// expose this function to our app
module.exports = passport => {
/**
* passport session setup =======================
* required for persistent login sessions
* serialize and unserialize users out of session
*/
//serialize the user for the session
passport.serializeUser((user, done) => {
done(null, user.id);
});
//deserialize the user
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
/**
* LOCAL SIGNUP
* using named strategies
*/
// local signup
passport.use(
'local-signup',
new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
},
(req, email, password, done) => {
process.nextTick(() => {
// find a user whose email is the same as the forms email
User.findOne({ 'local.email': email }, (err, user) => {
if (err) return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
// save the user
newUser.save(err => {
if (err) throw err;
return done(null, newUser);
});
}
});
});
}
)
);
// =========================================================================
// LOCAL LOGIN =============================================================
passport.use(
'local-login',
new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
},
(req, email, password, done) => {
// 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 before anything else
if (err) return done(err);
// if no user is found, return the message
if (!user) return done(null, false, req.flash('loginMessage', 'No
user found.'));
// if the user is found but the password is wrong
let correctPassword =
bcrypt.compareSync(password,user.local.password);
if (!correctPassword)
return done(null, false, req.flash('loginMessage', 'Oops!
Wrong password.'));
// If all is well, return successful user
return done(null, user);
});
}
)
);
};
I rewrote most of the script, and it works now! Here is the code.
const express = require ('express');
const session = require ('express-session');
const bodyParser = require ('body-parser');
const cookieParser = require ('cookie-parser');
const mongoose = require ('mongoose');
const passport = require ('passport');
const LocalStrategy = require ('passport-local').Strategy;
const User = require ('./UserModel');
const dbUrl = 'url';
const port = process.env.PORT || 9000;
const app = express ();
app.use (bodyParser.json ());
app.use (bodyParser.urlencoded ({extended: true}));
app.use (cookieParser ());
app.use (
session ({secret: 'borkar.amol', saveUninitialized: false, resave: false})
);
app.use (passport.initialize ());
app.use (passport.session ());
mongoose.connect (dbUrl, {useNewUrlParser: true}, () => {
console.log ('Successfully connected to hosted database.');
});
passport.serializeUser ((User, done) => {
//console.log ('SERIALIZEUSER: ', User._id);
done (null, User._id);
});
passport.deserializeUser ((id, done) => {
User.findById (id, (err, User) => {
//console.log ('DESERIALIZEUSER: ', User);
done (err, User);
});
});
passport.use (
'signup',
new LocalStrategy (
{
usernameField: 'email',
passwordField: 'password',
},
(email, password, done) => {
process.nextTick (() => {
User.findOne ({email: email}, (err, foundUser) => {
if (err) return done (err);
if (foundUser) {
return done (null, false, {
message: 'The email is already registered.',
});
} else {
let newUser = new User ();
newUser.email = email;
newUser.password = newUser.hashPassword (password);
newUser.save (err => {
if (err) console.error ('Error when writing to database', err);
});
return done (null, newUser, {
message: 'User has been registered successfully.',
});
}
});
});
}
)
);
passport.use (
'login',
new LocalStrategy (
{
usernameField: 'email',
},
(email, password, done) => {
User.findOne ({email: email}, (err, foundUser) => {
if (err) return done (err);
if (!foundUser) {
return done (null, false, {
message: 'Invalid Username or Password.',
});
}
if (!foundUser.comparePassword (password)) {
return done (null, false, {
message: 'Invalid Username or Password.',
});
}
return done (null, foundUser);
});
}
)
);
//Routes --->
app.get ('/user', (req, res, next) => {
if (req.isAuthenticated ()) {
res.json ({user: req.user});
return next ();
}
res.status (400).json ({message: 'Request is not authenticated.'});
});
app.post (
'/signup',
passport.authenticate ('signup', {
successRedirect: '/user',
failureMessage: true,
successMessage: true,
})
);
app.post (
'/login',
passport.authenticate ('login', {
successRedirect: '/user',
failureMessage: true,
successMessage: true,
})
);
app.post ('/logout', (req, res) => {
if (req.user) {
req.session.destroy ();
req.logout ();
res.clearCookie ('connect.sid');
return res.json ({message: 'User is now logged out.'});
} else {
return res.json ({message: 'Error: No user to log out.'});
}
});
app.listen (port, () => {
console.log (`Started listening on PORT: ${port}`);
});
define it as a function instead
before
userSchema.methods.validPassword = password => {
return bcrypt.compareSync(password, this.local.password);
};
after
userSchema.methods.validPassword = function (password) {
return bcrypt.compareSync(password, this.local.password);
};
i dont know why it doesn't work with arrow callback
"In classic function expressions, the "this" keyword is bound to different values based on the context in which it is called. With arrow functions however, this is lexically bound. It means that it uses "this" from the code that contains the arrow function." - Quote from freeCodeCamp
Here, we are using "this" from the userSchema instead of the "this" from arrow function itself. So, you should change your arrow function to classic function expression.
I use express-sessions and passports. I store token data based on the sessionID stored in cookies which will later be retrieved in the mysql database.
So far I can store shopping cart data using this sessionID as a token
that is stored in cookies and will later be retrieved in the database
every request made.
but I got a problem when the user logged in using a passport, which is where the user data is also stored in the session and makes the sessionID change when the user successfully logs in.
So after successfully logging in the shopping cart data disappears because the sessionID is different
Did I make mistake?
to be honest, I was really confused about the use of this express session
I have tried deleting this : app.use(passport.session()); and still the sessionID is changed
Server.js
const app = express();
app.use(session({
genid: function (req) {
return uuidv4() // use UUIDs for session IDs
},
name:keys.session.name,
secret: keys.session.secret,
resave: false,
saveUninitialized: true,
rolling:true,
cookie: {
secure: false,
httpOnly: true,
maxAge:keys.session.maxAge, // satu hari,
sameSite:true,
}
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.disable('x-powered-by');
app.use((req,res,next)=>{
res.header('X-XSS-Protection', '1; mode=block');
res.header('X-Frame-Options','deny');
res.header('X-Content-Type-Options','nosniff');
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.cookie('hammerstout_t',req.csrfToken());
console.log(req.sessionID);
next();
})
app.use(cors({ origin: keys.origin.url,credentials:true}))
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.get('/', function (req, res, next) {
var ua = UAparser(req.headers['user-agent']);
res.end(JSON.stringify(ua, null, ' '));
})
app.use('/api/',[CategoryRoutes, ProductRoutes, CartRoutes, AuthRoutes,TrackRoutes]);
const port = process.env.PORT || 5000;
app.listen(port, (err) => {
if(err){
console.log(err);
}else{
console.log(`Server running on port ! ${port}`);
}
});
passport-setup-js
import passport from 'passport';
import GoogleStrategy from 'passport-google-oauth20';
import keys from './keys';
import db from './conn';
passport.serializeUser((user, done) => {
let tokenValue={
providerId:user.providerId,
token:user.token,
user_id:user.id
}
done(null, tokenValue)
})
passport.deserializeUser((data, done) => {
let querySelect = `SELECT us.id,us.displayName,us.email,up.providerId,up.token,up.provider from user as us
left join user_provider as up on us.id = up.user_id
where us.id = ? and up.provider = 'google' and up.providerId = ? and up.token = ?`;
db.query(querySelect,[data.user_id,data.providerId,data.token], (err, ress) => {
if(ress.length > 0){
done(null, ress[0])
}
})
})
passport.use(
new GoogleStrategy({
//options for the google strategy
callbackURL: '/api/auth/google/redirect',
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret
}, (accessToken, refreshToken, profile, done) => {
// console.log(profile);
// TODO : HARUS VALIDASI DULU
let queryInsert = `INSERT INTO user set ?; INSERT INTO user_provider set user_id = (SELECT u.id from user as u order by id desc limit 1), ?;`;
let queryFind = `SELECT us.id from user as us
left join user_provider as up on us.id = up.user_id
where up.providerId = ${profile.id}
and up.token = '${accessToken}' group by us.id`;
let querySelect = `SELECT us.id,us.displayName,us.email,up.providerId,up.token,up.provider from user as us
left join user_provider as up on us.id = up.user_id
where us.id = ? and up.provider = ? and up.providerId = ? and up.token = ?`;
let user = {
displayName: profile.displayName,
email: profile.emails[0].value,
}
if(profile.gender){
user.gender = profile.gender
}
let user_provider={
provider:profile.provider,
providerId:profile.id,
token:accessToken
}
db.query(queryFind, (error, result, fields) => {
if(error) return done(error);
if (result.length > 0) {
return done(null, result[0]);
}else{
db.query(queryInsert, [user, user_provider], (err, ress, fields) => {
if (err) return done(err);
if (ress) {
db.query(querySelect, [ress[0].insertId, profile.provider, profile.id, accessToken],(err,ress)=>{
if (err) return done(err);
if(ress.length > 0){
return done(null, ress[0]);
}
})
}
})
}
})
})
)
Controller
export const loginGoogleRedirect = (req,res)=>{
let payload ={
id:req.user.id,
displayName:req.user.displayName,
email:req.user.email,
providerId:req.user.providerId,
token:req.user.token,
provider:req.user.provider
};
jwt.sign(
payload,
keys.jwt.secretOrPrivateKey,
{
expiresIn: keys.jwt.expiresIn
}, (err, token) => {
res.redirect(keys.origin.url + "?token=" + token);
});
}
I am very stuck with a routing error on express.js. I am using jade to render my page views. I have read all the docs and refactored many times to no avail.
This is the plain contents of the link that is pointing to the '/sell' route.
index.jade
include layout
html
button.btn.btn-primary(type='submit', href='/sell') Add Item
When clicking this button the browser returns the following as a 404:
Cannot GET /sell
The problem here is that all other routes work correctly, if you are to modify the above href to other pages i.e. '/' , '/sign_in', etc no error occurs. The problem appears to be isolated to the '/sell' route.
The controller with the '/sell' route is below:
server.js
var dotenv = require('dotenv');
dotenv.load();
var session = require('express-session')
var nodemailer = require('nodemailer');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var bcrypt = require('bcrypt');
var async = require('async');
var crypto = require('crypto');
var cookieParser = require('cookie-parser');
var flash = require('express-flash');
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var root = __dirname;
var path = require('path');
var User = require('./models/user');
var bodyParser = require('body-parser');
var errorHelper = require('mongoose-error-helper').errorHelper;
var validator = require('validator');
var Item = require("./models/item")
var username, email, password, owner, product_name, condition, details, price;
mongoose.connect(process.env.MONGODB_CONGO_DEV);
// Middleware
app.set('views', 'app/views');
app.set('view engine', 'jade');
app.set('port', process.env.PORT || 3000);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true}));
app.use(cookieParser());
app.use(session( {
secret: 'session secret key',
resave: false,
saveUninitialized: true
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
// Routing
app.get('/', function(req,res) {
res.render('index', {
title: 'Congo',
message: 'Congo',
user: req.user
});
});
app.get('/sell', function(req,res) {
res.render('item', {
title: 'Congo - Sell',
message: 'Sell',
user: req.user
});
});
app.get('/sign_in', function(req,res) {
res.render('sign_in', {
title: 'Congo',
message: 'sign in motherfucker',
user: req.user
});
});
app.post('/sign_in', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) return next(err)
if (!user) {
req.flash('error', 'Incorrect login details');
return res.redirect('/sign_in')
};
req.logIn(user, function(err) {
if (err) return next(err);
res.
return res.redirect('/');
});
})(req, res, next);
});
app.get('/sign_up', function(req, res) {
res.render('sign_up', {
title: 'Congo',
message: 'sign up',
user: req.user
});
});
app.post('/sign_up', function(req, res) {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
if (req.body.confirm != req.body.password) {
req.flash('error', 'Password do not match')
res.redirect('/sign_up')
};
else if (validator.isEmail(req.body.email) === false) {
req.flash('error', 'Invalid email address')
res.redirect('/sign_up')
};
else {
user.save(function(err) {
if (err) {
req.flash('error', 'Email already in use')
res.redirect('/sign_up')
} else {
req.logIn(user, function(err) {
res.redirect('/');
});
}
});
};
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.get('/forgot', function(req, res) {
res.render('forgot', {
user: req.user,
message: 'you wally'
});
});
app.get('/profile', function(req, res) {
res.render('profile', {
user: req.user,
message: 'User profile'
});
});
app.post('/forgot', function(req, res, next) {
async.waterfall([
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
function(token, done) {
User.findOne({ email: req.body.email }, function(err, user) {
if (!user) {
req.flash('error', 'No account with that email address exists.');
return res.redirect('/forgot');
};
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
done(err, token, user);
});
});
},
function(token, user, done) {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'Gmail',
auth: {
user: 'harryandrew.dix#gmail.com',
pass: process.env.GMAIL_PASS
};
});
var mailOptions = {
to: user.email,
from: 'passwordreset#demo.com',
subject: 'Node.js Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/reset/' + token + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
done(err, 'done');
});
};
], function(err) {
if (err) return next(err);
res.redirect('/forgot');
});
});
app.get('/reset/:token', function(req,res) {
User.findOne({resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now()}}, function(err,user) {
if (!user) {
req.flash('error', 'Password reset token invalid or expired.');
return res.redirect('/forgot');
}
res.render('reset', {
user: req.user,
message: 'reset dem pass'
});
});
});
app.post('/reset/:token', function(req, res) {
async.waterfall([
function(done) {
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('error', 'Password reset token is invalid or has expired.');
return res.redirect('back');
};
user.password = req.body.password;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function(err) {
req.logIn(user, function(err) {
done(err, user);
});
});
});
},
function(user, done) {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'Gmail',
auth: {
user: 'harryandrew.dix#gmail.com',
pass: process.env.GMAIL_PASS
};
});
var mailOptions = {
to: user.email,
from: 'passwordreset#demo.com',
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
req.flash('success', 'Success! Your password has been changed.');
done(err);
});
};
], function(err) {
res.redirect('/');
});
});
// app.post('/add_item', function(req, res) {
// var item = new Item({
// owner: req.user.id,
// product_name: req.body.product_name,
// condition: req.body.condition,
// details: req.body.details,
// price: req.body.price
// });
// item.save(function(err) {
// if (err) {
// req.flash('error', 'Something went wrong, make sure you are signed in.')
// res.redirect('/add_item');
// } else {
// req.logIn(item, function(err) {
// res.redirect('/user_profile');
// });
// };
// });
// });
var server = app.listen(3000, function() {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s',host,port);
});
item.jade
include layout
html
block content
form(method='POST')
legend(style='font-family: Raleway; font-size: 30px;') Item Details
.form-group
label(for='product_name') Product Name
input.form-control(type='text', name='product_name', placeholder='include product name, brand, condition, colour etc.', required autofocus)
.form-group
label(for='condition') Condition
input.form-control(type='text', name='condition', placeholder='e.g. New, Used', required)
.form-group
label(for='image') Image
input.form-control(type='file', name='image')
.form-group
label(for='details') Details
textarea.form-control(name='details', cols='40', rows='5')
.form-group
label(for='price') Price
.input-group
.input-group-addon £
input.form-control(type='text', placeholder='Amount', required)
.input-group-addon .00
br
br
button#btnSubmit.btn.btn-primary(type='submit') Post Item
I think it's acting as form post and tries to reach app.post('/sell')
Change it:
button.btn.btn-primary(type='submit', href='/sell') Add Item
to:
a.btn.btn-primary(href='/sell') Add Item
also remove some parts of middleware and keep only these lines:
app.set('views', 'app/views');
app.set('view engine', 'jade');
app.set('port', process.env.PORT || 3000);
app.use(logger('dev'));
and then navigate in Your browser to /sell route, check if it's working.
if Yes - so problem with one of middlewares that we have deleted to check.
You should add a logger middleware and see what error the server gives when you try to go to that route. Another thing to try would be to rename the sell route and see if that works. That could indicate a conflict with another one of your routes, although it's not clear where this would be from looking at your code.