passportJS req.user is undefined (not deserializing) - node.js

I have followed the steps described in the documentation of passportJS to configure local authentication. I pretty much copy-pasted their code to test if it works.
However, I now have 2 (related?) problems:
deserializeUser is never being called
req.user is undefined
I tried pretty much everything (changing the middleware orders, putting withCredentials: true on the client side, ...) that is mentioned in related posts, but nothing seems to work. Does someone maybe see what I'm doing wrong here?
index.js
app.use(express.json()); // to send data to the database
app.use(express.urlencoded({ extended: true }));
app.use(
cors({
origin: "http://localhost:3000", // location of the react app
credentials: true,
})
);
app.use(
session({
secret: "secretcode",
resave: false,
saveUninitialized: false,
})
);
app.use(passport.session());
app.use(cookieParser("secretcode"));
app.use(passport.initialize());
config(passport);
app.post("/login", passport.authenticate('local', { failureRedirect: '/login', failureMessage: true }),
function(req, res) {
res.redirect('/~' + req.user.username);
});
app.get("/user", (req, res) => {
res.send(req.user);
});
config.js
export const config = (passport) => {
passport.use(new LocalStrategy(function verify(username, password, cb) {
db.query('SELECT * FROM users WHERE username = ?', [ username ], function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false, { message: 'Incorrect username or password.' }); }
bcrypt.compare(password, user[0].password, (err, result) => {
if (err) { return cb(err); }
if (result === true) {
return cb(null, user[0]);
}
return cb(null, false, { message: 'Incorrect username or password.' });
});
});
}));
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user.id);
});
});
passport.deserializeUser(function(id, cb) {
db.query('SELECT * FROM users WHERE id = ?', [ id ], function(err, user) {
if (err) { return cb(err); }
return cb(null, user);
});
});
};

I believe that your problem may be because you have not included app.use(passport.authenticate('session')). Try that.
You can also debug further by passing a path into the ensureLogIn middleware as I've had problems with that in the past. It helps debug if you know what is being redirected.
Please find a working barebones attached here for a working passport-local and express example without all of the bloat. I think comparing this with your application would be the best bet.
Let me know if this helps.
const express = require('express')
const passport = require('passport')
const LocalStrategy = require('passport-local')
const session = require('express-session')
const ensureLogIn = require('connect-ensure-login').ensureLoggedIn
const cookieParser = require('cookie-parser')
const app = express()
const port = 3000
const ensureLoggedIn = ensureLogIn('/not-logged-in')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}))
app.use(passport.authenticate('session'))
app.use(passport.initialize())
app.use(cookieParser())
app.get('/', (req, res) => {
res.json({ hello: 'world' })
})
app.all('/secure', ensureLoggedIn, (req, res) => res.json({ is: 'secure' }))
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: true
},
function (username, password, cb) {
if (username !== 'hello' && password !== 'world') {
console.log('verify', username, password, 'fail')
return cb(null, false)
} else {
console.log('verify', username, password, 'pass')
return cb(null, { username: 'hello' })
}
}
))
passport.serializeUser(function (user, cb) {
process.nextTick(function () {
console.log('serializeUser', user)
cb(null, user)
})
})
passport.deserializeUser(function (user, cb) {
process.nextTick(function () {
console.log('deserializeUser', user)
return cb(null, user)
})
})
app.post('/logins',
passport.authenticate('local', {
// successReturnToOrRedirect: '/',
failureRedirect: '/login-fail',
failureMessage: true
}),
function (req, res) {
console.log('logged in', req.params, req.user)
// res.send('Logged in')
res.redirect('/secure')
}
)

Related

How to use connect-flash with passport-local

