You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SvelteKit hooks.js无法即时读取新创建Cookie的问题求助

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异常的显性错误,按优先级修复即可解决问题:

  1. Set-Cookie语法错误
    disco_access_token对应的Cookie字符串末尾多了一个多余的右大括号},直接破坏了HTTP Cookie的标准格式,浏览器无法正常解析存储该Cookie,会出现偶发延迟写入、写入失败的问题。
    修复:删除多余的右括号,将该行调整为:

    `disco_access_token=${response.access_token}; Path=/; HttpOnly; SameSite=Strict; Expires=${access_token_expires_in}`
    
  2. 过期时间单位计算错误
    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);
    
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 16:15:45