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)
})
})
Related
I am creating a mern stack chat application. I used google authentication using passport js. everything is working fine in the localhost. I can log in and the user get saved in the database but when I upload the frontend to Netlify it does not work. I can login and the user get saved in the Mongodb database but cookies doesn't get saved.
// passport.js middle ware
import * as dotenv from "dotenv";
import passport from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
import User from "./models/user.js";
dotenv.config();
passport.serializeUser((user, cb) => {
cb(null, user.id);
});
passport.deserializeUser((id, cb) => {
User.findById(id).then((user) => {
cb(null, user);
});
});
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
},
function (accessToken, refreshToken, profile, cb) {
console.log(" google strategy called");
User.findOne({ googleId: profile.id }).then((currentUser) => {
if (currentUser) {
// already have user
console.log("user find one called");
cb(null, currentUser);
} else {
// if not, create user in our db
new User({
name: profile.displayName,
googleId: profile.id,
email: profile.emails[0].value,
picture: profile.photos[0].value,
membership: "free",
})
.save()
.then((newUser) => {
cb(null, newUser);
});
}
});
}
)
);
// index.js
dotenv.config();
const app = express();
app.use(
cors({
origin: "https://taupe-starburst-7fab32.netlify.app",
methods: "GET,POST,PUT,DELETE",
credentials: true,
})
);
app.use(express.json());
Connection();
app.use(
cookieSession({
name: "session",
keys: [process.env.SESSION_SECRET],
// Cookie Options
domain: "https://taupe-starburst-7fab32.netlify.app",
maxAge: 24 * 60 * 60 * 1000, // 24 hour
secure: true,
httpOnly: true,
sameSite: "none",
})
);
// to solve session.regenarate issue
app.use(function (request, response, next) {
if (request.session && !request.session.regenerate) {
request.session.regenerate = (cb) => {
cb();
};
}
if (request.session && !request.session.save) {
request.session.save = (cb) => {
cb();
};
}
next();
});
app.use(passport.initialize());
app.use(passport.session());
app.use("/auth", router);
const server = app.listen(process.env.PORT || 5000, () =>
console.log("server running")
);
// auth.js
import express from "express";
import passport from "passport";
const router = express.Router();
//sunccess redirect
router.get("/login/success", (req, res) => {
if (req.user) {
res.status(200).json({
success: true,
message: "successfull",
user: req.user,
});
} else {
res.status(401).json({
success: false,
message: "Unauthorized: User is not logged in",
});
}
});
//failure redirect
router.get("/login/failed", (req, res) => {
res.status(401).json({
success: false,
message: "failure",
});
});
// authenticate
router.get(
"/google",
passport.authenticate("google", { scope: ["email", "profile"] })
);
// callback
router.get(
"/google/callback",
passport.authenticate("google", {
successRedirect: process.env.CLIENT_URL,
failureRedirect: "/login/failed",
})
);
//logout
router.get("/logout", (req, res) => {
req.logout(function (err) {
if (err) {
return next(err);
}
res.redirect(`${process.env.CLIENT_URL}/login`);
});
});
export default router;
// React Frontend
function Login() {
const google = () => {
window.open(`${process.env.REACT_APP_API_URL}auth/google`, '_self');
};
return (
<button type="button" onClick={google}>
Login with Google
</button>
);
}
export default Login;
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')
}
)
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);
});
Currently a user can sign in with github, and i can see it gets the req.user in the callback function, and on passport github file
console.log(`frontid ${req.user.id}`) // logs user id
and passport
console.log(`backbro ${id}`); // logs an id
however, when i go on this route, the github user returns null and im not sure why. There were a few times, i see the signed in github user return in the current_user route, however its rare that i see it now. It sometimes shows, sometimes dont. Kinda wierd. Could it be a session issue ?
router.get("/current_user", (req, res) => {
if(req.user){
res.status(200).send({ user: req.user});
} else {
res.json({ user:null})
}
});
yes i looked at the following links, still no suitable answer.
Node + Express + Passport: req.user Undefined
req.user undefined after twitter authentication using express sever, passport.js
The way im accessing this link, is by explictily calling
localhost:8000/api/users/auth/github
in the address bar.
routes/users
router.get('/auth/github', passport.authenticate('github', {
scope:[ 'profile', 'id']
}));
router.get('/auth/github/callback',
passport.authenticate('github', { session:true, failureRedirect: 'http:localhost:8001/signIn' }),
function(req, res) {
// 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);
// })
const user = req.user
req.logIn(user, err => {
models.User.findOne({
where: {
id: req.user.id,
},
}).then(user => {
const token = jwt.sign({ id: 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.redirect('http://localhost:8001');
// console.log(req.user)
});
});
console.log(`frontid ${req.user.id}`)
// res.redirect('')
// console.log('this works', token);
});
passport-github.js
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);
// });
module.exports = async (passport) => {
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.findOne({
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
return 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
.findOne({ where: { id: userId } })
.then(function(user){
// console.log(user);
return done(null, userId);
}).catch(function(err){
done(err, null);
});
// return done(null, id);
});
}
app.js
const express = require('express');
const app = express();
const userRoute = require('./routes/users');
const postRoute = require('./routes/posts');
const bodyParser = require('body-parser');
const logger = require('morgan');
const session = require('express-session');
const cookieParser = require('cookie-parser') ;
const dotenv = require('dotenv');
const env = dotenv.config();
const cors = require('cors');
const models = require('./models/');
const host = '0.0.0.0';
const PORT = process.env.PORT || 8000;
const passport = require('passport');
const path = require('path');
const Sequelize = require('sequelize');
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const proxy = require('express-http-proxy');
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();
});
app.use(cors({
origin: process.env.ALLOW_ORIGIN,
credentials: false,
allowedHeaders: 'X-Requested-With, Content-Type, Authorization',
methods: 'GET, POST, PATCH, PUT, POST, DELETE, OPTIONS',
exposedHeaders: ['Content-Length', 'X-Foo', 'X-Bar'],
}))
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')));
// We need a store in order to save sessions, instead of the sessions clearing out on us :)
require('./config/passport')(passport); // PASSPORT Init
require('./config/passport-github')(passport); // PASSPORT Init
app.use(cookieParser());
app.use(bodyParser.json());
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();
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({ extended:false}));
// this code may be useless or useful, still trying to understand cors.
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)
})
})
I'm having an issue with saving the session. It appears that logging in and logging out works fine. However, if making code changes or if the nodemon server refreshes, it renders null on the current_user route.
And then this error when making post requests.
Cannot read property 'id' of undefined
router.get("/current_user", (req, res) => {
if(req.user){
res.status(200).send({ user: req.user});
} else {
res.json({ user:null})
}
});
routes
const jwt = require('jsonwebtoken');
const passport = require('passport');
router.post('/loginUser', passport.authenticate('login', {session: true}), (req, res, next) => {
passport.authenticate('login', (err, user, info) => {
if (err) {
console.log(err);
}
if (info != undefined) {
console.log(info.message);
res.status(401).send(info.message);
} else {
req.logIn(user, err => {
models.User.findOne({
where: {
username: req.body.username,
},
}).then(user => {
const token = jwt.sign({ id: 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({
auth: true,
token: token,
message: 'user found & logged in',
});
// console.log(req.user)
});
});
}
})(req, res, next);
});
passport.js
const bcrypt = require('bcrypt'),
BCRYPT_SALT_ROUNDS = 12,
JWTstrategy = require('passport-jwt').Strategy,
ExtractJWT = require('passport-jwt').ExtractJwt,
Sequelize = require('sequelize'),
Op = Sequelize.Op,
models = require( '../models/'),
localStrategy = require('passport-local').Strategy;
// passport = require("passport");
// serialize session, only store user id in the session information
module.exports = async (passport) => {
passport.use(
'register',
new localStrategy(
{
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true,
session: false,
},
(req, username, password, done) => {
try {
models.User.findOne({
where: {
[Op.or]: [
{
username: username,
},
{ email: req.body.email },
],
},
}).then(user => {
if (user != null) {
console.log('username or email already taken');
return done(null, false, {
message: 'username or email already taken',
});
} else {
bcrypt.hash(password, BCRYPT_SALT_ROUNDS).then(hashedPassword => {
models.User.create({
username: req.body.username,
password: hashedPassword,
email: req.body.email
}).then(user => {
console.log('user created');
return done(null, user);
});
});
}
});
} catch (err) {
done(err);
}
},
),
);
passport.use(
'login',
new localStrategy(
{
usernameField: 'username',
passwordField: 'password',
session: false,
},
(username, password, done, req) => {
try {
models.User.findOne({
where: {
[Op.or]: [
{
username: username,
}
],
},
}).then(user => {
if (user === null) {
return done(null, false, { message: 'Username doesn\'t exist' });
} else {
bcrypt.compare(password, user.password).then(response => {
if (response !== true) {
console.log('passwords do not match');
return done(null, false, { message: 'passwords do not match' });
}
console.log('user found & authenticated');
// note the return needed with passport local - remove this return for passport JWT
return done(null, user);
});
}
});
} catch (err) {
done(err);
}
},
),
);
const opts = {
jwtFromRequest: ExtractJWT.fromAuthHeaderWithScheme('JWT'),
secretOrKey: process.env.JWT_SECRET,
};
passport.use(
'jwt',
new JWTstrategy(opts, (jwt_payload, done) => {
try {
models.User.findOne({
where: {
username: jwt_payload._id,
},
}).then(user => {
if (user) {
console.log('user found in db in passport');
// note the return removed with passport JWT - add this return for passport local
done(null, user);
// console.log(user);
} else {
console.log('user not found in db');
done(null, false);
}
});
} catch (err) {
done(err);
}
}),
);
passport.serializeUser(function(user, done) {
done(null, user.id);
console.log(user.id); // gets user id
});
// from the user id, figure out who the user is...
passport.deserializeUser(function(id, done){
models.User.findOne({
where: {
id,
},
}).then(user => done(null, user))
.catch(done);
});
}
app.js
var express = require('express');
var app = express();
var userRoute = require('./routes/users');
var postRoute = require('./routes/posts');
var bodyParser = require('body-parser');
var logger = require('morgan');
var session = require('express-session');
var cookieParser = require('cookie-parser') ;
var dotenv = require('dotenv');
var env = dotenv.config();
var cors = require('cors');
var models = require('./models/');
const host = '0.0.0.0';
const PORT = process.env.PORT || 8000;
const passport = require('passport');
const path = require('path');
// const allowOrigin = process.env.ALLOW_ORIGIN || '*'
// CORS Middleware
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());
app.use(session({
secret : process.env.JWT_SECRET,
}));
require('./config/passport.js')(passport); // PASSPORT Init
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({ extended:false}));
app.use(bodyParser.json());
// this code may be useless or useful, still trying to understand cors.
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', true);
res.header("preflightContinue", false)
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
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'));
})
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();
});
models.sequelize.sync().then(function() {
app.listen(PORT, host, () => {
console.log('[api][listen] http://localhost:' + PORT)
})
})
The question is not so much about saving the req.user, this is an issue with not having a store set in place.
app.use(session({
store: '', // enter a store
secret : process.env.JWT_SECRET,
}));
You're using Sequelize, so i would recommend you to look into this
https://github.com/mweibel/connect-session-sequelize
If all runs well, you will not have to worry about req.id becoming undefined everytime you make a code change. Hope this helps.
You can do something like
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const 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,
})
app.use(session({
store: myStore,
secret : process.env.JWT_SECRET,
}));