hello every one i was trying to use connect-flash with passport local but can not seem to find relevant info either on the docs and other sources,if any one can guide me on this and tell me what i went wrong ,thanks.And why does request to login-fail gives this error:-
ERROR is AxiosError: Request failed with status code 404
but when i test the route with rest client on vscode it responses perfectly like seen
here.
also when i check the database right after i click submit on the route
there will be two sessions stored consecutively one having the flash(the first) and the other not like this:-
session:
{"cookie":{"originalMaxAge":259200000,"expires":"2023-01-16T12:30:14.395Z","secure":false,"httpOnly":true,"path":"/"},"flash":{"error":["Incorrect password"]}}
session:
{"cookie":{"originalMaxAge":259200000,"expires":"2023-01-16T12:30:14.453Z","secure":false,"httpOnly":true,"path":"/"}}
why this is happening?
userRoutes.js
const router = require("express").Router();
const passport = require("passport");
const { ObjectID } = require("mongodb");
const LocalStrategy = require("passport-local");
const bcrypt = require("bcrypt");
const flash = require("connect-flash");
const User = require("../models/userModel.js");
router.route("/register").post(
(req, res, next) => {
User.findOne({ fullName: req.body.fullName }, (err, user) => {
if (err) {
next(err);
} else if (user) {
res.redirect("/");
} else {
const {
fullName,
email,
id,
department,
stream,
batch,
sex,
age,
phoneNumber,
password,
} = req.body;
const hash = bcrypt.hashSync(password, 12);
console.log(hash);
const newUser = new User({
fullName,
email,
id,
department,
stream,
batch,
sex,
age,
phoneNumber,
password: hash,
});
newUser.save((err, data) => {
if (err) res.redirect("/");
next(null, data);
});
}
});
},
passport.authenticate("local", { failureRedirect: "/" }),
(req, res, next) => {
res.json({ user: "i am josh" });
}
);
router
.route("/login")
.post(
passport.authenticate("local", {
failureRedirect: "/login-fail",
failureFlash: true,
}),
(req, res) => {
res.json({ user: "i am josh" });
}
);
router.route("/login-fail").get((req, res) => {
// console.log(req.flash('error'));
res.json({ user: "josh" });
});
router.route("/logout").get((req, res) => {
req.logout();
res.redirect("/");
});
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
User.findOne({ _id: new ObjectID(id) }, (err, doc) => {
done(null, doc);
});
});
const customFields = {
usernameField: "email",
};
passport.use(
new LocalStrategy(customFields, (email, password, done) => {
User.findOne({ email }, (err, user) => {
//console.log(`User ${user.username} attempted to log in.`);
if (err) return done(err);
if (!user) return done(null, false, { message: "email does not exist" });
if (!bcrypt.compareSync(password, user.password)) {
return done(null, false, { message: "Incorrect password" });
}
return done(null, user);
});
})
);
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.json({ msg: "You are not authorized to view this page" });
}
module.exports = router;
server.js
require("dotenv").config();
const express = require("express");
const app = express();
const flash = require("connect-flash");
const session = require("express-session");
const passport = require("passport");
const mongoose = require("mongoose");
const MongoStore = require("connect-mongo");
const cors = require("cors");
app.use(express.json());
// app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(flash());
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
store: MongoStore.create({
mongoUrl: process.env.DB,
collection: "sessions",
}),
cookie: { secure: false, maxAge: 1000 * 60 * 60 * 24 * 3 },
})
);
app.use(passport.initialize());
app.use(passport.session());
mongoose
.connect(process.env.DB)
.then(() => {
console.log("DB connection successful");
})
.catch((err) => {
console.log(`DB connection Error: ${err}`);
});
const usersRouter = require("./routes/userRoutes");
app.use("/user", usersRouter);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`server is running on port ${PORT}`);
});
module.exports = app;
ya,have seen the same question here on stack over flow but none of the answers work

CAS integration with node js using passport-cas

