NextJS14中获取服务端认证Cookie失败问题求助
实现流程
- 设置服务端Cookie,结构如下:
{id: <WordPress用户数据库中的user_id>, token: <从REST API获取的token>}
- 通过nodemailer发送验证邮件,引导用户跳转至
/verified路由;在该路由中读取已设置的Cookie,通过WP REST API完成用户认证,并将WordPress用户数据库中的用户状态更新为已授权。
当前问题
用户点击邮件链接跳转至/verified路由后,无法获取到Cookie值(相关函数返回undefined),但在Chrome开发者工具中能看到该Cookie存在,且手动访问/api/cookie路由时可以正常获取到Cookie值。
api/cookie路由处理器
// API route that validates the user's token by storing it in a cookie import { NextRequest, NextResponse } from 'next/server'; import { cookies } from "next/headers"; // Export function for GET method export async function GET(req: NextRequest) { // GET request to fetch the verification cookie try { // Stores the verification cookie const cookie = cookies().get("verify")?.value console.log('Getting cookie: ', cookies().has("verify")); // Checks if the cookie exists if (cookie == undefined) { return new NextResponse(JSON.stringify({error: 'Missing verification cookie.'}), {status: 400}); } // Returns the cookie else { return new NextResponse(cookie, {status: 200}); } // Exception error handler } catch (error) { console.error('Error during verification:', error); return new NextResponse(JSON.stringify({error}), {status: 500});; } } export async function POST(req: NextRequest) { try { const body = await req.json(); console.log('POST request body: ', body) // Expires one hour after the email is sent const expires = new Date(Date.now() + 60 * 60 * 1000); cookies().set("verify", JSON.stringify(body), { expires, httpOnly: true }); console.log('cookie created: ', cookies().has('verify')) return new NextResponse(body, {status: 200});; } catch (error) { console.error('Error:', error); return new NextResponse(JSON.stringify({error: 'Server error while creating cookie.'}), {status: 500});; } }
/verified服务端路由
'use server' import MaxWidthWrapper from "@/components/MaxWidthWrapper"; import { buttonVariants } from "@/components/ui/button"; import Link from "next/link"; import { wp_fetch } from "@/lib/wp-fetch"; import { Url } from "next/dist/shared/lib/router/router"; import { verify } from "@/lib/verify"; export default async function Verified() { let verified: String; let buttonText: String; let buttonRedirect: Url; async function isVerified () { const wpVerified = await verify(); if (typeof wpVerified == 'string'){ verified = wpVerified; buttonText = 'Return to register form'; buttonRedirect = '/register'; return {verified, buttonText, buttonRedirect}; } else { const authenticated = await wp_fetch('PUT', `users/${wpVerified.id}?roles=subscriber`, {roles: 'subscriber'}); if(authenticated.id){ verified = 'Your registration to <span className="text-rose-500">NextJStore</span> has been verified.'; buttonText = 'Please return to homepage and login with your account.'; buttonRedirect = '/'; return {verified, buttonText, buttonRedirect}; } else{ verified = 'Internal server error. Please try again.'; buttonText = 'Return to register form'; buttonRedirect = '/register'; return {verified, buttonText, buttonRedirect}; } } } return ( <> <MaxWidthWrapper> <div className="py-20 mx-auto text-center flex flex-col items-center max-w-3xl"> <h1 className="text-3xl font-bold tracking-tight text-gray-700 sm:text-5xl"> {(await isVerified()).verified} </h1> <p className="mt-6 text-lg max-w-prose text-muted-foreground"> {(await isVerified()).buttonText} </p> <div className="flex flex-col sm:flex-row gap-4 mt-6"> {/* buttonVariants() applies default styles of the button component. With parameters, the styles change */} <Link href={(await isVerified()).buttonRedirect} className={buttonVariants()}> Return </Link> </div> </div> </MaxWidthWrapper> </> ); }
verify.ts辅助函数
import { sendEmail } from "@/app/api/cookie/mailer"; import SMTPTransport from "nodemailer/lib/smtp-transport"; export async function verify(username?: string, email?: string, password?: string, id?: number) { if (username && email && password && id){ const wpAppCredentials = { username: process.env.NEXT_PUBLIC_WP_ADMIN_USERNAME, password: process.env.NEXT_PUBLIC_WP_REGISTER_APP_PASSWORD, }; const encryptedWpAppCredentials = btoa(`${wpAppCredentials.username}:${wpAppCredentials.password}`); const tokenFetch = await fetch(process.env.NEXT_PUBLIC_JWT_BASE + 'token', { method: 'POST', body: JSON.stringify({username, password}), headers: { 'Content-Type': 'application/json', 'Authorization': `Basic ${encryptedWpAppCredentials}` }, }); const json = await tokenFetch.json() console.log('Token:', json) if(tokenFetch.ok){ const token = json.token // Sends a verification email to the user const mailer: SMTPTransport.SentMessageInfo = await sendEmail(username, email, 'VERIFY'); const res = await fetch(process.env.NEXT_PUBLIC_DEPLOY_URL + '/api/cookie', { method: 'POST', body: `{"token": "${token}", "id": "${id}"}` }) const cookie = await res.json(); console.log('Created cookie value: ', cookie) if (res.ok){ return cookie; } else{ console.error('Error while creating the cookie.') return ('Error while creating the cookie') } } else{ console.error('Error while fetching the token from CMS.') return ('Error while fetching the token from CMS') } } // Gets the cookie to validate user's email address else if (!username && !email && !password && !id){ const res = await fetch(`${process.env.NEXT_PUBLIC_DEPLOY_URL}api/cookie`, { method: 'GET' }); const cookie = await res.json(); console.log('cookie: ', cookie); if (res.ok){ const validated = await fetch(process.env.NEXT_PUBLIC_JWT_BASE + 'token/validate', { method: 'POST', headers: { 'Authorization': `Basic ${cookie.token}` }, }); console.log('validated: ', validated) if(validated.status == 200){ return cookie; } else{ console.error('Error validating the token.') return ('Error validating the token'); } } else{ console.log('Cookie not found.') return('Please visit your email to validate the submitted address') } } else{ console.error('Missing required parameters.') return ('Wrong parameters. Please try again.') } }
内容的提问来源于stack exchange,提问作者Christos Gkoutzis
相关产品推荐
相关产品推荐

