SvelteKit hooks.js无法即时读取新创建Cookie的问题求助
问题现象
- 自建Svelte站点接入Discord API实现登录时,Auth回调接口已返回Set-Cookie头写入鉴权Cookie,但
hooks.js中的getSession方法往往需要等待数分钟才能读取到对应Cookie值,登录跳转后无法立刻获取登录态。 - 实现参考公开的Discord OAuth2 Svelte示例编写,核心代码如下。
核心业务代码
hooks.js 实现
import cookie from 'cookie'; const DISCORD_API_URL = import.meta.env.VITE_DISCORD_API_URL; const HOST = import.meta.env.VITE_HOST; /** @type {import('@sveltejs/kit').GetSession} */ export async function getSession(req) { const cookies = cookie.parse(req.request.headers.get("cookie") || ''); console.log(cookies) // 仅存在refresh token时说明access token已过期,执行刷新逻辑 if (cookies.disco_refresh_token && !cookies.disco_access_token) { console.log("Tem o refresh mas não tem o acess") const discord_request = await fetch(`${HOST}/api/refresh?code=${cookies.disco_refresh_token}`); const discord_response = await discord_request.json(); if (discord_response.disco_access_token) { console.log('setting discord user via refresh token..') const request = await fetch(`${DISCORD_API_URL}/users/@me`, { headers: { 'Authorization': `Bearer ${discord_response.disco_access_token}` } }); const response = await request.json(); if (response.id) { return { user: { // 仅返回客户端需要的字段,不要泄露access token等敏感信息 ...response } } } } } if (cookies.disco_access_token) { console.log('setting discord user via access token..') const request = await fetch(`${DISCORD_API_URL}/users/@me`, { headers: { 'Authorization': `Bearer ${cookies.disco_access_token}`} }); // JWT有效时返回Discord用户信息 const response = await request.json(); if (response.id) { return { user: { ...response } } } } // 未认证返回空用户对象 return { user: false } }
Auth回调接口实现
const DISCORD_CLIENT_ID = import.meta.env.VITE_DISCORD_CLIENT_ID; const DISCORD_CLIENT_SECRET = import.meta.env.VITE_DISCORD_CLIENT_SECRET; const DISCORD_REDIRECT_URI = import.meta.env.VITE_DISCORD_REDIRECT_URI; /** * @type {import('@sveltejs/kit').RequestHandler} */ export async function get({ url }) { // 读取URL参数中的授权码 const returnCode = url.searchParams.get("code"); console.log('returnCode =>', returnCode); // 构造请求Discord token接口的参数,该接口会返回用户的access和refresh token const dataObject = { client_id: DISCORD_CLIENT_ID, client_secret: DISCORD_CLIENT_SECRET, grant_type: 'authorization_code', redirect_uri: DISCORD_REDIRECT_URI, code: returnCode, scope: 'identify email guilds' }; // 请求Discord token接口 const request = await fetch('https://discord.com/api/oauth2/token', { method: 'POST', body: new URLSearchParams(dataObject), headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); const response = await request.json(); console.log(response) if (response.error) { console.log('redirect to / due error'); return { headers: { Location: '/' }, status: 302 } } // 写入Cookie后跳转至首页 const access_token_expires_in = new Date(Date.now() + response.expires_in); // 10分钟有效期 const refresh_token_expires_in = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30天有效期 console.log('redirect to /logged with cookies'); return { headers: { 'set-cookie': [ `disco_access_token=${response.access_token}; Path=/; HttpOnly; SameSite=Strict; Expires=${access_token_expires_in}}`, `disco_refresh_token=${response.refresh_token}; Path=/; HttpOnly; SameSite=Strict; Expires=${refresh_token_expires_in}`, ], Location: '/' }, status: 302 } }
问题根因与修复方法
代码里存在3个直接导致Cookie异常的显性错误,按优先级修复即可解决问题:
Set-Cookie语法错误
disco_access_token对应的Cookie字符串末尾多了一个多余的右大括号},直接破坏了HTTP Cookie的标准格式,浏览器无法正常解析存储该Cookie,会出现偶发延迟写入、写入失败的问题。
修复:删除多余的右括号,将该行调整为:`disco_access_token=${response.access_token}; Path=/; HttpOnly; SameSite=Strict; Expires=${access_token_expires_in}`过期时间单位计算错误
Discord OAuth2接口返回的expires_in字段单位为秒(默认值600,对应10分钟有效期),原代码直接将该值和毫秒级的Date.now()相加,计算出的access_token过期时间仅比当前时间晚600毫秒,等同于设置了一个立即过期的Cookie,浏览器会按照异常过期策略处理Cookie,不会正常持久化存储。
修复:时间计算时将秒转为毫秒:const access_token_expires_in = new Date(Date.now() + response.expires_in * 1000);Expires属性格式不符合HTTP规范
代码中直接将Date对象传入字符串模板,调用默认toString()得到的时间格式不是Cookie标准要求的GMT/UTC格式,部分浏览器无法识别该过期时间,会导致Cookie存储、发送逻辑异常。
修复:调用toUTCString()转换为标准HTTP时间格式:const access_token_expires_in = new Date(Date.now() + response.expires_in * 1000).toUTCString(); const refresh_token_expires_in = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toUTCString();
可选调试优化
本地开发阶段如果使用HTTP协议访问站点,SameSite=Strict属性会触发部分浏览器的跨站Cookie拦截规则,可临时调整为SameSite=Lax调试,生产环境部署HTTPS后再改回Strict即可。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