I am using passport-cas to implement CAS 2.0 on my web server. I am trying to create a dummy environment where I just implement login functionality. It seems that the CAS login implementation is successful, I get redirected to the CAS login page but upon logging in the URL I am getting is http://localhost:3000/cas?ticket=ST-439273-mNA1Jeea4lEjK5vzJJuJmHHZ72w-vm-cas6prdapp-01 instead of redirecting to a page. I am not sure what is going on and can't find any similar answers online.
Here is my code containing the configuration of passport.
const app = express()
const CasStrategy = require('passport-cas').Strategy
const cors = require('cors')
const passport = require('passport')
const session = require('express-session')
const authRoute = require("./routes/auth.js");
app.use(session({
secret: "this is totally secret",
resave: false,
saveUninitialized: false,
}))
app.use(passport.initialize());
app.use(passport.session());
app.use(cors({
origin: "http://localhost:3000",
methods: "GET, POST,PUT, DELETE",
credentials: true,
}))
// app.use("/auth", authRoute)
passport.use(new CasStrategy({
version: 'CAS3.0',
ssoBaseURL: 'https://secure.its.yale.edu/cas',
serverBaseURL: 'http://localhost:3000/cas',
validateURL: '/validate'
}, function(profile, done) {
var login = profile.user;
User.findOne({login: login}, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {message: 'Unknown user'});
}
return done(null, user);
});
}));
passport.serializeUser(function (user, done) {
done(null, user.netId);
});
passport.deserializeUser(function (netId, done) {
done(null, {
netId,
});
});
const casLogin = function (
req, res, next
) {
passport.authenticate('cas', function (err, user, info) {
if (err) {
return next(err);
}
if (!user) {
req.session.messages = info.message;
return res.redirect('/check');
}
req.logIn(user, function (err) {
if (err) {
return next(err);
}
req.session.messages = '';
return res.redirect('/after');
});
})(req, res, next);
};
app.get("/cas", casLogin)
app.listen("5000", () =>{
console.log("Server is running on port 5000");
})

Setting up passport for the first time. isAuthenticated() always returning false

I'm working through a learning module on authentication and security and I'm trying to get passport up and running but I seem to be having trouble. The code included below all works as you'd expect, except that when users are redirected from the /register post route to the /secrets route, they are not authenticated, in spite of .register() having worked (otherwise the logic in the route would have redirected me back to the /register get route instead of the login page via the secrets route).
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const ejs = require("ejs");
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static("public"));
app.set("view engine", "ejs");
app.use(
session({
secret: "Our little secret.",
resave: false,
saveUninitialized: false
})
);
app.use(passport.initialize());
app.use(passport.session());
mongoose.connect("mongodb://localhost:27017/userDB", { useNewUrlParser: true });
mongoose.set("useCreateIndex", true);
const userSchema = new mongoose.Schema({
username: String,
password: String
});
userSchema.plugin(passportLocalMongoose);
const Users = new mongoose.model("Users", userSchema);
passport.serializeUser(Users.serializeUser());
passport.deserializeUser(Users.deserializeUser());
app.listen(3000, (req, res) => {
console.log("Listening on port 3000.");
});
app.get("/", (req, res) => {
res.render("home");
});
app.get("/login", (req, res) => {
res.render("login");
});
app.get("/register", (req, res) => {
res.render("register");
});
app.get("/secrets", (req, res) => {
console.log(req.isAuthenticated())
if (req.isAuthenticated()) {
res.render("secrets");
} else {
res.redirect("/login");
}
});
app.post("/register", (req, res) => {
console.log(req.body.username)
console.log(req.body.password)
Users.register(
{ username: req.body.username },
req.body.password,
(error, user) => {
if (error) {
console.log('there was an error: ', error);
res.redirect("/register");
} else {
passport.authenticate("local")(req, res, () => { //////////////not authenticating
res.redirect("/secrets");
});
}
}
);
});
app.post("/login", (req, res) => {});
any help figuring out why isAuthenticaed() is returning false would be greatly appreciated. thank you :)
You have to define your local strategy :
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
here it has queried User information from the database ( Mongodb for instance) in case of successful response it will pass.

Passport calls deserializeUser for every request with NextJs and Express

