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

如何获取AWS Cognito用户池的JWK(JSON Web Key)?

解决AWS Cognito IdToken验证中JWK获取与匹配问题

核心问题分析

无法获取有效JWK的常见原因有两个:一是URI占位符未正确替换(比如<Region>和<userPoolId>没换成实际值);二是未根据IdToken的kid字段匹配对应JWK(JWKS返回的是密钥集合,需找到与当前token对应的密钥)。

完整实现步骤与代码

1. 正确构建JWKS URI

将<Region>替换为你的Cognito用户池所在区域(如us-west-2),<userPoolId>替换为用户池ID(格式类似us-west-2_abc123XYZ),最终URI示例:

https://cognito-idp.us-west-2.amazonaws.com/us-west-2_abc123XYZ/.well-known/jwks.json

2. 编写Express中间件

以下是完整的中间件代码,包含JWKS获取、缓存、kid匹配及IdToken验证逻辑:

const jwt = require('jsonwebtoken');
const jwtToPem = require('jwk-to-pem');
const axios = require('axios'); // 需先安装:npm install axios

// 缓存JWKS,避免重复请求AWS接口
let cachedJwks = null;
let cacheExpiry = 0;

const cognitoAuthMiddleware = async (req, res, next) => {
  try {
    // 从Authorization头获取IdToken(推荐方式,格式为Bearer <IdToken>)
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return res.status(401).json({ message: '缺少有效IdToken' });
    }
    const idToken = authHeader.split(' ')[1];

    // 解析IdToken的header获取kid,无需验证签名
    const decodedHeader = jwt.decode(idToken, { complete: true }).header;
    const kid = decodedHeader.kid;

    // 获取并缓存JWKS(缓存1小时)
    if (!cachedJwks || Date.now() > cacheExpiry) {
      const jwksUri = 'https://cognito-idp.<Region>.amazonaws.com/<userPoolId>/.well-known/jwks.json';
      const response = await axios.get(jwksUri);
      cachedJwks = response.data.keys;
      cacheExpiry = Date.now() + 3600000; // 1小时后过期
    }

    // 根据kid匹配对应的JWK
    const jwk = cachedJwks.find(key => key.kid === kid);
    if (!jwk) {
      return res.status(401).json({ message: '找不到匹配的JWK密钥' });
    }

    // 转换JWK为PEM格式并验证token
    const pem = jwtToPem(jwk);
    const decodedToken = jwt.verify(idToken, pem, { algorithms: ['RS256'] });

    // 将解码后的用户信息挂载到req对象,供后续路由使用
    req.user = {
      email: decodedToken.email,
      userId: decodedToken.sub
      // 可按需添加其他字段
    };

    next();
  } catch (err) {
    console.error('IdToken验证失败:', err.message);
    return res.status(401).json({ message: '身份验证失败' });
  }
};

module.exports = cognitoAuthMiddleware;

3. 使用中间件保护路由

在需要保护的路由中引入中间件:

const express = require('express');
const cognitoAuthMiddleware = require('./path/to/middleware');
const router = express.Router();

// 受保护路由,仅携带有效IdToken的请求可访问
router.get('/protected', cognitoAuthMiddleware, (req, res) => {
  res.json({ 
    message: '访问受保护路由成功',
    userEmail: req.user.email 
  });
});

module.exports = router;

关键注意事项

  • IdToken传递方式:建议通过Authorization请求头传递,比放在body更符合REST规范。
  • 缓存策略:缓存JWKS可减少对AWS的请求次数,避免触发限流,同时需设置合理的过期时间。
  • 错误处理:需捕获网络请求失败、kid不匹配、token过期/篡改等异常,返回对应的401或403状态码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 23:43:31