同一AWS Cognito用户池多客户端访问令牌互认问题咨询
问题
我正在用Nest.js重构遗留的Django服务,两者均采用AWS Cognito作为身份提供商。尽管使用相同的User Pool ID,但彼此不认可对方生成的访问令牌,导致用户迁移时需要重新登录,不符合需求。我已尝试让两者使用相同Client ID,但问题仍未解决。以下是Nest.js中校验JWT令牌的代码:
export class JwtStrategy extends PassportStrategy(Strategy) { constructor( @Inject(CognitoConfig.KEY) cognitoConfig: ConfigType<typeof CognitoConfig>, private userRepository: UserRepository, ) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, audience: cognitoConfig.appId, issuer: cognitoConfig.appAuthority, algorithms: ['RS256'], secretOrKeyProvider: passportJwtSecret({ cache: true, rateLimit: true, jwksRequestsPerMinute: 5, jwksUri: cognitoConfig.appAuthority + '/.well-known/jwks.json', }), }); } // Used internally by Passport async validate(payload: any) { const user = await this.userRepository.get(payload.email); if (!user) { throw new NotFoundException('User not found'); } return user; } }
修复方案
1. 统一受众(Audience)校验规则
AWS Cognito访问令牌的aud字段默认是生成该令牌的Client ID。如果Django和Nest.js服务使用不同的Client ID(哪怕同属一个用户池),Nest.js的JWT策略会因aud不匹配拒绝令牌。解决方法:
- 若需支持多客户端令牌,将
audience配置为包含所有允许的Client ID的数组:audience: [cognitoConfig.appId, cognitoConfig.djangoClientId], - 若已使用相同Client ID仍失败,需验证令牌的
aud字段和Nest.js配置的audience是否完全一致(注意大小写、特殊字符)。
2. 确保Issuer格式完全一致
Cognito令牌的iss字段格式为https://cognito-idp.<region>.amazonaws.com/<userPoolId>,必须和Nest.js配置的issuer完全匹配,包括末尾是否带斜杠。比如不能一边是https://xxx.com/xxx,另一边是https://xxx.com/xxx/。
3. 校验令牌类型一致性
确认Django和Nest.js使用的是同类型令牌:
- Cognito的ID令牌(ID Token)主要用于前端身份验证,
aud是Client ID; - 访问令牌(Access Token)用于资源服务器授权,若配置了资源服务器,
aud会是资源服务器的标识符而非Client ID。
两边必须统一使用访问令牌或ID令牌,且校验逻辑对应正确的令牌类型。
4. 修正JWKS URI拼接问题
检查jwksUri的拼接是否正确,避免多斜杠或路径错误。推荐使用模板字符串拼接减少手动错误:
jwksUri: `${cognitoConfig.appAuthority}/.well-known/jwks.json`,
5. 在Validate方法中补充客户端校验
如果需要更灵活的客户端权限控制,可以在validate方法中直接校验令牌的client_id字段(Cognito访问令牌会包含该字段):
async validate(payload: any) { const allowedClientIds = [cognitoConfig.appId, cognitoConfig.djangoClientId]; if (!allowedClientIds.includes(payload.client_id)) { throw new UnauthorizedException('无效客户端'); } const user = await this.userRepository.get(payload.email); if (!user) { throw new NotFoundException('用户不存在'); } return user; }
内容的提问来源于stack exchange,提问作者Arif Ata Cengiz
相关产品推荐
相关产品推荐

