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.
Related
I am trying to create a simple node.js CRUD application just to put into practice what I have learnt, however, I have been stuck on this this issue for about two days now.
I am using passport to authenticate users, however, when valid passwords are entered it is not redirecting and justs remain on the login page. Here is my code:
//**Model**
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
//Passport Config File
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
const User = require("../models/user");
module.exports = function (passport) {
passport.use(
new LocalStrategy({usernameField:'username'},(username, password, done) => {
User.findOne({username: username })
.then((user) => {
if (!user) {
return done(null, false, { message: "User Not Found" });
}
//match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Password is Incorrect" });
}
});
})
.catch((err) => console.log(err));
})
);
//**Route**
router.post('/login', (req, res, next)=>{
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/login'
})(req, res, next)
})
//**DB**
username: "bobby"
password: "password"
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.
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'm new to React. I'm having trouble understanding what is happening to my req.session.passport object that is created when a user logs in to the application. When logging into my application I get the following in my console:
axios.post(/login) response: login.component.js:39
Object { cookie: {…}, passport: {…} }
The problem comes when I go to another page. This appears on the console:
axios.get(/logged_in) response: user.component.js:37
this.state.session: user.component.js:38
Object { cookie: {…} }
Where did the passport object go? I need it to tell react who is logged in. I'm calling req.session in both axios paths so I expect to see the exact same result, but I don't. None of the topics similar enough to my situation has helped me figure out this problem.
onSubmit(e) function from login component:
onSubmit(e) {
e.preventDefault();
axios.post("http://localhost:4000/users/login", {
username: this.state.username,
password: this.state.password
}).then(res => {
console.log("axios.post(/login) response: ");
console.log(res.data);
if (res.status === 200) {
this.props.updateSession({
session: res.data
});
}
}).catch(err => {
console.log("Login error: ");
console.log(err);
});
this.props.history.push("/");
}
Route that the above function uses to authenticate a user:
router.post("/login", passport.authenticate("local"), (req, res) => {
console.log("Calling router.post(/login)...");
console.log("req.session: ", req.session);
res.status(200).json(req.session);
});
componentDidMount() function from the main user component that checks if a session is active:
componentDidMount() {
axios.get("http://localhost:4000/users/logged_in").then(res => {
this.setState({
session: res.data
});
console.log("axios.get(/logged_in) response: ");
console.log("this.state.session: ");
console.log(this.state.session);
}).catch(err => {
console.log(err);
})
}
Route that the above function uses. Very similar to the login route minus the authentication:
router.get("/logged_in", (req, res) => {
console.log("Calling router.get(/logged_in)...");
res.status(200).json(req.session);
});
localStrategy.js (Passport):
const User = require("../database/models/user.model"),
LocalStrategy = require('passport-local').Strategy;
const strategy = new LocalStrategy(
{
usernameField: "username" // not necessary, DEFAULT
},
function(username, password, done) {
User.findOne({ username: username }, (err, user) => {
if (err) {
return done(err)
}
if (!user) {
return done(null, false, { message: "Incorrect username" })
}
if (!user.checkPassword(password)) {
return done(null, false, { message: "Incorrect password" })
}
return done(null, user)
})
}
)
module.exports = strategy;
index.js (Passport):
const passport = require("passport"),
LocalStrategy = require("./localStrategy"),
User = require("../database/models/user.model");
passport.serializeUser((user, done) => {
console.log("*** serializeUser called, user: ");
console.log(user);
console.log('---------');
done(null, { _id: user._id });
})
passport.deserializeUser((id, done) => {
console.log("DeserializeUser called");
User.findOne(
{ _id: id },
"username",
(err, user) => {
console.log("*** Deserialize user, user:");
console.log(user);
console.log("--------------");
done(null, user);
}
)
})
passport.use(LocalStrategy);
module.exports = passport;
console.log result from serializeUser() above:
*** serializeUser called, user:
{ isAdmin: false,
orders: [],
createdAt: 2020-02-11T02:09:18.992Z,
_id: 5e420cdb...,
username: 'user',
password:
'$2a$1...',
__v: 0 }
server.js:
const express = require("express"),
app = express(),
bodyParser = require("body-parser"),
session = require("express-session"),
MongoStore = require('connect-mongo')(session),
cors = require("cors"),
PORT = 4000,
// Require routes
productRoutes = require("./routes/products"),
orderRoutes = require("./routes/orders"),
userRoutes = require("./routes/users"),
passport = require('./passport'),
dbConnection = require('./database');
app.use(bodyParser.json());
app.use(cors());
// Sessions
app.use(
session({
secret: "Birdhouses are cool.", // Secret can be any string
store: new MongoStore({ mongooseConnection: dbConnection }),
resave: false,
saveUninitialized: false
})
);
app.use(passport.initialize());
app.use(passport.session()); // calls serializeUser and deserializeUser
// Routes config
app.use("/products", productRoutes);
app.use("/orders", orderRoutes);
app.use("/users", userRoutes);
// Start server
app.listen(PORT, function() {
console.log("Server is running on Port: " + PORT);
});
User model:
const mongoose = require("mongoose"),
bcrypt = require("bcryptjs");
// Define user schema
let userSchema = new mongoose.Schema({
username: {
type: String
},
password: {
type: String
},
isAdmin: {
type: Boolean,
default: false
},
orders: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Order"
}],
createdAt: {
type: Date,
default: Date.now()
}
});
userSchema.methods = {
checkPassword: function(inputPassword) {
return bcrypt.compareSync(inputPassword, this.password);
},
hashPassword: plainTextPassword => {
return bcrypt.hashSync(plainTextPassword, 10);
}
}
userSchema.pre("save", function(next) {
if (!this.password) {
console.log("models/user.js =======NO PASSWORD PROVIDED=======")
next();
} else {
console.log("models/user.js hashPassword in pre save");
this.password = this.hashPassword(this.password);
next();
}
})
module.exports = mongoose.model("User", userSchema);
I am creating a self sustained chat app project with user authentication.The following code is able to signup (add new users) successfully.However login of previous signed up users does not occur.
Example: upon entering the username as foo and password as foopass,this is what happens inside the terminal
Executing (default): CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER auto_increment , `email` VARCHAR(255) NOT NULL DEFAULT 'abhinav#gmail.com' UNIQUE, `username` VARCHAR(255) NOT NULL UNIQUE, `password` VARCHAR(255) NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `users`
Database is ready
Executing (default): SELECT `id`, `email`, `username`, `password`, `createdAt`, `updatedAt` FROM `users` AS `users` WHERE `users`.`username` = 'foo' LIMIT 1;
the following are my js files
1.server.js
const express = require('express')
const session = require('express-session')
const passport = require('passport')
const path = require('path')
const http = require('http')
const app = express()
const server = http.createServer(app)
const cookieparser = require('cookie-parser')
const bodyParser = require('body-parser');
app.set('views',path.join(__dirname , 'chat'));
app.set('view engine','html');
var port = process.env.PORT || 5898;
app.use('/', express.static(path.join(__dirname, 'intro')));
app.use('/profile', express.static(path.join(__dirname, 'chat')));
require('./passport')(passport);
require('./app/routes.js')(app, passport);
app.use(express.json());
app.use(session({
secret: 'project-session',
resave: true,
saveUninitialized:true
}))
app.use(passport.initialize())
app.use(passport.session())
server.listen(port, () => console.log("Server running on http://localhost:5898"))
here intro is the public file(accessible to everyone i.e main page) while chat is the private file(accessible to only logged in users)
2.app/routes.js
const express = require('express')
const app = express();
const route = express.Router()
const path = require('path');
const bodyParser = require('body-parser');
const passport = require('passport-local');
const Users = require('../db').Users;
module.exports = function(app,passport)
{
app.use((bodyParser.urlencoded({ extended: false })))
app.use(bodyParser.json());
app.get('/signup', function(req,res) {
res.render('../intro/index');
})
app.get('/login',function(req,res){
res.send({
username: req.body.username
})
})
app.post('/signup', function(req,res){
var a = [req.body.email,req.body.username,req.body.password];
Users.findOne({
where: { email: a[0],
username: a[1],
password: a[2],
}
}).then(users => {
if (users) {
res.send({
url:'/'
})
}
else{
Users.create({
email: a[0],
username: a[1],
password: a[2],
}).then(users => {
res.send({
url:'/profile',
username: a[1]
})
})
}
})
})
app.post('/login', passport.authenticate('local'),
function(req, res) {
res.send({
url:'/profile',
username:req.body.username
});
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
}
3.db.js(using mysql)
const Sequelize = require('sequelize');
const db = new Sequelize(
'socializedb',
'soc',
'socpass',
{
dialect: 'mysql',
host: 'localhost',
operatorsAliases: false,
}
);
const Users = db.define('users', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
defaultValue: "abhinav#gmail.com"
},
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
password: {
type: Sequelize.STRING,
allowNull: false,
}
})
db.sync().then(() => console.log("Database is ready"))
exports = module.exports = {
db,
Users
}
4.passport.js(where my local strategy resides)
const LocalStrategy = require('passport-local').Strategy
const Users = require('./db').Users
module.exports = function(passport)
{
console.log("passport is working");
passport.serializeUser(function (users, done) {
return done(null, users.id);
console.log("Serialize");
})
passport.deserializeUser(function (id, done) {
console.log("DeSerialize");
Users.findById(id).then((users) => {
console.log(users);
return done(null, users);
});
})
passport.use(new LocalStrategy(
function(username, password, done) {
Users.findOne({where:{ username: username} },
function(err, users) {
if (err) { return done(err); }
if (!users) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!users.password === password) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, users);
});
}
));
}
I have used local authentication.I am sure it is not a problem with my database as I can see the entries inside the tables residing on my machine.I mostly think the issue could be the use of serialize/deserialize in passport.js.Thanks in advance.
The code is having two problems related to Login.
1. Sequelize Querying works with Promises. The Sequelize Query in passport's Local Strategy callback is trying to use callbacks instead of Promises. So,
passport.use(new LocalStrategy(
function(username, password, done) {
Users.findOne({where:{ username: username} },
function(err, users) {
if (err) { return done(err); }
if (!users) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!users.password === password) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, users);
});
}
));
must be changed to
passport.use(new LocalStrategy(
function (username, password, done) {
Users.findOne({ where: { username: username } })
.then(function (users) {
if (!users) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!users.password === password) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, users);
})
.catch(err => done(err));
}
));
This should hopefully solve the current error.
2. Another problem is in server.js, the router functions must be called after the Passport's initialization to initialize the Passport before actually trying to use them.
Otherwise, the login and other routes would try to use Passport before passport's initialization middlewares, causing errors. So,
require('./app/routes.js')(app, passport);
must be moved after
app.use(passport.initialize())
app.use(passport.session())
These two fixes should enable the User Login.