I've just included Passport js in my project for auth. But now my POST requests to create a new user hang and return an alert on the client saying "Not authorized" after I close the server. My old code (without Passport) still works so I don't think it's an issue with proxy-ing from client to server
The last console log I see is the first log in the user API, console.log('received request ', req.body)
There are no logged error messages, except when I stop the server I'll get
"Proxy error: Could not proxy request /api/user from localhost:3000 to http://localhost:5000/. [1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (ECONNRESET).
///app.js (server)
const express = require("express");
const bodyParser = require("body-parser");
const cors = require('cors');
const mongoose = require("mongoose");
const routes = require("./routes"); //Used to end with /api
const path = require("path");
require("dotenv").config();
const passport = require('passport');
const app = express();
const port = process.env.PORT || 5000;
//database
mongoose
.connect(process.env.DB, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log("Database connected successfully"))
.catch((err) => console.log(err));
mongoose.Promise = global.Promise;
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
require('./config/passport');
app.use(cors());
app.use(bodyParser.json());
app.use(passport.initialize());
app.use("/", routes); //Load API - this folder has an index.js file
app.use((err, req, res, next) => {
console.log("$!$", err);
next();
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
///users.js (API for User model)
const passport = require('passport');
const express = require("express");
const router = express.Router();
const User = require("../models/User");
//Old - This works, which makes me think the issue is with Passport js
// router.post("/user", (req, res, next) => {
// try {
// User.create(req.body)
// .then((data) => res.json(data))
// .catch(next);
// } catch {
// res.json({
// error: "Failed to upload new user",
// });
// }
// });
//New
router.post("/user", (req, res, next) => {
console.log('received request ', req.body);
passport.authenticate('register', (err, hashedPassword, info) => {
if (err) {
console.log("Passport err on register ", err);
}
else if (info != undefined) {
console.log("Defined err ", info.message);
res.send(info.message);
} else {
req.login(user, err => {
const newUser = {
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: hashedPassword,
};
User.create(newUser)
.then((data) => res.json(data))
.catch(next);
});
}
});
});
//passport.js (for auth)
const bcrypt = require('bcrypt');
const BCRYPT_SALT_ROUNDS = 12;
const passport = require('passport'),
localStrategy = require('passport-local').Strategy,
User = require('../models/User'),
JWTstrategy = require('passport-jwt').Strategy,
ExtractJWT = require('passport-jwt').ExtractJwt;
const opts = {
jwtFromRequest: ExtractJWT.fromAuthHeaderWithScheme('JWT'),
secretOrKey: 'secret key',
};
passport.use(
'register',
new localStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
(email, password, done) => {
try {
console.log('searching for User');
User.findOne({ email })
.then(user => {
if (user != null) {
console.log('email already taken');
return done(null, false, { message: 'email already in use' });
} else {
console.log('email is available');
bcrypt.hash(password, BCRYPT_SALT_ROUNDS).then(hashedPassword => {
console.log('hashed password created for new user');
return done(null, hashedPassword, null);
});
}
})
} catch(err) {
done(err);
}
}
)
)
passport.authenticate in your code isn't used as a middleware but just like a regular function called inside the request handler. As of passport documentation you should call the returning function when used this way, like this:
passport.authenticate('register', (err, hashedPassword, info) => {
// ...
})(req, res, next); // <- ADD ARGUMENTS AND CALL
Related
I am implementing user authentication and and once they are authenticated, they can CRUD their user data on the database. I can currently authenticate users and have users register a new account, but im not sure what my next step should be. What should my Node server pass back to my front end once my user is authenticated? I read everywhere that I am suppose to use tokens and sessions to check if user is auth for every request.
Auth.js
const router = express.Router()
const passport = require("passport")
router.post("/login", (req, res, next) => {
passport.authenticate("local", function(err, user, info) {
console.log(user)
if(err){
return res.status(400).json({errors: err})
}
if(!user){
return res.status(400).json({errors: "Incorrect username or password"})
}
//figure out what this does
req.logIn(user, function(err){
if(err){
return res.status(400).json({errors: err});
}
return res.status(200).json({success: `Logged in ${user.id}`})
})
})(req, res, next)
})
module.exports = router
Setup.js (for passportjs)
const bcrypt = require("bcrypt")
const User = require('../models/user')
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
})
})
passport.use(
new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
//Match user
User.findOne({ email: email })
.then(user => {
//match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
console.log("successfully auth")
return done(null, user);
} else {
console.log("incorrect password ")
return done(null, false, { message: "Incorrect Password" })
}
})
})
.catch(err => {
console.log("no account found")
return done(null, false, { message: err })
})
})
)
module.exports = passport
index.js
require('dotenv').config()
const express = require('express');
const session = require('express-session');
const mongoose = require("mongoose")
const MongoStore = require("connect-mongo")
const cors = require("cors");
const axios = require('axios');
const User = require('./models/user')
const bcrypt = require('bcrypt')
const passport = require("./passport/setup")
const auth = require("./routes/auth")
const app = express();
app.use(cors());
let port = process.env.PORT;
if (port == null || port == "") {
port = 5000;
}
mongoose.connect(process.env.ATLAS_URI).then(console.log("MongoDB Connection Success")).catch(err => console.log("MongoDB Connection Failed" + err))
// For backend and express init
app.use(express.json());
app.use(express.urlencoded({ extended: false }))
app.use(session({
secret: 'random secret',
resave: false,
saveUninitialized: true,
store: MongoStore.create({ mongoUrl: process.env.ATLAS_URI }),
}));
app.use(passport.initialize())
app.use(passport.session())
app.use("/auth", auth)
app.post('/register', (req, res) => {
const email = req.body.email
const plainTextPassword = req.body.password;
//check if user already exists
User.find({ name: email }, (err, existingUser) => {
if (existingUser.length === 0) {
bcrypt.hash(plainTextPassword, 8, async (err, hash) => {
try {
const user = new User({
email: email,
password: hash
});
let result = await user.save();
if (result) {
console.log("account registered successfully")
res.send(result)
}
} catch (e) {
res.send("Something Went Wrong");
console.log("something went wrong ---" + e)
}
})
} else {
console.log("user account already exists! Login instead. ")
}
})
})
Im not trying to debug an error, just need to know what the approach is. Basically, I want the user to login once, and then be able to navigate around the web app and crud to their database. Thank you!!
My sessions are stored in a different collection in mongodb but idk what to do with this
the req.isAuthenticated an req.logout seems not to be working and flagged as 'not a function' in my project. here's the code for the project. my verify function seems not to be working as expected.
i've worked other video tutorials aside the paid tutorial i'm watching now on udemy, yet i get the same error on my end or my session's aren't stored.
const http = require("http");
const path = require("path");
const express = require("express");
const helmet = require("helmet");
const passport = require("passport");
const cookieSession = require("cookie-session");
const googleStrategy = require("passport-google-oauth20").Strategy;
require("dotenv").config();
const PORT = 8000;
const G_CONF = {
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "/auth/google/cb",
};
const CC_CONF = {
name: "session",
maxAge: 60 * 60 * 1000,
keys: [process.env.COOKIE_KEY_00, process.env.COOKIE_KEY_01],
secure: false,
httpOnly: true,
};
const app = express();
const server = http.createServer(app);
function verifyUser(req, res, next) {
const loggedIn = req.user;
console.log(loggedIn);
if (req.user === undefined) {
return res.status(401).json({ error: "you must log in" });
}
next();
}
passport.use(
new googleStrategy(G_CONF, (accessToken, refreshToken, profile, done) => {
done(null, profile);
})
);
// Serializes or push authenticated user to session.
passport.serializeUser((user, done) => {
done(null, user.id);
});
// Deserializes or pull authenticated user from subsequent session request.
passport.deserializeUser((id, done) => {
done(err, user);
});
app.use(helmet());
app.use(cookieSession(CC_CONF));
app.use(passport.initialize());
app.get("/", (req, res) => {
res.status(200).sendFile(path.join(__dirname, "public", "index.html"));
});
app.get(
"/auth/google",
passport.authenticate("google", { scope: ["profile"] })
);
app.get(
"/auth/google/cb",
passport.authenticate("google", {
failureRedirect: "/failed",
successRedirect: "/success",
session: true,
})
);
app.get("/secret", verifyUser, (req, res, next) => {
res.send("secret");
});
app.get("/failed", (req, res) => {
res.sendFile(path.join(__dirname, "public", "failed.html"));
});
app.get("/success", (req, res) => {
res.status(200).sendFile(path.join(__dirname, "public", "success.html"));
});
app.get("/auth/logout", (req, res) => {
req.logout();
res.redirect("/");
});
server.listen(PORT, (err) => {
if (err) {
console.error(err);
}
console.log("Auth Server running on port 8000");
});
i get 'undefined' when i log 'user'
my verify seems not to be working properly
passportjs req.method() isn't working.
There are many similar posts to this one, but I haven't found a qualifying solution in any of the answers to those posts that have helped me with this.
the code
"use strict";
require('dotenv').config();
const auth = require('./auth.js');
const express = require('express');
const passport = require('passport');
const bcrypt = require('bcrypt');
const mongo = require('mongodb');
const session = require('express-session');
const cors = require('cors');
const util = require('util');
const app = express();
const port = process.env.PORT || 8080;
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({extended:true}));
app.use(session({
secret: process.env.SESSION_SECRET,
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
maxAge: 1000 * 60 * 60 * 24 * 7
}
}));
app.use(passport.initialize());
app.use(passport.session());
mongo.connect(process.env.DATABASE, {useNewUrlParser: true, useUnifiedTopology: true}, (err, db) => {
if (err) {
console.log('Database error: ' + err);
} else {
console.log('Database connection successful');
auth(app, db);
app.route('/test').get((req, res) => {
res.send('The connection works!')
});
const ensureAuthenticated = (req, res, next) => {
console.log('isAuth() is: ' + req.isAuthenticated());
console.log('session store: ' + util.inspect(req.session, {showHidden: false, depth: null}));
if (req.isAuthenticated()) return next();
res.send('user not authenticated, begone! >:(');
}
app.route('/profile').get(
ensureAuthenticated,
(req, res) => {
res.render({username: req.user.username});
}
);
app.post('/login',
(request, response, next) => {
console.log(request.session)
passport.authenticate('local',
(err, user, info) => {
if(!user){ response.send(info.message);}
else{
request.login(user, function(error) {
if (error) return next(error);
console.log("Request Login supossedly successful.");
return response.send('Login successful');
});
//response.send('Login successful');
}
})(request, response, next);
}
);
app.route('/register').post((req, res, next) => {
const hash = bcrypt.hashSync(req.body.password, 13);
db.db().collection('users').findOne({username: req.body.username}, (err, user) => {
if (err) {
next(err);
} else if (user) {
res.send('user already exists :(');
} else {
db.db().collection('users').insertOne({
username: req.body.username,
password: hash
},
(err, doc) => {
if (err) {
res.send('registration mongo error');
} else {
next(null, user);
}
}
)
}
})
},
passport.authenticate('local', {failureMessage: 'passport authenticate failure'}),
(req, res, next) => {
console.log('registration successful');
req.logIn(req.user, err => {
if (err) next(err)
return console.log("i'm trying: " + req.user);
});
res.send('registration successful!!! :D');
}
);
app.listen(port, () => {console.log(`Listening on port: ${port}`)});
}
});
auth.js
const passport = require('passport');
const LocalStrategy = require('passport-local');
const ObjectID = require('mongodb').ObjectID;
const bcrypt = require('bcrypt');
module.exports = (app, db) => {
passport.use(new LocalStrategy(
(username, password, done) => {
db.db().collection('users').findOne({username: username}, (err, user) => {
console.log(`${username} attempted to log in`);
if (err) return done(err);
if (!user) return done(null, false);
if (!bcrypt.compareSync(password, user.password)) return done(null, false);
console.log('local strategy successful');
return done(null, user);
})
}
));
passport.serializeUser((user, done) => {
console.log(user.username + " serialized");
done(null, user._id);
});
passport.deserializeUser((id, done) => {
db.db().collection('users').findOne(
{_id: new ObjectID(id)},
(err, doc) => {
done(null, doc);
}
);
});
}
The Question:
All of the functions work just fine, I get all of the success return messages, and even register saves the user to the database, and login loads it successfully from the database. The only problem I'm having is that req.isAuthenticated() in the function ensureAuthenticated is always returning false, and as you can see, I really really really need it to be true so I can send the information to the client for the /profile route, and do all the other things I need with Passport. What am I missing?
Solution: I needed to add credentials: true to cors(), and something similar to the http headers (withCredentials:true for axios) in the client. I'm adding this because I know somebody is going to have this same problem one day, and will probably have as hard of a time finding this answer as I did.
Extra: In most forums that I asked this question in, the answers I got were all people not believing that this code works, and patronizing me by telling me I need to re-learn passport and react (if I got an answer at all lmao).
I am facing a session problem, Getting req.user undefined after successful passport. authenticate method.
Basically after signUp or login, when I am redirecting, unable to find "user" in request variable.
I have used a hackathon starter which was using mongo, I tried to change things to use Postgres.
Edit -- After following suggestions on Comment
Now signup flow is working fine, but login flow having some problem. Something weird happening, when I am adding a lot of breaking points, It seems to do login intermittently. Update code with comment suggestions
app.js
const express = require('express');
const compression = require('compression');
const session = require('express-session');
const bodyParser = require('body-parser');
const logger = require('morgan');
const chalk = require('chalk');
const errorHandler = require('errorhandler');
const lusca = require('lusca');
const dotenv = require('dotenv');
const flash = require('express-flash');
const path = require('path');
const passport = require('passport');
const expressValidator = require('express-validator');
const expressStatusMonitor = require('express-status-monitor');
const sass = require('node-sass-middleware');
const multer = require('multer');
dotenv.load({ path: '.env.example' });
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const models = require('./models');
const upload = multer({ dest: path.join(__dirname, 'uploads') });
/**
* Load environment variables from .env file, where API keys and passwords are configured.
*/
/**
* Controllers (route handlers).
*/
const homeController = require('./controllers/home');
const userController = require('./controllers/user');
const contactController = require('./controllers/contact');
const dashController = require('./controllers/dash');
const currencyController = require('./controllers/currency');
const accountController = require('./controllers/account');
const testController = require('./controllers/test');
const txController = require('./controllers/transaction');
/**
* API keys and Passport configuration.
*/
const passportConfig = require('./config/passport');
/**
* Create Express server.
*/
const app = express();
/**
* Express configuration.
*/
app.set('host', '127.0.0.1');
app.set('port', 8080);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(expressStatusMonitor());
app.use(compression());
app.use(sass({
src: path.join(__dirname, 'public'),
dest: path.join(__dirname, 'public')
}));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.use(session({
resave: true,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
cookie: { maxAge: 1209600000 }, // two weeks in milliseconds
store: new SequelizeStore({
db: models.sequelize
})
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use((req, res, next) => {
// if (req.path === '/api/upload') {
// next();
// } else {
// lusca.csrf()(req, res, next);
// }
next();
});
app.use(lusca.xframe('SAMEORIGIN'));
app.use(lusca.xssProtection(true));
app.disable('x-powered-by');
app.use((req, res, next) => {
res.locals.user = req.user;
next();
});
app.use((req, res, next) => {
// After successful login, redirect back to the intended page
if (!req.user
&& req.path !== '/login'
&& req.path !== '/signup'
&& !req.path.match(/^\/auth/)
&& !req.path.match(/\./)) {
req.session.returnTo = req.originalUrl;
} else if (req.user
&& (req.path === '/account' || req.path.match(/^\/api/))) {
req.session.returnTo = req.originalUrl;
}
next();
});
app.use('/', express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/chart.js/dist'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/popper.js/dist/umd'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/js'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/jquery/dist'), { maxAge: 31557600000 }));
app.use('/webfonts', express.static(path.join(__dirname, 'node_modules/#fortawesome/fontawesome-free/webfonts'), { maxAge: 31557600000 }));
/**
* Primary app routes.
*/
app.get('/', homeController.index);
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/logout', userController.logout);
app.get('/forgot', userController.getForgot);
app.post('/forgot', userController.postForgot);
app.get('/reset/:token', userController.getReset);
app.post('/reset/:token', userController.postReset);
app.get('/signup', userController.getSignup);
app.post('/signup', userController.postSignup);
// app.get('/account', passportConfig.isAuthenticated, userController.getAccount);
// app.post('/account/profile', passportConfig.isAuthenticated, userController.postUpdateProfile);
app.post('/account/password', passportConfig.isAuthenticated, userController.postUpdatePassword);
// app.post('/account/delete', passportConfig.isAuthenticated, userController.postDeleteAccount);
// app.get('/account/unlink/:provider', passportConfig.isAuthenticated, userController.getOauthUnlink);
app.get('/dashboard', passportConfig.isAuthenticated, dashController.index);
app.get('/test', passportConfig.isAuthenticated, testController.test);
// app.get('/contact', contactController.contact);
// app.post('/addContact', contactController.addContact);
// app.post('/editContact', contactController.editContact);
// app.get('/listContacts', contactController.listContacts);
// app.get('/listCurrency', currencyController.listCurrency);
// app.post('/addAccount', accountController.addAccount);
// app.post('/editAccount', accountController.editAccount);
// app.get('/listAccounts', accountController.listAccounts);
// app.get('/getTransactions', txController.getTransactions);
/**
* Error Handler.
*/
if (process.env.NODE_ENV === 'development') {
// only use in development
app.use(errorHandler());
} else {
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Server Error');
});
}
/**
* Start Express server.
*/
models.sequelize.sync({}).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'));
console.log('Press CTRL-C to stop\n');
});
});
module.exports = app;
user.js
const { promisify } = require('util');
const crypto = require('crypto');
const nodemailer = require('nodemailer');
const passport = require('passport');
const models = require('../models');
const { User } = models;
const randomBytesAsync = promisify(crypto.randomBytes);
/**
* GET /login
* Login page.
*/
exports.getLogin = (req, res) => {
if (req.user) {
return res.redirect('/');
}
res.render('account/login', {
title: 'Login'
});
};
/**
* POST /login
* Sign in using email and password.
*/
exports.postLogin = (req, res, next) => {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password cannot be blank').notEmpty();
req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
const errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/login');
}
passport.authenticate('local', (err, user, info) => {
if (err) { return next(err); }
if (!user) {
req.flash('errors', info);
return res.redirect('/login');
}
req.logIn(user, (err) => {
if (err) { return next(err); }
res.locals.user = user; //Updated code after comment suggestions
req.flash('success', { msg: 'Success! You are logged in.' });
res.redirect('/dashboard');
});
})(req, res, next);
};
/**
* GET /logout
* Log out.
*/
exports.logout = (req, res) => {
req.logout();
req.session.destroy((err) => {
if (err) console.log('Error : Failed to destroy the session during logout.', err);
req.user = null;
res.redirect('/');
});
};
/**
* GET /signup
* Signup page.
*/
exports.getSignup = (req, res) => {
if (req.user) {
return res.redirect('/');
}
res.render('account/signup', {
title: 'Create Account'
});
};
/**
* POST /signup
* Create a new local account.
*/
exports.postSignup = (req, res, next) => {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password must be at least 4 characters long').len(4);
req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
const errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/signup');
}
User.findAll({ limit: 1, where: { email: req.body.email }, plain: true })
.then((existingUser) => {
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
User.create({
email: req.body.email,
password: req.body.password
}).then((user) => {
req.logIn(user, (err) => {
if (err) { return next(err); }
res.locals.user = user; //updated code after comment suggestions
return res.redirect('/');
});
}).catch(error => next(error));
});
};
passport.js
const passport = require('passport');
const request = require('request');
const { Strategy: LocalStrategy } = require('passport-local');
const _ = require('lodash');
const models = require('../models');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
models.User.findAll({ where: { id }, limit: 1, plain: true })
.then(user => done(null, user))
.catch(err => done(err));
});
/**
* Sign in using Email and Password.
*/
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
models.User.findAll({ where: { email }, limit: 1, plain: true }).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.' });
});
}).catch(error => done(error));
}));
/**
* OAuth Strategy Overview
*
* - User is already logged in.
* - Check if there is an existing account with a provider id.
* - If there is, return an error message. (Account merging not supported)
* - Else link new OAuth account with currently logged-in user.
* - User is not logged in.
* - Check if it's a returning user.
* - If returning user, sign in and we are done.
* - Else check if there is an existing account with user's email.
* - If there is, return an error message.
* - Else create a new account.
*/
/**
* Login Required middleware.
*/
exports.isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
};
/**
* Authorization Required middleware.
*/
exports.isAuthorized = (req, res, next) => {
const provider = req.path.split('/').slice(-1)[0];
const token = req.user.tokens.find(token => token.kind === provider);
if (token) {
next();
} else {
res.redirect(`/auth/${provider}`);
}
};
In your app.js, you need to call const passportConfig = require('./config/passport'); after you initialize passport, like so:
app.use(passport.initialize());
app.use(passport.session());
// place it here
const passportConfig = require('./config/passport')(passport);
Further, you need to adjust your password config file (./config/passport), so that on module loading it can get the passport instance that you instantiated in app.js, and subsequently uses it instead of loading a new passport instance via require('passport') - this in my opinion messes things up; didn't test it though. :)
Can you share the user object sample which refer to this code in your passport.js LocalStrategy
user.comparePassword(password, (err, isMatch) => {....});
looks like user.id is not available in serializeuser below which may be the cause it cant set user.id in the session and cant deserialize User further not setting the user object in req
passport.serializeUser((user, done) => {
done(null, user.id);
});
I have a tremendous headche with a problem when I try to login using Passport.
I'm making a post request to /login with an email and password. Passport authenticates it correctly, err isn't called and then return res.redirect('/user') gets called too but it returns 404 and doesn't redirect.
I don't know why this is happening. Here's my code:
Server (index.js):
const
express = require('express'),
app = express(),
mongoose = require('mongoose'),
passport = require('passport'),
cookieSession = require('cookie-session'),
bodyParser = require('body-parser'),
keys = require('./config/keys'),
user = require('./models/User'),
passportService = require('./services/passport'),
authRoutes = require('./routes/auth');
mongoose.connect(keys.mongoURI);
app.use(bodyParser.json());
app.use(
cookieSession({
maxAge: 15 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(passport.initialize());
app.use(passport.session());
passportService(passport);
authRoutes(app);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
});
passportService (passport.js):
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = mongoose.model('users');
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
passport.use(
new LocalStrategy(
{
usernameField: 'emailaddr',
passwordField: 'passwd'
},
function(username, password, done) {
User.findOne({ email: username }, function(err, user) {
if(err){
return done(err);
}
if(!user) {
console.log('User not found with email: ', username);
return done(null, false);
}
if(user.password != password) {
console.log('Invalid password');
return done(null, false)
}
return done(null, user);
});
}));
}
Authentication route:
const passport = require('passport');
module.exports = app => {
app.post('/api/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/user');
});
})(req, res, next);
});
}
There is not route for /user because I'm working with React and React Router in the client.
Please I need your help!!!
I would suggest not using the custom callback on your authenticate function. Instead, check to see if after the api is called and authentication is run if you have a user attached to the request object.
// calling redirects from your api will be problematic with react router
// allow your front end to decide what route to call based on the response
app.post('/api/login', passport.authenticate('local'), function(req, res, next) {
if(req.user) {
res.json(req.user);
} else {
// handle errors here, decide what you want to send back to your front end
// so that it knows the user wasn't found
res.statusCode = 503;
res.send({message: 'Not Found'})
}
});