I'm implementing a simple login for my node app with Passport Local, Express, Next.js, and MongoSession store.
Everything works well, except my app runs deserializeUser for every single request. This results in my db being hit 10+ times for any app interaction
Based on this post https://github.com/jaredhanson/passport/issues/14#issuecomment-4863459 I know that my requests for static assets are hitting the middleware stack.
Most of the requests are for the path /_next/static*
I have tried and failed to implement express.static as shown in the above example. Please help me figure out how avoid calling deserializeUser on every request.
Thanks!
Here's my code:
app.js
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(async () => {
const server = express();
server.use(helmet());
server.use(express.static(path.join(__dirname, '_next', 'static')));
server.use(express.json());
auth({ ROOT_URL, server });
api(server);
routesWithSlug({ server, app });
sitemapAndRobots({ server });
server.get('*', (req, res) => {
const url = URL_MAP[req.path];
if (url) {
app.render(req, res, url);
} else {
handle(req, res);
}
});
server.listen(port, (err) => {
if (err) throw err;
logger.info(`> Ready on ${ROOT_URL}`);
});
});
module.exports = { app };
auth.js
function auth({ ROOT_URL, server }) {
const dev = process.env.NODE_ENV !== 'production';
const MongoStore = mongoSessionStore(session);
const sess = {
name: 'builderbook.sid',
secret: process.env.sessSecret,
store: new MongoStore({
mongooseConnection: mongoose.connection,
ttl: 14 * 24 * 60 * 60, // expires in 14 days
}),
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
maxAge: 14 * 24 * 60 * 60 * 1000, // expires in 14 days
},
};
if (!dev) {
server.set('trust proxy', 1);
sess.cookie.secure = true;
}
server.use(session(sess));
server.use(passport.initialize());
server.use(passport.session());
server.use(bodyParser.urlencoded({ extended: false }));
passport.serializeUser((user, done) => {
console.log('serializeUser');
done(null, user.id);
});
passport.deserializeUser((id, done) => {
console.log(`deserializeUser, id: ${id}`);
User.findById(id, User.publicFields(), (err, user) => {
done(err, user);
});
});
const verifyLocal = async (req, email, password, done) => {
console.log({ email, password, req });
const { firstName, lastName } = req.body;
try {
// signInOrSign up the user to MongoDb
const user = await User.signInOrSignUp({
email,
password,
firstName,
lastName,
});
console.log(user);
if (!user) {
return done(null, false);
}
if (!User.verifyPassword(email, password)) {
return done(null, false);
}
return done(null, user);
} catch (err) {
console.log(err); // eslint-disable-line
return done(err);
}
};
passport.use(
new LocalStrategy(
{
usernameField: 'email',
passReqToCallback: true,
},
verifyLocal,
),
);
}
module.exports = auth;
authroutes.js
router.post('/login', passport.authenticate('local', { failureRedirect: '/fail' }), (req, res) => {
res.redirect('/');
});
router.get('/logout', (req, res) => {
req.logout();
res.redirect('/login');
});
module.exports = router;
This code seemed to solve the problem. Thanks to Tima over at Builderbook!
https://github.com/builderbook/builderbook/issues/229
server.get('/_next*', (req, res) => {
handle(req, res);
});
server.get('/static/*', (req, res) => {
handle(req, res);
});

passport-github cannot read property of id sometimes

