I am turning to full-stack from a frontend developer. As a React.Js developer, I wanted to stick with the javascript ecosystem. So, intuitively I picked Next.Js to smoothen the transition, reused some of my react code blocks to do so & built a few simple Node.Js apps.
Problems occurred as I built the frontend in Next.js and the backend with Express+MongoDB Compass, then deployed them on Vercel and Render separately.
I ) Deployment Failure Due to Local MongoDB Setup :
The Express + MongoDB application was deployed on Render, but it was still using a local MongoDB Compass setup instead of a cloud database.
As Render (and services like Vercels) are designed to connect to hosted databases, the deployment failed immediately.
Resolve :
I learned about the issue and created a MongoDB Atlas cluster, connected it with MongoDB Compass using the atlas connection string to access and manage the database in the cloud.
II ) Incorrect Cookie Handling in Next.js :
Initially, cookies were being set and verified in Next.js rather than in the Express backend. Since both environments were completely separate, it caused conflicts.
A quick recap :
Only the backend should create and remove secure cookies (
httpOnly,secure,sameSite).The frontend should not handle sensitive cookie logic due to potential XSS attack risks.
So the ideal way is to set and clear cookies from the backend during login and logout, while the frontend simply managed routing logic.
Also I was trying to verify cookie in Next.js Middleware, which runs in an Edge Runtime environment — not Node.js.
This environment can only check if a cookie exists, not verify it.
Resolve :
Removed
app/api/session.-
Only conditional redirection logic (
if-elsechecks) was kept inmiddleware.js.
export function middleware(req) { const session = req.cookies.get("session_marker")?.value; const path = req.nextUrl.pathname; if (!session && path !== "/login") { return NextResponse.redirect(new URL("/login", req.url)); } if (session && path === "/login") { return NextResponse.redirect(new URL("/home", req.url)); } return NextResponse.next(); }
III ) Cross-Origin Cookie Issue :
New issue appeared as I deployed them.
While testing locally, both frontend and backend shared the same origin, so cookies persisted correctly.
But, in production:
Frontend : deployed on
vercel.app.Backend : deployed on
onrender.com.
In production when a user logged in, the backend generated a cookie correctly using the firebase token. The cookie was visible in the browser’s DevTools for a fraction of sec but disappeared immediately as the page reloaded.
Now the user is redirected back to the login page, as if never authenticated.
// Login (set)
res.cookie("auth_token", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
maxAge: 7 * 24 * 60 * 60 * 1000, // expires in 7 days
path: "/",
});
// Logout (clear)
res.clearCookie("auth_token", {
httpOnly: true,
secure: process.env.NODE_ENV==="production",
sameSite:process.env.NODE_ENV==="production"? "none" : "lax",
path: "/",
});
Attempts to fix the issue :
Using CHIPS (Cookies Having Independent Partitioned State).
-
Setting
domainattributes for cookies explicitly.But that won’t work, as
vercel.app&onrender.comare completely different top-level domains
= res.cookie("auth_token", token, { .... partitioned: true, // CHIPS domain: "https://mern-todo-8f4w.onrender.com/", }); -
Adding proxy routes in
next.config.jsto simulate same-origin behaviour, which worked only on development.
/** @type {import('next').NextConfig} */ const nextConfig = { async rewrites() { return [ { source: "/api/:path*", destination: "https://mern-todo-8f4w.onrender.com/:path*", }, ]; }, }; module.exports = nextConfig;
The Fact :
Cross-origin cookies between different deployment domains are unreliable and often blocked by browsers.
Implementing a Hybrid JWT + Session Authentication :
Here is how it works,
1. Login request :
The frontend sends a Firebase token to the backend.
The backend verifies it using
firebase-admin.A JWT is signed with verified user details.
A sessionId is generated using
crypto.Both JWT and sessionId are sent back to the frontend, and the session is stored in the database.
2. Frontend handling :
JWT is saved in
localStorage.A client-side cookie is created containing the
sessionId, used only for routing logic in middleware.
3. Middleware routing :
Checks if the
sessionIdcookie exists.Redirects the user to the home page if it does, otherwise to the login/signup page.
4. Authenticated requests :
- Every request includes both JWT (from
localStorage) andsessionId(from cookies) in the headers.
5. Authentication in backend :
The JWT is verified.
The sessionId is checked against the database.
If both are valid, the user data is attached to the request and proceed to server.
I learned that cross-origin setups rarely have a conventional fix — you often need to experiment, blending a few ideas to obtain the right fit that works for you.
Top comments (0)