Vercel部署Next.js14与Express API时Cookie设置失败问题求助

已将Node/Express API与Next.js 14.1.0客户端部署至Vercel,其他功能均正常,但Cookie存在两类异常问题:
- 部分场景下能获取到Cookie,但仅为临时有效,刷新页面后便会丢失;
- 另一些场景则会出现如下关于域名的警告:
"尝试通过Set-Cookie设置Cookie被阻止,因其domain属性无效"
即使按如下代码为Cookie设置domain,问题仍未解决:
res.cookie("refreshToken", refreshToken, { maxAge: 15 * 24 * 60 * 60 * 1000, sameSite: "none", httpOnly: true, secure: true, partitioned: true, domain: ".mern-nextjs-ecommerce.vercel.app" }); res.cookie("accessToken", accessToken, { maxAge: 15 * 24 * 60 * 60 * 1000, sameSite: "none", httpOnly: true, secure: true, partitioned: true, domain: ".mern-nextjs-ecommerce.vercel.app" });
登录后端代码
export const signIn = TryCatch(async (req, res, next) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user) return next(new ErrorHandler("Invalid credentials", 400)); const isMatched = await user.comparePassword(password); if (!isMatched) return next(new ErrorHandler("Invalid credentials", 400)); const accessToken = jwt.sign({ id: user._id }, config.JWT_SECRET, { expiresIn: "45m", }); const refreshToken = jwt.sign({ id: user._id }, config.JWT_SECRET); if (!user.tokens) user.tokens = [refreshToken]; else user.tokens.push(refreshToken); await user.save(); res.cookie("refreshToken", refreshToken, { maxAge: 15 * 24 * 60 * 60 * 1000, sameSite: "none", httpOnly: true, secure: true, partitioned: true, }); res.cookie("accessToken", accessToken, { maxAge: 15 * 24 * 60 * 60 * 1000, sameSite: "none", httpOnly: true, secure: true, partitioned: true, }); res.json({ success: true, profile: { id: user._id, email: user.email, name: user.username, verified: user.verified, avatar: user.avatar?.url, accessToken: accessToken, }, accessToken, refreshToken, }); });
CORS配置
app.use( cors({ origin: [ "https://mern-nextjs-ecommerce.vercel.app", config.ADMIN_CLIENT_URL, "http://localhost:3000", ], credentials: true, methods: ["GET", "POST", "PUT", "DELETE"], allowedHeaders: [ "Content-Type", "Authorization", "Origin", "X-Requested-With", "Accept", "Set-Cookie", "Cookie", ], }) );
前端登录提交函数
const handleSignInSubmit = async (data: SignInInput) => { try { const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/signin`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email: data.email, password: data.password, }), }); const resData = await res.json(); toast.success(resData.message || "Logged In !"); } catch (error) { toast.error("An error occurred"); } };
middleware.ts代码
import { NextRequest, NextResponse } from "next/server"; export async function middleware(request: NextRequest) { const accessToken = request.cookies.get("accessToken")?.value; const refreshToken = request.cookies.get("refreshToken")?.value; console.log("accessToken: ", accessToken); console.log("refreshToken: ", refreshToken); const response = NextResponse.next(); if (!accessToken || !refreshToken) return response; const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/me`, { method: "GET", headers: { Authorization: `Bearer ${accessToken}`, }, credentials: "include", }); if (res.status === 401) { try { const res = await fetch( `${process.env.NEXT_PUBLIC_API_URL}auth/refresh-token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refreshToken: refreshToken, }), credentials: "include", } ); const data = await res.json(); if (data.success) { const response = NextResponse.redirect(request.url); response.cookies.set("accessToken", data.tokens.access, { httpOnly: true, sameSite: "lax", }); response.cookies.set("refreshToken", data.tokens.refresh, { httpOnly: true, sameSite: "lax", }); return response; } } catch (error) { console.log("An error occurred11111: ", error); } } return response; }
前端注册提交函数
const handleSignInSubmit = async (data: SignUpInput) => { try { const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}auth/signup`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ username: data.username, email: data.email, password: data.password, }), }); const resData = await res.json(); if (res.ok) { if (resData.success) { setOpen(false); router.refresh(); } } else { setOpen(false); toast.error(resData.message || "An error occurred"); } } catch (error) { toast.error("An error occurred"); } };
注册后端代码
export const createNewUser = TryCatch(async (req, res, next) => { const { username, email, password } = req.body; const isUserThere = await User.findOne({ email }); if (isUserThere) { return next(new ErrorHandler("User already exists", 400)); } const user = await User.create({ username, email, password, }); const token = crypto.randomBytes(36).toString("hex"); await AuthVerificationTokenModel.create({ userId: user._id, token }); console.log(token); const accessToken = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: "5m", }); const refreshToken = jwt.sign({ id: user._id }, process.env.JWT_SECRET); const link = `${process.env.CLIENT_URL}verify?token=${token}&id=${user._id}`; await sendEmail(user.email, link); if (!user.tokens) user.tokens = [refreshToken]; else user.tokens.push(refreshToken); await user.save(); res.cookie("refreshToken", refreshToken, { maxAge: 15 * 24 * 60 * 60 * 1000, sameSite: "none", httpOnly: true, secure: true, }); res.cookie("accessToken", accessToken, { maxAge: 15 * 24 * 60 * 60 * 1000, sameSite: "none", httpOnly: true, secure: true, }); res.status(201).json({ success: true, message: "Registration successful | Please verify your email", accessToken, refreshToken, }); });
内容的提问来源于stack exchange,提问作者Harry 408
相关产品推荐
相关产品推荐