passport-github is having a hard time knowing the req.user. The following code worked a few minutes ago, now im getting this error
TypeError: Cannot read property 'id' of undefined
shows on this line
var token = jwt.sign({ id: req.user.id}, process.env.JWT_SECRET );
I don't have this issue with passport local strategy, and i do have a store configured. Could it be an issue with the serialization ?
Or maybe because im testing the route path so much that after a while the github api stops working ?
routes/users.js
router.get('/auth/github', passport.authenticate('github', {
session:true,
scope:[ 'id', 'profile']
}));
router.get('/auth/github/callback', (req, res, next) => {
passport.authenticate('github', (user) => {
// Successful authentication, redirect home.
var token = jwt.sign({ id: req.user.id}, process.env.JWT_SECRET );
// res.cookie("jwt", token, { expires: new Date(Date.now() + 10*1000*60*60*24)});
jwt.verify(token, process.env.JWT_SECRET, function(err, data){
console.log(err, data);
})
res.status(200).send({message:"github user signed in", auth: true});
// console.log(`frontid ${req.user.id}`)
// res.redirect('')
console.log('this works', token);
})(req, res, next);
});
passport-github.js
const passport = require("passport");
const GitHubStrategy = require('passport-github2').Strategy;
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const models = require("../models/");
// passport.serializeUser((user, done) => {
// // push to session
// done(null, user.id);
// console.log(user.id)
// });
// passport.deserializeUser((id, done) => {
// models.User.findOne({
// where: {
// id,
// },
// }).then(user => done(null, user))
// .catch(done);
// });
passport.use(
new GitHubStrategy(
{
clientID: process.env.clientID,
clientSecret: process.env.secret,
callbackURL: 'http://127.0.0.1:8000/api/users/auth/github/callback',
passReqToCallback: true,
profileFields: ['id', 'login']
},
(req, accessToken, refreshToken, profile, done) => {
const { id, login, email} = profile._json;
console.log(`backbro ${id}`);
// console.log(req)
models.User.find({
where:{
id: id
}
}).then( user => {
// if user is found
if(user){
return done(null, user)
}
// else create new user
else{
models.User.create({
id: id,
username:login,
email: email,
createdAt: Date.now()
}).then( user => {
console.log('github user created');
return done(null, user);
})
}
})
}
)
);
passport.serializeUser((user, done) => {
// push to session
done(null, user.id);
});
passport.deserializeUser((userId, done) => {
// console.log('calling deserial' + userId);
// // TODO: findByPk syntax? findById deprecated? Try later after sucessfully record data in DB
models.User
.find({ where: { id: userId } })
.then(function(user){
// console.log(user);
return done(null, userId);
}).catch(function(err){
done(err, null);
});
// return done(null, id);
});
module.exports = passport;
routes/current_user
router.get("/current_user", (req, res) => {
if(req.user){
res.status(200).send({ user: req.user});
} else {
res.json({ user:null})
}
});
app.js
var sequelize = new Sequelize(
process.env.POSTGRES_DB,
process.env.POSTGRES_USER,
process.env.POSTGRES_PASSWORD,{
"dialect": "sqlite",
"storage": "./session.sqlite"
});
myStore = new SequelizeStore({
db:sequelize,
})
if (!process.env.PORT) {
require('dotenv').config()
}
// console.log(process.env.DATABASE_URL);
if (!process.env.PORT) {
console.log('[api][port] 8000 set as default')
console.log('[api][header] Access-Control-Allow-Origin: * set as default')
} else {
console.log('[api][node] Loaded ENV vars from .env file')
console.log(`[api][port] ${process.env.PORT}`)
console.log(`[api][header] Access-Control-Allow-Origin: ${process.env.ALLOW_ORIGIN}`)
}
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'build')));
app.use(cookieParser());
// We need a store in order to save sessions, instead of the sessions clearing out on us :)
app.use(session({
store: myStore,
saveUninitialized: false,
resave:false,
cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 }, // 30 days
secret : process.env.JWT_SECRET,
}));
myStore.sync();
require('./config/passport')(passport); // PASSPORT Init
require('./config/passport-github'); // PASSPORT Init
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({ extended:false}));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.locals.user = req.user; // This is the important line
// req.session.user = user
console.log(res.locals.user);
next();
});
// this code may be useless or useful, still trying to understand cors.
app.use((req, res, next) => {
const { headers } = req;
res.header('Access-Control-Allow-Origin', headers.origin);
res.header('Access-Control-Allow-Headers', headers);
res.header('Access-Control-Allow-Credentials', true);
next();
});
app.use(cors({
origin: process.env.ALLOW_ORIGIN,
credentials: true,
allowedHeaders: 'X-Requested-With, Content-Type, Authorization',
methods: 'GET, POST, PATCH, PUT, POST, DELETE, OPTIONS'
}))
app.use('/api/users', userRoute );
app.use('/api/posts', postRoute );
// In order to use REACT + EXPRESS we need the following code, alone with a build
// in the client folder we run a npm run build in the client folder then it is referred
// in the following code.
app.use(express.static(path.join(__dirname, 'client/build')));
if(process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, 'client/build')));
//
app.get('*', (req, res) => {
res.sendfile(path.join(__dirname = 'client/build/index.html'));
})
}
//build mode
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/public/index.html'));
})
models.sequelize.sync().then(function() {
app.listen(PORT, host, () => {
console.log('[api][listen] http://localhost:' + PORT)
})
})

Resources