How to handle express session cookies on the client side? - node.js

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.

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

Cookies are not being set in production

I deployed an express server to heroku as well as a next.js app.
The cookies are being sent from the server and even being shown in the network tab:
However, the cookies are not actually stored, all my requests failed because they depends on the csrf cookie, and the storage tab is empty:
This is the code for setting the cookies in the backend:
const csrfProtection = csrf({
cookie: {
httpOnly: true,
sameSite: 'none',
secure: true,
},
});
app.set('trust proxy', 1);
app.use(cors({ credentials: true, origin: clientOrigin }));
app.get('/', csrfProtection, function (req: Request, res: Response) {
res.cookie('XSRF-TOKEN', req.csrfToken(), { sameSite: 'none', secure: true });
res.end();
});
app.use(csrfProtection);
this is my axios instance:
const baseURL = process.env.baseURL;
const axiosInstance = Axios.create({
baseURL,
withCredentials: true,
});
and the request code:
useEffect(() => {
dispatch(loadingAction.setToTrue());
const getCsrf = async () => {
await axiosInstance.get('/');
};
getCsrf()
.then(() => {
dispatch(loadingAction.setToFalse());
})
.catch((err) => {
dispatch(loadingAction.setToFalse());
setError(err);
});
}, []);
While looking at your screenshot - I observed that the browser has a different domain set hilife01 vs hilife-1
Accessing throuugh https://hlife01.herokuapp.com/auth/login - gets you the cookies but your App doesn't have the right route configured.
Most likely, The Right domain is not associated with the cookie being set so while setting the cookie, the browser silently rejects the cookie because you are not matching the domain, however it is visible in the address bar.

How do I add a mock session for express-session when using SuperTest?

I have the following session setup...
setupSession(){
const session = {
secret: process.env.SESSION_SECRET,
cookie: {},
resave: false,
saveUninitialized: false,
};
this.app.use(expressSession(session));
}
I have the following test...
it("User should pass", (done)=>{
supertest(app)
.get("/user")
.set('Cookie', ['connect.sid=...'])
.expect(200)
.end(function(err, res){
if (err) done(err);
done();
});
})
I now need to mock out a session in the app to make sure that the login is valid? How do I mock a session in the session store so it thinks it is logged in during supertests?
I am not going to accept this, however, my workaround was to wrap the existing Express App in a Test Wrapper that overrode the properties needed for Auth. Like this...
const mockAuth = (app)=>{
const parentApp = express();
parentApp.use(function(req, res, next) { // lets stub session middleware
req.isAuthenticated = ()=>true;
next();
});
parentApp.use(app);
return parentApp;
};

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

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.

Node Express can't set session with post request

I use node express 4.0 to implement a message code verify function.
I use session to store the msg code I send.
I set up the session middleware as dos said:
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(session({secret:'ssssss'}));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/uploads', express.static(path.join(__dirname, 'uploads/temp')));
but when I use post method get the msg code, I found that the session didn't set successfully.
Like this code below:
router.post('/msgCode', function(req, res) {
req.session.test = 'test';
// request send msg code api then response
}
when I repost this router I found that the req.session.test is undefined.
then I try this in another router:
router.get('/sendMsgTest', function(req, res) {
req.session.test = 'test';
res.json({
status:0
})
}
every time I request sendMsgTest, I can get req.session.test. And when I request another get method, I can get req.session.test value successfully.
So why is it my post method didn't work?
I had the same issue. I've been using fetch for requests and it does not send cookies by default, so I got different cookie ids for GET and POST requests. The solution was:
fetch(url, {
credentials: 'same-origin' // or 'include'
});
See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials for more info.
you just try the session.save() , and in express 4 you must mention "resave" ,"saveUninitialized"
var session = require('express-session');
app.use(session({secret: 'keyboard cat', resave: false, saveUninitialized: true,cookie: { path: '/', httpOnly: true, maxAge: 30 * 30000 },rolling: true}));
router.post('/savesession', function (req, res) {
req.session.user = 'user';
req.session.save()
res.json(req.session.user);
});
router.get('/getsession', function (req, res) {
res.json(req.session.user);
});
You don't mention how you're calling the POST route. Are you sending the session cookie along with the POST request? Also, what type of response is the POST method returning?
(in app.js)
var session = require('express-session');
app.use(session({
secret: 'sooper_secret',
resave: false,
saveUninitialized: false
}));
(in routes/index.js)
router.post('/post', function (req, res) {
req.session.test = 'test';
res.json({success: true});
});
router.get('/get', function (req, res) {
const results = {
sessionData: req.session.test || 'session data not found'
};
res.json(results);
});
(command line)
% curl -X POST localhost:3000/post --cookie-jar test.cookie
% {"success":true}
% curl localhost:3000/get --cookie test.cookie
% {"sessionData":"test"}

Resources