I'm using the MERN stack for an e-commerce site. I decided to use cookies with express-session to keep track of the currently logged-in user.
All my tests are passing fine locally, meaning that when the user logs in with the right credentials, they are redirected to the home page, from which I make another request to the back end to determine if they are indeed connected.
However, after deploying on Vercel and rendering, it no longer works. When the user logs in, the request I make to determine their status only returns a 403. It's been over 5 days that I've been trying to fix this, but nothing.
Also, I'm facing the same issue with OAuth requests such as Google and Facebook, but I prefer to start with this.
Thanks in advance for your responses.
The axios instance use to make requestyour text
/**
* @desc authenticate a user with email and password
* @route POST /auth/login
* @access public
* @param req
* @param res
* @returns
*/
static async login(req: Request, res: Response) {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ status: 'error', message: 'All fields are required!' });
}
// check if the user exist
const checkUser = await User.findOne({ email });
// console.log('checkUser', checkUser);
if (!checkUser) {
return res.status(401).json({ message: 'Unauthorized ! ', status: 'error' });
}
// check if the password match
const matchPassword = await bcrypt.compare(password, checkUser.password);
// console.log('findUser', matchPassword, checkUser, password);
// the password doesn't match
if (!matchPassword) {
return res.status(401).json({ message: 'Unauthorized', status: 'error' });
}
req.session.user = checkUser;
req.session.userId = checkUser._id.toString();
delete checkUser.password;
console.log('user : ', req.session.id);
return res.json({
status: 'success',
data: checkUser,
message: 'successfully logged in',
});
}
The entry file of my nodejs backend
dotenv.config();
const PORT = process.env.PORT || 3500;
const secretKey = process.env.EXPRESS_SESSION_KEY as string;
const CLIENT_DOMAIN = process.env.CLIENT_DOMAIN as string;
const app = express();
const isProduction = process.env.NODE_ENV === 'production';
//
// other imports
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
AdminJS.registerAdapter({ Database, Resource });
const start = async () => {
const mongoDB = await initializeDb();
// await dbConnection();
const admin = new AdminJS({
...options,
databases: [mongoDB.db],
});
if (process.env.NODE_ENV === 'production') {
await admin.initialize();
} else {
admin.watch();
}
const router = buildAuthenticatedRouter(
admin,
{
cookiePassword: process.env.COOKIE_SECRET,
cookieName: 'adminjs',
provider,
},
null,
{
secret: process.env.COOKIE_SECRET,
saveUninitialized: true,
resave: true,
}
);
// server files
// app.use(express.static(path.join(__dirname, 'public', 'images')));
app.use(admin.options.rootPath, router);
configurePassport();
// deleteAllDocuments(User);
// logger middleware
app.use(logger);
// cookie-parser to manage secure cookie
app.use(cookieParser());
// cors
app.use(cors(corsOptions));
// session authentication configuration
const sessionMiddleware = expresssession({
secret: secretKey,
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 1000 * 60 * 60 * 10,
// TODO : change
secure: !!isProduction,
sameSite: isProduction ? 'none' : 'lax',
domain: !isProduction ? 'localhost' : '.orender.com ',
httpOnly: true,
},
name: 'oubuntu_cookie',
});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(sessionMiddleware);
app.use(passport.initialize());
app.use(passport.session());
// static files
app.use((req, res, next) => {
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
app.use('/', express.static(join(process.cwd(), 'src', 'public')));
// root router
app.use('/', rootRouter);
// auth routes
app.use('/api/auth', authRoute);
// users Route
app.use('/api/users', usersRoute);
app.use('/api/products', productsRoute);
app.use('/api/offers', offersRoute);
app.use('/api/groups', groupsRoute);
app.use('/api/categories', categoriesRoutes);
// Catch-all routes
app.all('/*', (req, res) => {
res.status(404);
if (req.accepts('html')) {
// nothing
} else if (req.accepts('json')) {
res.json({ message: 'Not Found !' });
} else {
res.type('text').send('Not Found');
}
});
// custom error handler
app.use(errLogger);
app.use(errorHandlerMiddleware);
// listener
mongoose.connection.once('open', () => {
console.log('Connected to MongoDB');
});
app.listen(PORT, () => {
console.log('Server is running on : ', `http://localhost:${PORT}`);
console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`);
});
mongoose.connection.on('error', (err) => {
console.log(`MongoDB connection error: ${err.message}`);
});
};
start();
// authRoute
const authRoute = express.Router();
authRoute
.post('/login', AuthenticationController.login)
.get('/logout', AuthenticationController.logout)
.get('/status', AuthenticationController.getUserStatus);
I tried many things like deploying frontend and backend to the same domain, configuring cookie domain and also sameSite options, but it still is just working perfectly in development mode, but not at all in production.
// here is the express-session configuration
// cors
app.use(cors(corsOptions));
// session authentication configuration
const sessionMiddleware = expresssession({
secret: secretKey,
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 1000 * 60 * 60 * 10,
// TODO : change
secure: isProduction,
sameSite: isProduction ? 'none' : 'lax',
domain: !isProduction ? 'localhost' : 'oubuntu-frontend-ori.onrender.com',
httpOnly: true,
},
name: 'oubuntu_cookie',
});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(sessionMiddleware);
app.use(passport.initialize());
app.use(passport.session());
// static files
app.use((req, res, next) => {
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS');
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
app.use('/', express.static(join(process.cwd(), 'src', 'public')));