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

无法将httpOnly Cookie中的JWT刷新令牌发送回服务器问题排查

问题:JWT刷新令牌存入httpOnly Cookie后无法回传至服务器

我正在实现JWT访问令牌/刷新令牌机制,将refresh token存入httpOnly Cookie中并希望将其回传至服务器。该httpOnly Cookie在响应的Set-Cookie头中存在,但在Chrome开发者工具的Application标签中不可见(不确定是否正常,有说法称httpOnly Cookie因安全原因不显示)。

现有代码

CORS配置

const corsOptions = {
  origin: "http://localhost:5173",
  credentials: true, //access-control-allow-credentials:true
  optionSuccessStatus: 200,
  exposedHeaders: [
    "x-auth-token",
    "Access-Control-Allow-Origin",
    "Access-Control-Allow-Credentials",
  ],
};
app.use(cookieParser());
app.use(cors(corsOptions)); 

向客户端发送Cookie的接口代码

res
    .cookie("refreshToken", refreshToken, {
      httpOnly: true,
      origin: "http://localhost:3000",
      secure: true,
      maxAge: 7 * 24 * 60 * 60 * 1000,
    })
    .header("x-auth-token", accessToken)
    .send("User validated, token accesible from headers");

接收令牌的中间件(console.log打印undefined)

module.exports = function authorizeRefreshToken(req, res, next) {
  try {
    const refreshToken = req.cookie;
    console.log("Refresh Token from client : ", refreshToken);
    if (!refreshToken) return res.status(403).send("No token inside cookie.");

    const decoded = jwt.verify(
      refreshToken,
      config.get("jwtPrivateKey"),
      (err) => {
        if (err) {
          if (err.name === "TokenExpiredError") {
            return res.status(403).send("Unauthorized");
          } else {
            console.log(err);
            return res.status(400).send("An error with JWT occurred!");
          }
        }
      }
    );

    req._id = decoded._id;
    req.refreshToken = refreshToken;
    next();
  } catch (error) {
    console.log("Error with the token", error);
  }
};

前端API调用代码

const getAccessToken = async () => {
      try {
        const response = await axios.get(END_POINT, {
          withCredentials: true,
        });
        const token = response.headers["x-auth-token"];
        setAccessToken(token);
        console.log("Access Token in useEffect : ", token);
      } catch (error) {
        console.error("AXIOS ERROR", error);
      }
    };

相关截图

  • 服务器响应头中的Set-Cookie:
    服务器响应头中的Set-Cookie
  • Chrome开发者工具Application标签:
    Chrome Application标签无Cookie显示

问题分析与解决方法

1. 中间件获取Cookie的错误

你在中间件里写的req.cookie是错误的,cookieParser中间件解析后,客户端发送的Cookie会存放在req.cookies(复数)对象中。正确的获取方式应该是:

const refreshToken = req.cookies.refreshToken;

2. Cookie配置错误(导致Cookie未被客户端存储)

(1)origin不是res.cookie的有效选项

res.cookie的配置里没有origin这个参数,应该替换为sameSite和domain:

  • domain:设置为'localhost',允许跨端口(5173和3000)共享Cookie;
  • sameSite:结合CORS的credentials: true,建议设置为'none'(需要配合secure: true),或者开发环境用'lax'。

(2)secure: true在HTTP环境下无效

如果你的本地开发环境是HTTP(而非HTTPS),secure: true会导致浏览器拒绝存储Cookie,因为该选项要求Cookie只能通过HTTPS传输。开发环境可以暂时将其设为false,生产环境再改回true。

修正后的Cookie配置:

res
    .cookie("refreshToken", refreshToken, {
      httpOnly: true,
      domain: "localhost",
      sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
      secure: process.env.NODE_ENV === 'production',
      maxAge: 7 * 24 * 60 * 60 * 1000,
    })
    .header("x-auth-token", accessToken)
    .send("User validated, token accesible from headers");

3. Chrome Application标签看不到httpOnly Cookie的原因

httpOnly Cookie在Chrome的Application标签中是可以看到的,只是会标记为HttpOnly,无法通过JS的document.cookie访问。你看不到说明Cookie根本没被浏览器存储,本质是上面的Cookie配置错误导致的,修正配置后就能在Application标签中看到该Cookie。

4. JWT验证的回调问题

你在jwt.verify中使用了回调函数,此时decoded会返回undefined(因为回调模式下不会返回解码结果),应该改用同步模式或者正确处理回调:

同步模式写法:

try {
  const decoded = jwt.verify(refreshToken, config.get("jwtPrivateKey"));
  req._id = decoded._id;
  req.refreshToken = refreshToken;
  next();
} catch (err) {
  if (err.name === "TokenExpiredError") {
    return res.status(403).send("Unauthorized");
  } else {
    console.log(err);
    return res.status(400).send("An error with JWT occurred!");
  }
}

或者保持回调模式,但要在回调内部处理后续逻辑:

jwt.verify(refreshToken, config.get("jwtPrivateKey"), (err, decoded) => {
  if (err) {
    if (err.name === "TokenExpiredError") {
      return res.status(403).send("Unauthorized");
    } else {
      console.log(err);
      return res.status(400).send("An error with JWT occurred!");
    }
  }
  req._id = decoded._id;
  req.refreshToken = refreshToken;
  next();
});

内容的提问来源于stack exchange,提问作者Νίκος Ανδρεάδης

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:21:03