In the code below, when I visit / the first time (not logged onto FB) I'm redirected to the /login page, as expected.
Then I login onto facebook and I'm then taken to /, as expected
If I then refresh the page (while on /) I'm taken back the /login page, as if the session is already over.
It looks like for every new page view of / I'm always asked to login every time.
What am I doing wrong ?
(for testing purposes I'm not yet using a real database and have just set up this preliminary user.js one for the sake of the session..)
user.js
import fs from 'fs'
if (!fs.existsSync('./user.json')) {
fs.writeFileSync('./user.json', JSON.stringify(null))
}
export default {
query(facebookId) {
let user = JSON.parse(fs.readFileSync('./user.json', 'utf8'));
if (user && user.facebookId === facebookId) {
return user
}
return null
},
save(user) {
fs.writeFileSync('./user.json', JSON.stringify(user, null, 2))
return user
},
delete() {
fs.writeFileSync('./user.json', JSON.stringify(null, null, 2))
return true
}
}
server.js
import { Strategy as FacebookStrategy } from 'passport-facebook'
import express from 'express'
import passport from 'passport'
import session from 'express-session'
import fetch from 'node-fetch'
import { fileURLToPath } from 'url'
import { dirname } from 'path'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import { ensureLoggedIn } from 'connect-ensure-login'
import FB from './fb'
import USER from './user'
import bodyParser from 'body-parser'
const jsonParser = bodyParser.json()
const {
PORT,
INSTAGRAM_USER_ID,
FACEBOOK_APP_ID,
FACEBOOK_APP_SECRET,
FACEBOOK_PAGE_NAME
} = process.env
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const app = express()
app.use(cors({
origin: '*',
credentials: true,
optionSuccessStatus: 200
}))
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', `http://localhost:${PORT}`)
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization')
res.header('Access-Control-Allow-Methods', 'POST, GET')
next()
})
app.use(cookieParser())
app.use(bodyParser.json({limit: "50mb"}));
app.use(bodyParser.urlencoded({limit: "50mb", extended: true, parameterLimit:50000}))
app.use(session({ secret: 'googoogaga', resave: false, saveUninitialized: false }))
app.use(passport.initialize())
app.use(passport.session())
passport.use(new facebookStrategy({
clientID : FACEBOOK_APP_ID,
clientSecret : FACEBOOK_APP_SECRET,
callbackURL : `http://localhost:${PORT}/facebook/callback`,
profileFields: ['id', 'displayName']
}, function(accessToken, refreshToken, profile, done) {
let user = USER.query(profile.id)
if (user) {
return done(null, user)
} else {
user = USER.save({
facebookId: profile.id,
accessToken
})
return done(null, user);
}
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
app.get('/auth/facebook', passport.authenticate('facebook', { scope:'email,instagram_basic,pages_show_list' }));
app.get('/facebook/callback', passport.authenticate('facebook', {
successRedirect : '/',
failureRedirect : '/login'
}));
app.get('/login', (req,res) => {
res.sendFile(__dirname + '/public/login.html');
})
app.get('/', ensureLoggedIn('/login'), (req,res) => {
res.status(200).send({ user: req.user })
})
app.get('/logout', function(req, res){
req.session.destroy(()=>{
USER.delete()
res.redirect('/login')
})
})
app.listen(PORT)
It was because of nodemon.
Every time the user logs in I update some files and I forgot to tell nodemon to ignore those changes so the server restarts every time due to those file changes.
Whenever the server restarts the session is also reset.
Be careful with nodemon, make sure you place any files your server will be changing inside a folder and tell nodemon to ignore it.
On the other hand, a server might restart for other reasons as well. Why should the session be reset every time ? Isn't there a way around it ?.
Related
I am aware that there are there are loads of questions already on this particular topic and I have gone through all of them and none of the solutions have help so I am aware that this may be a local issue or something very obvious that I'm missing, however any help or tips that anyone could provide would be much appreciated.
I am using passport.js to authenticate users in my express app. The authentication doesn't seem to raise any errors and seems to execute correctly as the users are stored in the session data in the db, e.g.
session: {"cookie":{"originalMaxAge":3600000,"expires":"2023-01-18T15:00:13.646Z","secure":false,"httpOnly":true,"path":"/"},"passport":{"user":"63c69e118d89ef5921231f75"}}
Despite this, if I try to access req.user in any of the controller modules, it appears undefined.
I will paste my code below:
app.js
// Declare imported packages and modules
const express = require('express');
const MongoStore = require('connect-mongo');
const passport = require('passport');
const methodOverride = require('method-override');
const session = require('express-session');
const staticDirectory = require('serve-static');
require('dotenv').config({ path: './config/config.env' });
const dbConnect = require('./config/db');
const authRouter = require('./routes/authRoutes');
const moviesRouter = require('./routes/moviesRoutes');
const userRouter = require('./routes/userRoutes');
const errorHandler = require('./middleware/errorHandler');
// Declare variables for server
const HOSTNAME = process.env.HOST || 'localhost';
const PORT = process.env.PORT || 3000;
// Instantiate express app
const app = express();
// Add middleware to instantiated app
app.use(express.json());
app.use(staticDirectory(__dirname + '/public'));
app.use(methodOverride('_method')); // TODO: read about method override
// Add session middleware and declare session parameters
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: { maxAge: 1000 * 60 * 60 },
store: MongoStore.create({
mongoUrl: `${process.env.DB_SERVER}/${process.env.database}`,
collection: 'sessions'
})
}));
// Add passport middleware
app.use(passport.initialize());
app.use(passport.session());
// Add routes to defined endpoints
app.use('/auth', authRouter);
app.use('/user/favourites', userRouter);
app.use('/', moviesRouter);
// Add error-handling middleware
app.use(errorHandler);
// Connect the db
dbConnect();
// Run the server
app.listen({ path: HOSTNAME, port: PORT }, (error) => {
if (error) return console.log(error);
console.log(`Server is running on port ${PORT}...`);
});
passport-config.js
onst LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const User = require('../models/User');
// Middleware function to initialise passport
const initialize = (passport) => {
// Defining the fields used to login
const customFields = {
usernameField: 'email',
passwordField: 'password'
};
// Function to determin how authentication is handled
const authenticateUser = async (email, password, done) => {
// Finding user in the db
const user = await User.findOne({ email: email });
if (!user) {
// No user found returns an error
return done(null, false, { message: `No user with email ${email} found` });
}
try {
// Checking password entered for user hashed against the stored hash
if (await bcrypt.compare(password, user.hash)) {
return done(null, user);
} else {
// return an error if the hashes don't match
return done(null, false, { message: 'Password incorrect' });
}
} catch (error) {
return done(error);
}
}
passport.use(new LocalStrategy(customFields, authenticateUser));
// Built-in passport.js method to add user id in session cookie info
passport.serializeUser((user, done) => {
return done(null, user.id);
});
// Built-in passport.js method to extract user id from session to obtain user info
passport.deserializeUser(async (id, done) => {
const user = await User.findOne({ _id: id });
return done(null, user);
})
}
module.exports = initialize;
In my authControllers file, I have tried a couple of options:
exports.login = (req, res, next) => {
try {
// Login using passport authenticate function
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/'
})(req, res, next);
} catch (error) {
next(error);
}
}
and
exports.login = (req, res, next) => {
try {
// Login using passport authenticate function
passport.authenticate('local', (error, user, info) => {
if (error) return next(error);
if (!user) return res.redirect('/');
req.login(user, (err) => {
if (err) return next(err);
res.redirect('/');
})
})(req, res, next);
} catch (error) {
next(error);
}
}
I have also tried importing cors module and adding
app.use(
cors({
origin: `http://localhost:${process.env.PORT}`,
credentials: true,
})
);
and also adding
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", req.headers.origin);
res.header("Access-Control-Allow-Credentials", true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
if ('OPTIONS' == req.method) {
return res.send(200);
}
next();
});
I have also tried adding { session: true } to the options for passport.authenticate(), switching from express-session to cookie-session, adding bodyParser middleware, adding cookieParser middleware. I checked the order of app.use(session({...})) and the passport functions and tried removing some of the middleware to see if it was interfering. I also tried manually saving the user in the login function and adding the user to res.locals and without results. And also importing passport-config.js after the passport functions:
app.use(passport.initialize());
app.use(passport.session());
const initializePassport = require('./config/passport-config')(passport);
All of which without results.
Sorry for the long question and as I said, any insight on what is going wrong here would be much appreciated. Thanks!
This is my folder structure:
/app
server.js
/public
index.html
script-a.js
/scripts
script-b.js
These are the relevant contents of my index.html:
<!-- index.html -->
.....
...
<script scr="/script-a.js"></script>
<script scr="/script-b.js"></script>
These are the relevant contents of server.js:
import express from 'express'
import session from 'express-session'
import NedbStore from 'nedb'
import passport from 'passport'
import { Strategy as FacebookStrategy } from 'passport-facebook'
import { ensureLoggedIn } from 'connect-ensure-login'
....
..
const db = new NedbStore({ filename: `${__dirname}/db/nedb.db`, autoload: true })
const DB = dbAsyncWrapper(db)
app.use(cors({
origin: '*',
credentials: true,
optionSuccessStatus: 200
}))
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:4200')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization')
res.header('Access-Control-Allow-Methods', 'POST, GET')
next()
})
app.use(cookieParser())
app.use(express.json({ limit: '50mb' }))
app.use(express.urlencoded({ extended: true, limit: '50mb' }))
app.use(session({
secret: 'googoogaga',
resave: false,
saveUninitialized: false
}))
app.use(passport.initialize())
app.use(passport.session())
passport.use(new FacebookStrategy({
clientID : FACEBOOK_APP_ID,
clientSecret : FACEBOOK_APP_SECRET,
callbackURL : "http://localhost:4200/facebook/callback",
profileFields : ['id']
}, async(accessToken, refreshToken, profile, done) => {
let facebookId = profile.id
let userInDb = await DB.getUser()
if (userInDb && userInDb.facebookId === facebookId) {
await DB.updateUser({ accessToken })
done(null, userInDb)
} else {
let newUser = await DB.updateUser({ facebookId, accessToken })
done(null, newUser)
}
}))
passport.serializeUser(function(user, done) {
done(null, user)
})
passport.deserializeUser(function(user, done) {
done(null, user)
})
app.use('/', ensureLoggedIn('/auth/facebook'), express.static(__dirname + '/public'))
app.get('/auth/facebook', passport.authenticate('facebook', { scope:'email' }))
app.get('/facebook/callback', passport.authenticate('facebook', {
successRedirect : '/',
failureRedirect : '/auth/facebook'
}))
With the above code, I'm expecting to:
go to /
be redirected to facebook for login
go back to /
have index.html served and loaded
have script-a.js and script-b.js load correctly
What happens instead is:
I go to / and I'm redirected to /auth/facebook with a 302 status code.
If I remove:
app.use('/', ensureLoggedIn('/auth/facebook'), express.static(__dirname + '/public'))
and instead declare every single route handler manually, everything works:
....
..
app.get('/', ensureLoggedIn('/auth/facebook'), (req,res) => {
res.sendFile(__dirname + '/public/index.html')
})
app.get('/script-a.js', ensureLoggedIn('/auth/facebook'), (req,res) => {
res.sendFile(__dirname + '/public/script-a.js')
})
app.get('/script-b.js', ensureLoggedIn('/auth/facebook'), (req,res) => {
res.sendFile(__dirname + '/public/scripts/script-b.js')
})
If your auth is implemented by this:
app.use('/public', ensureLoggedIn('/auth/facebook'));
Then, you either run that before your express.static() route like this:
app.use(ensureLoggedIn('/auth/facebook'));
app.use(express.static(__dirname + '/public'));
Or, you incorporate it specifically into the static route:
app.use(ensureLoggedIn('/auth/facebook'), express.static(__dirname + '/public'));
When I also include the first 2 lines, not even the index.html gets served. I only get a failed facebook redirect registered on the network panel.
Then, your auth middleware is either correctly denying access to an unauthorized client or your auth middleware is not working properly. There's nothing in your question that indicates which that might be or could allow us to diagnose that for you.
But all other static files fail to be served.
For us to help you with that, we'd have to see the specifics of a static file that is not serving correctly (URL in the web page, location of the resource in your file system, etc...). There are 4-5 common mistakes people make to get static files to work properly. The most common is just using the wrong URL in the web page for static resources such as images, CSS files, script files, etc...
Unfortunately, the only solution I found was to turn my /public folder into a flat structure and do:
app.get('/', ensureLoggedIn('/auth/facebook'), (req,res) => {
res.sendFile(__dirname + '/public/index.html')
})
app.get('/:filename', ensureLoggedIn('/auth/facebook'), (req,res) => {
res.sendFile(__dirname + `/public/${req.params.filename}`)
})
The Problem
I am setting up my NodeJS & Express App, using Passport for authentication with Google Sign In and Facebook Login. Everything works very well when working on localhost, but when I deploy my app (to Vercel) I can't make the sessions work.
The signin process works very well, and I can see the user information attached to req.session, and req.user contains all user information.
However, right after that, when calling any route, req.user is undefined
The code
//Setting up the app
const express = require('express');
//const https = require('https');
//Bodyparser to handle JSON
const bodyParser = require('body-parser');
//Mongoose to connect to MongoDB
const mongoose = require('mongoose');
//To handle environments
const keys = require('./keys')
//To handle user sessions
var session = require("express-session");
var MongoDBStore = require('connect-mongodb-session')(session);
//Passport to hangle Google Sign In
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
//Authentication: serialize and deserialize user
passport.serializeUser(function(user, done) {
console.log(`serialize: user:${user}`);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
console.log(`deserialize user:${user}`)
done(err, user);
});
});
//Authentication: Google Sign In
passport.use(new GoogleStrategy({
clientID: keys.GOOGLE_CLIENTID,
clientSecret: keys.GOOGLE_SECRET,
callbackURL: keys.URL_GOOGLE_CALLBACK
},
function callback(accessToken, refreshToken, profile, done) {
process.nextTick( ()=> {
User.findOne({'googleId': profile.id}, (err,user)=> {
if(err)
return done(err,false);
if(user){
return done(null, user);
}
else{
var newUser = new User();
newUser.googleId = profile.id;
newUser.googleToken = accessToken;
newUser.firstName = profile.name.givenName;
newUser.email = profile.emails[0].value;
newUser.authType = "Google";
newUser.image = profile.photos[0].value;
newUser.save((err)=> {
if(err)
throw err;
return done(null, newUser);
});
}
});
});
}
));
//Importing the models used
const Country = require('./models/country');
const Place = require('./models/place');
const User = require('./models/user');
const Trip = require('./models/trip');
//Starting the app
const app = express();
//Using body parser as global middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }))
//Using cookie bodyParser
app.use(require('cookie-parser')('keyboard cat'))
//Using Cors for routes
//app.use(cors())
//Store for sessions
var storeSessions = new MongoDBStore({
uri: keys.URI_MONGO,
collection: 'sessions'
});
// Catch errors
storeSessions.on('error', function(error) {
console.log(error);
});
//Added but not sure if useful
app.use(cors({credentials: true, origin: true}));
app.set('trust proxy', 1);
//Setting up a session
app.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: false,
proxy: true,
store: storeSessions,
cookie : {
maxAge: 2419200000
}
}));
//Using passport for authentication
app.use(passport.initialize());
app.use(passport.session());
//Solving CORS issues
app.use((req, res, next) => {
let allowedOrigins = ['http://localhost:3000', 'http://localhost:8080', 'mywebsite.com'];
let origin = req.headers.origin;
if(allowedOrigins.indexOf(origin) > -1){
res.setHeader('Access-Control-Allow-Origin', origin);
}
//res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content, Accept, Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
res.header('Access-Control-Allow-Credentials', true);
next();
});
//Connection to MongoDB
mongoose.connect(keys.URI_MONGO, {useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connection successful!'))
.catch((error) => { console.error(error); });
//The API call from the Google sign in button
app.get('/authgoogle',
passport.authenticate('google', { scope : ['profile', 'email'] }));
//Callback from Google Sign in
app.get('/authgoogleredirect', function(req, res, next) {
passport.authenticate('google', {
scope : ['profile', 'email']
}, function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect(keys.URL_SIGN_IN); }
req.logIn(user, function(err) {
if (err) { return next(err); }
req.session.user = req.user;
req.session.save(function(){
console.log(`req.session.user in callback: ${req.session.user}`)
console.log(`req.session in callback: ${JSON.stringify(req.session)}`)
res.redirect(keys.URL_LOGGED_IN);
});
});
})(req, res, next);
});
//Middleware to check if user is logged in
function isLoggedIn(req, res, next) {
console.log(`req.session in isloggedin: ${JSON.stringify(req.session)}`);
console.log(`req.user in isloggedin: ${JSON.stringify(req.user)}`);
if (req.isAuthenticated()) {
next();
} else {
data = {
"redirect": true,
"url": keys.URL_SIGN_IN
}
res.send(JSON.stringify(data));
}
}
//--------------------USERS------------------------
//Getting the currently logged in user
app.get('/getcurrentuser', isLoggedIn, function(req, res, next) {
res.send(req.user);
});
What works and what does not
The google sign in works well, and passport.serialize and passport.deserialize work well, I can see the user object.
When I try to reach GET /getcurrentuser (call on all pages), I don't get the req.user object and isAuthenticated() always returns false.
These are the logs of the middleware isloggedin :
req.session in isloggedin: {"cookie":{"originalMaxAge":2419200000,"expires":"2020-08-07T12:09:54.220Z","httpOnly":true,"path":"/"}}
req.user in isloggedin: undefined
The sessions are correctly saved in the session collection in mongoDB.
Serialize and deserialize don't seem to be called after sign in, not sure if that's normal.
What I tried
I read plenty of answers to similar problems but none seem to work:
order of initialization of passport and express session: it should be correct
proxy: added but did not change anything
credentials: include has always been included in my front end fetch call
cookie.secure: false: turned it on and off, without success
cookie session tried to use it instead of express session, without success
It must be something stupid but I can't seem to find it...
What troubles me is why would this work on localhost?
Thanks a lot for your time :)
Alright, if anyone comes across this exact issue, I finally solved it:
req.user always came as undefined, because my front end and back end have different domain names (I'm using the free heroku tier) therefore the cookie was considered third-party, blocked by Google Chrome and never sent to my express app!
As soon as I disabled third-party cookie blocking in Chrome settings the session became persistent!
The passport and session configurations were correct all along...
Thanks #abhinav for your help and time :)
Passport has its own session handling code embedded in its library, if you want to store your passport login user to session-database you can do
router.get("/redirect", passport.authenticate('google',{failureRedirect:'/fail',successRedirect:'/success'}, (req,res)=>{
req.session.user = req.user;
});
and keeping your remaining code same as you have done, this will save your session in the database
I have a GraphQl server and a react frontend. I use passport and LocalStrategy to authenticate the user which works well, I can successfully login an existing user. I also want to use passport session to create a user session, so that I can access the logged in user later in my GraphQl resolvers for authentication. I expected passport to set the user in the session after successfully authenticating one. But after sending correct credentials from the client to the server, GraphQl queries do not have access to req.user.
The GraphQL server code looks like this:
import express from 'express';
import passport from 'passport';
import {Strategy as LocalStrategy} from 'passport-local';
import session from 'express-session';
import cors from 'cors';
import bodyParser from 'body-parser';
import models from './models';
import typeDefs from './schema';
import resolvers from './resolvers';
import { graphqlExpress, graphiqlExpress } from 'apollo-server-express';
import { makeExecutableSchema } from 'graphql-tools';
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
const app = express();
app.use('*', cors({ origin: 'http://localhost:3000' }));
app.set('port', (process.env.PORT || 3001));
//--- Passport ----
app.use(session({
saveUninitialized: true,
resave: false,
secret: 'verysecretsecret'
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
function(email, password, done) {
models.User.findOne({
where: {
email: email
}
}).then(function(user) {
if (user) {
if (user.validPassword(password)) {
return done(null, user);
} else {
return done(null, false);
}
}
return done(null, false);
});
}
));
//--- Routes ----
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql'
}));
app.use(
'/graphql',
bodyParser.json(),
graphqlExpress( (req) => {
console.log('/graphql User: ' + req.user); // prints undefined after sending correct login credentials to /login
return ({
schema,
context: {
user: req.user,
},
});}),
);
app.use(bodyParser.urlencoded({ extended: true }) );
app.post('/login', passport.authenticate('local'), (req, res) => {
console.log('/login: User', req.user); // prints the logged in user's data
return res.sendStatus(200);
});
export default app;
And this is the login fetch request from the client:
onSubmit = () => {
var details = {
'email': this.state.email,
'password': this.state.password,
};
var formBody = [];
for (var property in details) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('http://localhost:3001/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
credentials: 'include',
body: formBody
}).then(function(response) {
console.log(response);
}).catch(function(err) {
// Error
});
};
Do I have to change something on the client side for the server to receive the session cookie? Or is something going wrong in the backend?
I also uploaded a minimal example to this repo: https://github.com/schmitzl/passport-graphql-minimal-example
Managing sessions gets a little messy when you're dealing with CORS. There's a couple of things you need to change to get the behavior you're expecting:
First, modify your server code to ensure you're sending the Access-Control-Allow-Credentials header:
app.use('*', cors({ origin: 'http://localhost:3000', credentials: true }));
Next, make sure your requests are actually including the cookies. You've done so with the login request, by setting the credentials option to include. Apollo uses fetch under the hood and we need to pass this option to it as well.
I may be missing something, but it doesn't appear that apollo-boost provides an easy way to do the above (you have fetchOptions, but including credentials there doesn't appear to do anything). My advise would be to scrap apollo-boost and just use the appropriate libraries directly (or use apollo-client-preset). Then you can pass the appropriate credentials option to HttpLink:
import ApolloClient from 'apollo-client'
import { HttpLink, InMemoryCache } from 'apollo-client-preset'
const client = new ApolloClient({
link: new HttpLink({ uri: apolloUri, credentials: 'include' }),
cache: new InMemoryCache()
})
So I am trying to build a simple login/authorization tool utilizing node.js, passport.js and angular2. My current issue is that while a user can login, the session information doesn't appear to be passed to the front end server or the front end server doesn't pass the passport information back.
When logging in the user appears to get all the way to the portion where the res.send is called, and at that point serialize has been called and req.sessions.passport.user has been set; however, when the user tries to go to an authorized page, while the cookie is there, the passport is missing. While Deserialized is also never called, the middleware is called/appears called. When the middleware gets to the deserializer there is no passport/user attached thus deserialize is never called.
At this point I am wondering if it might be a CORS issue or something with angular2, but I have been working on this for several days and appear to be doing it the suggested way. I have also tried rebuilding it and setting up CORS in multiple ways along with the middleware and I am running out of ideas. I am also using express session but that appears to be working because the cookie I create in that exists.
Session Data at the end of auth but before responding to the site
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: false },
passport:
{ user:
anonymous {
username: 'test',
hash: '4024ca40c4372e029459a1d2d52a25b2fc4642f980f6cc948cc4b35f6350adde',
} } }
Session Data after making further requests
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: false } }
Relevant Code:
Passport.js
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((users, done) => {
var id=users.username;
db.one('select * from users where username = $1', id)
.then((user) => {
done(null, user);
})
.catch((err) => { done(err,null); });
});
local.js
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const init = require('./passport');
var promise = require('bluebird');
var options = {
// Initialization Options
promiseLib: promise
};
var hashclient = require('hashapi-lib-node');
const crypto = require('crypto');
var hash = crypto.createHash('sha256');
var pgp = require('pg-promise')(options);
var connectionString = 'postgresql://...';
var db = pgp(connectionString);
const optionsPassport = {};
init();
passport.use(new LocalStrategy(optionsPassport, (username, password, done) => {
db.one('select * from users where username = $1', username)
.then((user) => {
hash.update(password);
var encryptedPassword=hash.digest('hex');
hash = crypto.createHash('sha256');
if (!user) return done(null, false, { message: 'Incorrect username.' });
if (encryptedPassword!=user.password) {
return done(null, false, { message: 'Incorrect information.' });
} else {
return done(null, user);
}
})
.catch((err) => { return done(err); });
}));
helpers.js
function loginRequired(req, res, next) {
if (!req.user) return res.status(401).json({status: 'Please log in'});
return next();
}
Router.js example
const users = require('express').Router();
const auth = require('./auth');
const update = require('./update');
const password = require('./password');
const authHelpers = require('./helpers');
const passport = require('./local');
users.post('/update', authHelpers.loginRequired, update);
users.get('/:userId', authHelpers.loginRequired, single);
users.post('/create', create);
users.post('/auth', passport.authenticate('local'), auth);
app.js
var passport = require('passport');
app.use(cookieParser())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
secret: 'X+a1+TKXwd26mkiUUwqzqQ==',
resave:true,
saveUninitialized:true,
cookie:{secure:false}
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(function (req, res, next) {
var allowedOrigins = ['http://localhost:3000']
res.header('Access-Control-Allow-Origin', allowedOrigins);
res.header( 'Access-Control-Allow-Headers', 'withCredentials, Access-Control-Allow-Headers, Origin, X-Requested-With, X-AUTHENTICATION, X-IP, Content-Type, Accept, Access-Control-Request-Method, Access-Control-Request-Headers');
res.header( 'Access-Control-Allow-Methods', 'GET, OPTIONS, HEAD, POST, PUT, DELETE');
res.header( 'Access-Control-Allow-Credentials', true);
next();
});
var routes = require('./routes');
app.use('/', routes);
front end http service
getData (url:string, data:any): Observable<any> {
var headers = new Headers({ 'Content-Type': 'application/json', withCredentials: true });
var options = new RequestOptions({ headers: headers });
return this.http.get(url,options)
.map((res: Response): data => res.json())
.catch(this.handleError);
}
The issue was on the front end I was not setting withCredentials to true in the correct location
var options = new RequestOptions({ headers: headers, withCredentials: true });