(Yes, I know class components are outdated, updating to functional/hooks after successful deployment)
I have implemented a MERN (MongoDB, Express, React, and Node.js) application with authentication using Passport's local strategy and ES6.
Expected:
When the user logs in, they send a request to the "/auth/login" route, which is authenticated using the passport.authenticate()
method. If the authentication is successful, the server sends a "success" response back to the client.
Once the client receives the "success" response, the App.js component's componentDidMount()
method is called. In this method, the client sends a request to the "/auth/test"
route to check if the user is authenticated using req.isAuthenticated()
.
If the user is authenticated, the server sends back the user object, which is then stringified and stored in the client's local storage to indicate that the user is logged in.
Development Mode: For development, I am having the client on one port and the API on another. Everything works fine.
Currently: After deployment, I am hosting both the client and API through Render.com.
baseURL
to match the new client and API URLs respectively as I move away from localhost.Current Flow:
serializeUser()
is called with the correct id.localStrategy
is called and it finds the correct user and password and returns done(null, userObj)
./auth/login
returns the expected status.componentDidMount()
fires in App.js./auth/test
(in componentDidMount()
) fires and this is where we check req.isAuthenticated
.Notes on CORS:
credentials: "include"
const allowedOrigins =
process.env.NODE_ENV === "development"
? "http://localhost:3000"
: "https://my-client.onrender.com";
const corsOptions = {
origin: allowedOrigins,
credentials: true,
};
app.use(cors(corsOptions));
Problem:
req.isAuthenticated()
returns false
because req
is undefined.
What I've witnessed (through console logs):
deserializeUser()
is never called (Gets called fine in dev)Thank you
/auth/login
const url = baseURL + "/auth/login?username=" + id + "&password=" + pw;
const res = await fetch(url, {
method: "POST",
credentials: "include",
});
/auth/test
fetch(baseURL + "/auth/test", {
credentials: "include",
})
/auth/login
RouteauthRoute.post("/auth/login",passport.authenticate("local"),
(req, res) => {
res.status(200).send("Login successful");
},
/auth/test
RouteauthRoute.get("/auth/test", (req, res) => {
const isAuth = req.isAuthenticated(); // PROBLEM: Returns false because req is undefined
// etc.
app
.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
secure: process.env.NODE_ENV === "development" ? false : true,
httpOnly: process.env.NODE_ENV === "development" ? false : true,
sameSite: process.env.NODE_ENV === "development" ? "" : "none", // Set if using CORS
domain: process.env.NODE_ENV === "development" ? "" : ".onrender.com",
path: "/",
maxAge: 1000 * 60 * 5,
}, // 5 minutes
})
)
.use(passport.initialize())
.use(passport.session());
Thanks in advance!
One thing that looks suspect in your configuration is this line, specifically the .
preceding your domain:
domain: process.env.NODE_ENV === "development" ? "" : ".onrender.com",
line.
I would drop that line entirely. You could also change it to just onrender.com
.
Express will also not set a cookie if the remote server is passing the request through a non-http intermediary. You can tell it to trust this proxy:
if (process.env.NODE_ENV === 'production') {
app.set('trust proxy', 1);
}