Passport.js cookie not persist so login auth doesn't work even though session has the passport - passport.js

I'm using the passport.js local strategy.
I was testing it under proxy setting, localhost.
Things worked fine until I prepare to deploy.
I changed the API address to include dotenv and set CORS settings on the server-side.
When trying to login, CORS works fine, OPTIONS and the POST get 200 ok. The client receives the success data. cookie saved in client.
But when auth checking process runs right after Redux "isLoggedin" state is updated(useEffect), req.session doesn't
t have the passport object. So deserializeUser not be called. The session contains other cookie info except for Passport.
This one is only on Firefox(not Chrome): Page will be redirected if the login auth succeeded(it checks right after login redux state changed), but since it's failed, the user stays on the login page still. But if I try to login on the same page again, the cookie start to show the passport object.(in other words, it shows from 2nd attempt). But it doesn't persist because the Redux login state has been changed to true at the first login attempt already.(so Auth checking doesn't occur.)
client:
Axios.post(
`${process.env.REACT_APP_API_URI}/api/users/login`,
loginData,
{ withCredentials: true, }
).then((res) => res.data){
//save isLoggedin to true in Redux
}
// auth check logic starts right after changing isLoggedin. Axios Get to authenticateCheck
server.js
app.use(helmet());
// app.use(express.static('public'));
app.use("/uploads", express.static("uploads"));
// Passport configuration.
require("./utils/passport");
// connect to mongoDB
mongoose
.connect(db.mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
})
.then(() => console.log("mongoDB is connected."))
.catch((err) => console.log(err));
// CORS Middleware
const corsOptions = {
origin: "http://localhost:8000",
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
credentials: true,
methods: ["POST", "GET", "DELETE", "PUT", "PATCH", "OPTIONS"],
allowedHeaders:
"Origin, X-Requested-With, X-AUTHENTICATION, X-IP, Content-Type, Accept, x-access-token",
};
// app.use(cors(corsOptions));
app.options(/\.*/, cors(corsOptions), function (req, res) {
return res.sendStatus(200);
});
app.all("*", cors(corsOptions), function (req, res, next) {
next();
});
// to get json data
// support parsing of application/json type post data
app.use(express.json());
app.use((req, res, next) => {
req.requestTime = new Date().toISOString();
next();
});
//support parsing of application/x-www-form-urlencoded post data
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
// db session store
const sessionStore = new MongoStore({
mongoUrl: db.mongoURI,
collection: "sessions",
});
// tell app to use cookie
app.use(
session({
secret: process.env.SESSION_SECRET_KEY,
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
httpOnly: true,
secure: false,
sameSite:"none",
maxAge: 24 * 60 * 60 * 1000, // 24 hours
//keys: [process.env.COOKIE_ENCRYPTION_KEY]
},
name: "pm-user",
})
);
// tell passport to make use of cookies to handle authentication
app.use(passport.initialize());
app.use(passport.session());
app.use(compression());
app.use(flash());
app.use((req, res, next) => {
console.log("req.session:", req.session);
// console.log('/////// req: ///////', req);
console.log("////// req.user: ", req.user, " //////");
next();
});
//---------------- END OF MIDDLEWARE ---------------------//
authController:
exports.authenticateCheck = (req, res, next) => {
console.log("req.isAuthenticated():", req.isAuthenticated());
if (req.isAuthenticated()) {
return next();
} else {
return res.json({
isAuth: false,
error: true,
});
}
};
It would be a really big help if you can advise me where to look to fix it.
Thanks.

I found the solution finally.
It was because the session was renewed every time when a new request starts other than a login request.
The solution was, I had to add { withCredentials: true } to every Axios option in my frontend.

Related

Cookie not being sent to AWS ECS server from ReactJS

