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

页面刷新后登录失效,如何修复AWS Cognito的invalid_grant错误?

解决AWS Cognito机密客户端的invalid_grant及刷新登录状态问题

核心问题分析

invalid_grant错误通常和授权码重复兑换、客户端凭证配置或令牌生命周期管理直接相关,结合你的场景:

  • 首次登录时先返回400再返回200,大概率是前端重复调用了/exchangeCode接口:第一次调用消耗了授权码,第二次调用就会触发invalid_grant
  • 刷新页面后登出,说明前端未正确持久化令牌,且刷新时尝试用已失效的URL授权码重新兑换,导致再次返回400

具体修复步骤

1. 避免授权码重复兑换

检查前端CognitoContext.js的逻辑,确保仅在URL存在未使用过的code时调用兑换接口,且调用成功后立即清除URL中的code参数:

// CognitoContext中处理code的示例逻辑
useEffect(() => {
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  const hasStoredToken = localStorage.getItem('access_token');

  // 仅当存在未使用的code且无本地令牌时调用接口
  if (code && !hasStoredToken) {
    exchangeCode(code)
      .then(() => {
        // 清除URL中的code,防止重复触发
        window.history.replaceState({}, document.title, window.location.pathname);
      })
      .catch(err => console.error('兑换授权码失败:', err));
  }
}, []);

2. 后端正确处理机密客户端凭证

机密客户端兑换授权码时必须传入client_secret,且请求格式需符合Cognito要求(application/x-www-form-urlencoded),以下是server.js的示例修复:

app.post('/exchangeCode', async (req, res) => {
  const { code } = req.body;
  const tokenParams = new URLSearchParams();
  
  tokenParams.append('grant_type', 'authorization_code');
  tokenParams.append('client_id', process.env.COGNITO_CLIENT_ID);
  tokenParams.append('client_secret', process.env.COGNITO_CLIENT_SECRET); // 机密客户端必填
  tokenParams.append('code', code);
  tokenParams.append('redirect_uri', process.env.COGNITO_REDIRECT_URI); // 需与Cognito控制台配置完全一致

  try {
    const tokenRes = await fetch(`https://${process.env.COGNITO_DOMAIN}/oauth2/token`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: tokenParams
    });

    const tokenData = await tokenRes.json();
    if (!tokenRes.ok) return res.status(tokenRes.status).json(tokenData);

    // 获取用户邮箱信息
    const userInfoRes = await fetch(`https://${process.env.COGNITO_DOMAIN}/oauth2/userInfo`, {
      headers: { 'Authorization': `Bearer ${tokenData.access_token}` }
    });
    const userInfo = await userInfoRes.json();

    res.json({ ...tokenData, userEmail: userInfo.email });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

3. 前端持久化令牌并处理刷新逻辑

兑换成功后将令牌和用户邮箱存入本地存储,页面刷新时优先读取本地存储的令牌,而非URL中的过期code:

// CognitoContext初始化状态逻辑
useEffect(() => {
  const savedToken = localStorage.getItem('access_token');
  const savedEmail = localStorage.getItem('user_email');
  
  if (savedToken && savedEmail) {
    // 直接恢复用户登录状态
    setUserEmail(savedEmail);
    setAccessToken(savedToken);
  } else {
    // 处理URL中的code(如步骤1)
  }
}, []);

// 兑换令牌成功后持久化数据
const exchangeCode = async (code) => {
  const res = await fetch('/exchangeCode', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ code })
  });
  const data = await res.json();
  
  if (res.ok) {
    localStorage.setItem('access_token', data.access_token);
    localStorage.setItem('refresh_token', data.refresh_token);
    localStorage.setItem('user_email', data.userEmail);
    setUserEmail(data.userEmail);
  }
};

4. 验证Cognito控制台配置

  • 确认重定向URI与前端、后端配置完全一致(包括协议、域名、端口)
  • 确认客户端类型为机密客户端,且已生成并正确配置客户端密钥
  • 确认启用了Authorization Code Grant授权流程

额外排查点

  • 检查浏览器网络请求,确认是否存在重复的/exchangeCode调用
  • 查看Cognito授权码的生命周期(默认5分钟),确保兑换时code未过期
  • 后端调用令牌端点时,禁止使用JSON格式提交参数,必须用application/x-www-form-urlencoded

内容的提问来源于stack exchange,提问作者Meowzer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 02:57:11