Wie setze ich Cookies vom Backend in der Produktion? (ausdrücken + reagieren)JavaScript

Javascript-Forum
Anonymous
 Wie setze ich Cookies vom Backend in der Produktion? (ausdrücken + reagieren)

Post by Anonymous »

Ich versuche, Cookies vom Backend aus zu setzen, aber das Frontend (Browser) löscht sie immer wieder automatisch. Dies geschieht nur in der Produktion, nicht auf localhost.
Der Cookie-Ablauf ist dieser:

Code: Select all

user clicks login with discord
redirected to discord's oauth page
discord sends the data to my backend api
user account is created/updated on my database
redirects back to my authCallback page
sends another request to users/sessions to store the user's data in cookies
/sessions route sets the cookies
Sowohl das Backend als auch das Frontend werden auf Vercel (Monorepo) gehostet.
Die einzige Route, die die Cookie-Funktion aufruft, ist Benutzer/Sitzungen
Hier ist der notwendige Code:
Ich habe 2 vercel.json, 1 für das Frontend und eine für das Backend (falls jemand es wissen muss)
vercel.json (Frontend):

Code: Select all

{
"rewrites":
[
{
"source": "/(.*)",
"destination": "/"
}
]
}
vercel.json (Backend)

Code: Select all

{
"builds": [
{
"src": "/index.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "/index.js",
"methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]
}
]
}
index.js (backend – cors stuff)

Code: Select all

const allowedOrigins = [
// Front End
"http://localhost:5173",
prodDomain,

// Back End
"http://localhost:4000",
prodDomainAPI,
];

app.set("trust proxy", 1);

app.use(
cors({
credentials: true,
origin: (origin, callback) => {
if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
})
);

app.use(cookieParser());
app.use(json());
app.use(urlencoded({ extended: true }));
cookieUtils.js (Backend)

Code: Select all

export const setAuthCookies = (res, userData, token) => {
const commonOptions = {
path: "/",
sameSite: "None",
secure: true,
maxAge: 30 * 24 * 60 * 60 * 1000,
};

res.cookie("authUser", JSON.stringify(userData), {
...commonOptions,
httpOnly: false,
});

res.cookie("authToken", token, {
...commonOptions,
httpOnly: true,
});

return res;
};

export const clearAuthCookies = (res) => {
const clearOptions = {
path: "/",
sameSite: "None",
secure: true,
httpOnly: true,
};

res.clearCookie("authToken", clearOptions);
return res;
};
usersControllers.js (Backend – /sessions-Route nur)

Code: Select all

users.post("/sessions", requireAuth(), async (req, res) => {
const decodedUserData = req.user.decodedUser;

try {
const existingUser = await getUserByID(decodedUserData.id);
if (!existingUser) {
return res.status(404).send("User not found");
}

const userRole = await getUserRole(decodedUserData.id);

const userData = {
id: existingUser.id,
discord_id: existingUser.discord_id,
username: existingUser.username,
avatar_url: existingUser.avatar_url,
banner_color: existingUser.banner_color,
banner_url: existingUser.banner_url,
role: userRole,
created_at: existingUser.created_at,
};

setAuthCookies(res, userData, req.user.token);

res.status(200).json({
success: true,
message: "Session established successfully",
});
} catch (error) {
console.error("users.POST /sessions", { error });
res.status(500).send("Internal Server Error");
}
});
Hinweis: requireAuth befasst sich nur mit JWTs, nicht mit der Einrichtung von Cookies und ist daher nicht erforderlich,
AuthCallback.jsx (Frontend)
einzuschließen

Code: Select all

export const AuthCallback = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();

useEffect(() =>  {
const run = async () => {
const reason = searchParams.get("reason");
const token = searchParams.get("token");

if (reason) {
let errorMessage = null;

switch (reason) {
case "rate_limited":
errorMessage =
"Discord is rate limiting login attempts. Please wait a few minutes and try again.";
break;
case "auth_failed":
errorMessage = "Authentication failed. Please try again.";
break;
default:
errorMessage = "An error occurred during authentication.";
}

toast.error(errorMessage, {
containerId: "notify-failure",
});
return setTimeout(() => {
navigate("/");
}, 1000);
}

try {
// No token means backend error
if (!token) {
toast.error("Authentication failed. Please try again.", {
containerId: "notify-failure",
});
return setTimeout(() => {
navigate("/");
}, 1000);
}

await axios
.post(
`${API}/users/sessions`,
{},
{
headers: {
Authorization: `Bearer ${token}`,
},
withCredentials: true,
},
)
.then((res) => {
toast.success("Successfully logged in!", {
containerId: "notify-success",
});
})
.catch((err) => {
toast.error("Failed to load user data. Please try again.", {
containerId: "notify-failure",
});
});

} catch (err) {
console.error("OAuth callback error:", err);
toast.error("Failed to load user data. Please try again.", {
containerId: "notify-failure",
});
} finally {
// Only reload if we have a token (success case)
if (token) {
console.log("token received");
setTimeout(() => window.location.reload(), 1000);
}
}
};

run();
}, [navigate, searchParams]);

return (

Authenticating...
Please wait

);
};
Dinge, die ich bereits ausprobiert habe und die nicht funktioniert haben:

Code: Select all

Adding domain to the commonOptions

const commonOptions = {
path: "/",
sameSite: "None",
secure: true,
maxAge: 30 * 24 * 60 * 60 * 1000,
domain: isProd ? prodDomain : "localhost"
};

using sameSite Lax, Strict and None, as well as using lowercase for all 3 (lax, strict, none)

console logging the headers which showed:
'set-cookie': [
'authUser='
'authToken='
]
Ich habe nach ähnlichen Fragen gesucht und die Leute sagen, es liegt daran, dass sie die Domäne nicht festgelegt haben oder nicht die richtige sameSite verwendet haben, aber ihre Lösungen funktionieren bei mir einfach nicht.
Mir ist auch aufgefallen, dass es in der Produktion kurz vor der Seitenaktualisierung in Cookies 4 Cookies gibt, jeweils 2 Sätze (authUser, authToken). Eines davon ist meine tatsächliche Domäne und das andere ist meine Domäne mit einem Punkt davor. Ich gehe davon aus, dass das etwas ist, was der Browser automatisch macht, da ich keine Domain einstelle.
Auf Localhost funktioniert das alles einwandfrei, es setzt die Cookies und ich kann mich problemlos anmelden.
In der Produktion werden zwar die Cookies gesetzt, aber sobald die Seite aktualisiert wird, werden beide Cookies automatisch gelöscht.
Ich kann die Daten von /sessions abrufen und das Frontend einfach die Cookies direkt mit js-cookies einrichten lassen, aber ich denke Das wäre weniger sicher, da httpOnly

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post