I am current deploying a MERN Stack application and have successfully deployed the backend api to http://44.198.159.229/. I am now trying to connect it to my client server which is still running on localhost:3000. However, I am running into a cookie related issue. I am receiving the cookie on my frontend from the backend express server, but upon making a get request an authenticated route the frontend is not sending the cookie back. In the network tag in google chrome I see that the cookie is instead filtered out. I have done countless research and browsed various posts but cannot seem to find the solution for this. It works when I check the api route manually in my browser but does not upon sending an axios request. It also works when I'm deploying the backend on my local server but I imagine because they are both on the same domain.
Here is my express configuration on the backend.
const corsOptions = {
credentials: true,
origin: "http://localhost:3000",
optionsSuccessStatus: 200,
};
// Express backend for web application
const app = express();
app.set("trust proxy", true);
/////////////////////////////////////////////////////// Middleware //////////////////////////////////////////////////
app.use(cors(corsOptions));
app.use(cookieParser());
app.use(
session({
secret: "somethingsecretgoeshere",
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: false,
secure: false,
maxAge: 10 * 60 * 100000,
sameSite: 'none'
},
})
);
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(passport.initialize());
app.use(passport.session());
passportConfig(passport);
app.use("/api", auth_routes);
app.use("/api", major_requirement_routes);
app.use("/api", user_course_routes);
export default app;
Here is the route at which I am making the get request to see if a user is authenticated
router.get("/auth/check", (req, res) => {
console.log(req.user)
console.log(req.cookies)
if (req.user) {
User.findOne({netId: req.user}, function (err, docs) {
if (err) {
console.log(err);
} else {
res.json({
auth: true,
user: req.user,
courseList: docs.courseList,
semesterList: docs.semesterList,
major: docs.major,
creditsApplied: docs.creditsApplied,
emailAddress: docs.emailAddress,
});
}
});
} else {
res.json({auth: false, id: null});
}
});
Here is my axios config
import axios from "axios";
const backend_url = "http://44.198.159.229:5000/api"
// const backend_url = "http://localhost:5000/api"
export default axios.create({
withCredentials: true,
baseURL: backend_url,
});
Here is my axios get request on the frontend
axios
.get("auth/check", { withCredentials: true,credentials: 'include',
})
.then(({ data}) => {
console.log(data)
if (data.auth) {
setIsAuthenticated(true);
setUser(data.user);
setCourseList(data.courseList);
setIsLoading(false);
} else {
setIsAuthenticated(false);
setCourseList(undefined);
setUser(undefined);
setIsLoading(false);
}
})
.catch(() =>
console.log(
"Something went wrong while trying to fetch your auth status."
)
);
}, []);
Okay so after a lot of research and playing around for a few days I have found a solution. I had to use a SSL and redirect traffic to an https server via AWS load balancing and set sameSite: None httpOnly: true, secure: true. I hope this helps someone else. This is because cookies can only be sent to cross origin sites that are secure. I also had to change my local host to run on https instead of http

How to handle express session cookies on the client side?

I'm trying to create an API with express that I will access using a React front end. I'm having a hard time figuring out how to handle authentication using express-session.
I used the middleware like follows:
var corsOptions = {
credentials: true,
origin: 'http://localhost:3000'
}
app.use(cors(corsOptions));
app.use(session({
secret: 'dfgdfbgbdfgd',
saveUninitialized: false,
resave: false,
cookie: {
secure: false
}
}));
Here is the Log in route (the local_auth middleware is just checking if the credentials are correct):
AccountRouter.post('/login', local_auth, (req, res) => {
req.session.user_id = req.user._id;
return res.status(200).send('Connected');
});
After loging in, I try to access the following route from React to check if the session is operational:
AccountRouter.get('/authcheck', (req, res) => {
let sess = req.session.user_id;
console.log(sess);
if (sess) return res.status(200);
else return res.status(404).send('pffff');
});
req.session is undefined.
The front end code is just a simple axios request to the above url. I have no idea if I have to save the cookie to localStorage and send each for each request.
Here is the request to the authcheck route:
axios.get('http://localhost:5000/api/accounts/authcheck', {withCredentials: true})
.then((response) => {
console.log(response);
})
.catch(() => {
console.log('waaah va te connecter');
});
And the login request:
const data = {
'email': e.target.email.value,
'password': e.target.password.value
};
axios.post('http://localhost:5000/api/accounts/login', data)
.then((response) => {
const sessionID = response.data;
console.log(sessionID);
});
req.session is undefined.
If req.session is undefined, then you are apparently trying to use the session in a route that is defined BEFORE this middleware code:
app.use(session({
secret: 'dfgdfbgbdfgd',
saveUninitialized: false,
resave: false,
cookie: {
secure: false
}
}));
has had a chance to run. When req.session is undefined, that means the session code has not yet run. Even if you had a cookie issue that causes a prior session to get lost, you would still have a new empty req.session if the session middleware had already run.
So, since you don't show the overall order of all your routes, we can't offer a specific fix, but it appears that this:
AccountRouter.get('/authcheck, ...)
is running before your session middleware which is a problem for any route that depends upon the session.

Express-session creates new session every request

I put my node express server into production. In development, express-session worked fine (it stored session into cookies with MemoryStore). But now it creates a new session ID into MongoStore every time I refresh or make a request. Also, it doesn't create a cookie in the browser anymore (idk if it's a good or a bad thing)
Most StackOverflow queries on this topic tell me to do things that I've already done so no help there
Here is my express and session setup:
const app = express()
const apiPort = process.env.PORT || process.env.API_PORT
app.use(cors({credentials: true, origin: process.env.ORIGIN_URL}))
mongoose.connection.on('error', (err) => {
console.error(err);
console.log('MongoDB connection error. Please make sure MongoDB is running.');
process.exit();
});
const sessionMiddleware = session({
resave: false,
saveUninitialized: false,
secret: process.env.SECRET,
// secure: true,
cookie: {
sameSite: true,
httpOnly: true,
secure: true,
maxAge: 1000 * 60 * 60 * 24 * 30
},
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI
})
})
app.use(sessionMiddleware);
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static("public"));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json())
app.post('/auth/login', (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) { return next(err); }
if (info) {
return res.status(401).json({error: info.msg})
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.status(200).send(user);
});
})(req, res, next);
});
app.get('/auth/logout', (req, res) => {
req.logout()
res.sendStatus(200)
});
app.get('/checkToken', authCheck, (req, res) => {
res.status(200).send({accountType: res.accountType});
})
Additional info that might be handy for this problem: the front-end is on a separate domain, the aforementioned server is on Heroku, and the DB is in MongoDB cloud.
Turns out all I was missing was app.set('trust proxy', 1) in my setup and sameSite: 'none' in cookie: {} in session middleware.

Node endpoints stop working after cookie is set

I've been struggling with this issue for a while now. First, everything works great on my local PC, which makes it more difficult to test. When I upload the site to our public site, it breaks. I can log in just fine and get a cookie. But after that, all my endpoints stop working. Network tab shows nothing for request or response for them. I have tested with Postman. I can hit all the endpoints just fine until I log in and get a cookie. Then I can't hit those endpoints anymore, it just spins. If I delete the cookie, I can hit them again. So it's gotta be something with the way I'm setting the cookie or checking the cookie in my Node server.
Here is my main app.js Node server file. If any other files are needed, let me know.
const createError = require('http-errors');
const express = require('express');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const passport = require('passport');
const session = require('express-session');
const flash = require('connect-flash');
const cors = require('cors');
const MySQLStore = require('express-mysql-session')(session);
const config = require('./config/config');
// MySql Store setup
const options = {
host: config.host,
port: config.port,
user: config.username,
password: config.password,
database: config.database
};
const sessionStore = new MySQLStore(options);
const app = express();
// Middleware
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser(config.session_secret));
app.use(cors());
app.options('*', cors());
// app.use(function(req, res, next) {
// res.header('Access-Control-Allow-Origin', '*');
// res.header(
// 'Access-Control-Allow-Headers',
// 'Origin, X-Requested-With, Content-Type, Accept'
// );
//
// next();
// });
//
// app.use(function(req, res, next) {
// res.setHeader('Access-Control-Allow-Origin', '*');
// res.setHeader(
// 'Access-Control-Allow-Methods',
// 'GET, POST, PUT, DELETE'
// );
// res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// res.setHeader('Access-Control-Allow-Credentials', true);
// next();
// });
// session setup
app.set('trust proxy', 1); // trust first proxy
app.use(
session({
secret: config.session_secret,
resave: false,
saveUninitialized: false,
store: sessionStore,
name: 'reg-portal-cid',
cookie: {
secure: false,
httpOnly: false,
maxAge: 1000 * 60 * 60 * 24 * 365
}
})
);
app.use(flash());
require('./API_Gateways/passport')(passport);
// passport authentication
app.use(passport.initialize());
app.use(passport.session());
// user identification
app.use(require('./middleware/user_identification'));
app.use('/auth', require('./API_Gateways/Auth_Routes'));
// Application Gateways
// app.use('/api', function(req, res) {
// return res
// .status(200)
// .json({ message: 'Success! Base API endpoint.' });
// });
app.use('/users', require('./API_Gateways/User_Gateway'));
app.use('/customers', require('./API_Gateways/Customer_Gateway'));
app.use('/SDS', require('./API_Gateways/SDS_Gateway'));
app.use('/chemicals', require('./API_Gateways/Chemical_Gateway'));
app.use('/PDF', require('./API_Gateways/PDF_Gateway'));
app.get('/', (req, res) => {
return res
.status(200)
.send(
'<h1>This is the Node server for the Registration Portal.</h1>'
);
});
// Logout Route
app.post('/logout', (req, res) => {
console.log('app logout route');
sessionStore.destroy(req.sessionID, (err) => console.log(err));
req.logout();
req.session.destroy();
res.clearCookie('reg-portal-cid');
// res.clearCookie('connect.sid');
return res.status(200).json({ message: 'User Logged Out' });
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
});
process
.on('unhandledRejection', (reason, p) => {
console.error(reason, 'Unhandled Rejection at Promise', p);
})
.on('uncaughtException', (err) => {
console.error(err, 'Uncaught Exception thrown');
//process.exit(1);
});
module.exports = app;
Also, if this helps at all, after I have logged in with Postman and have a cookie, I click on the logout route and it just sits there and spins. When I hit cancel in Postman, my Node server terminal prints out POST /logout - - ms - - which tells me it is getting hit kind of.
The issue seems to be related to caching in the browser as there are no requests in the network tab of the browser.
To rule out this can you try to open the site in an incognito tab and try?
Try adding a memorystore, it worked for me
var MemoryStore = require('memorystore')(session)
app.use(session({
cookie: { maxAge: 86400000 },
store: new MemoryStore({
checkPeriod: 86400000 // prune expired entries every 24h
}),
resave: false,
secret: 'keyboard cat'
}))```
I figured out the issue. It was not anything anyone would have figured out from my question or from my code. So it is likely this question and answer will not be useful to anyone in the future. And it was very hard to find, like a needle in a haystack. I figured out that the session was not saving into the database on our live site, but it was on localhost.
So I started looking into why and started specifically looking into the express-mysql-session package. I figured out there was a way to run it in debug mode and once I did that, I instantly saw errors in the terminal saying it could not save to the database. So I knew I was on the right track. I looked up the specific error I was getting of ER_NOT_SUPPORTED_AUTH_MODE and that brought me to this Github issue thread: https://github.com/chill117/express-mysql-session/issues/109 And then I found twentythreetea's answer to run these 2 Mysql queries:
ALTER USER 'YOUR USERNAME'#'localhost' IDENTIFIED WITH mysql_native_password BY 'YOUR PASSWORD'
flush privileges;
I was using MySQL Workbench and figured out I had to remove the #'localhost' part. Once I did that, BOOM!! Everything is working beautifully on the live site!!

Cross Domain Cookies with Angularjs, Nodejs, Expressjs, express-sessoion, and Mongo Store

I have a suite of programs that are all under the same company and I am trying to develop a single login / authentication service that can persist through all of the programs. The idea is very micro-service oriented in which we will have one service to handle authentication and persist it as long as someone is in one of the programs. The issue is I need my other services to be able to access the same cookies across all of the domains and be able to send those cookies to the auth service for session verification. Please correct me if this is not the proper way to set up micro-services with a login/auth service.
For my front end (Angularjs):
service.login = function (obj, callback) {
$http.post(loginService + "login", obj, {
withCredentials: true
}).success(function (data) {
callback(data);
})
.error(function (data, status, headers) {
console.log(status);
});
};
For my server (Node, Express, Mongo):
var options = {
pfx: fs.readFileSync('company.pfx'),
passphrase: 'pass',
ca: [fs.readFileSync('gd1.crt'), fs.readFileSync('gd2.crt'), fs.readFileSync('gd3.crt')],
spdy: {
protocols: ['h2', 'spdy/3.1', 'http/1.1'],
plain: false,
'x-forwarded-for': true,
connection: {
windowSize: 1024 * 1024, // Server's window size
// **optional** if true - server will send 3.1 frames on 3.0 *plain* spdy
autoSpdy31: false
}
}
};
var server = spdy.createServer(options, app);
app.use(helmet());
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use("/static", express.static('static'));
app.use("/logins", express.static('logins'));
app.set('trust proxy', 1) // trust first proxy for production with a proxy server
app.use(session({
resave: false,
saveUninitialized: true,
genid: function (req) {
return uuid.v4() // use UUIDs for session IDs
},
name: "myToken",
secret: 'mysecret',
cookie: { secure: false, maxAge: (45 * 60000) }, // set secure to true when in production
store: new mongoStore({ url: 'mongodb://' + base + 'sessions' })
}));
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);//req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH');
res.header('Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version');
next();
});
Requesting:
app.get('/', function (req, res) {
var sess = req.session, token = req.cookies.myToken;
res.send('Hello World!');
});
To test this I have a virtual machine running on my system with the application deployed and then I am also running my localhost:/ application. From my understanding my cookies should remain the same between the two calls with the same session if I have CORS set up properly. Any help or suggestions?
Have you tried
res.header('Access-Control-Allow-Origin', '*.domain') ?
Basically a wildcard matching any subdomain under your main domain.

Resources