如何在Next.js TS API中验证AAD访问令牌?签名验证失败求助
解决Azure AD令牌在Next.js API中验证失败(invalid signature)的问题
问题排查与修复步骤
1. 修正JWKS URI地址
你现在用的JWKS URI带了appid参数,这会导致获取到的密钥不匹配。Azure AD的标准JWKS地址要根据你用的端点版本选择:
- v2.0端点:
https://login.microsoftonline.com/<你的租户ID>/discovery/v2.0/keys - v1.0端点:
https://login.microsoftonline.com/<你的租户ID>/discovery/keys
不用加appid参数,jwks-rsa库会自动根据令牌头部的kid匹配对应密钥。
2. 用jwks-rsa内置方法获取签名密钥
别自己手动拼接证书字符串,jwks-rsa的getSigningKey方法会自动处理密钥格式,避免格式错误导致签名验证失败。
3. 补充必要的验证参数
哪怕忽略过期时间,也得验证令牌的audience(你的Next.js API在Azure AD中的客户端ID)和issuer(签发者地址),否则可能验证的是其他应用的令牌。
4. 确认令牌类型与受众
确保Flutter客户端获取的是访问令牌(而非ID令牌),且令牌的aud字段值是你Next.js API的客户端ID,不是Flutter应用的客户端ID。
修正后的验证代码
import jwt, { VerifyOptions } from 'jsonwebtoken'; import jwksClient from 'jwks-rsa'; // 替换为你的租户ID和API客户端ID const TENANT_ID = '<你的租户ID>'; const API_CLIENT_ID = '<你的Next.js API客户端ID>'; // 选择对应版本的JWKS URI const JWKS_URI = `https://login.microsoftonline.com/${TENANT_ID}/discovery/v2.0/keys`; // 签发者地址,v2.0用此格式,v1.0为https://sts.windows.net/${TENANT_ID}/ const ISSUER = `https://login.microsoftonline.com/${TENANT_ID}/v2.0`; const client = jwksClient({ jwksUri: JWKS_URI, }); export default async function isValidToken(token: string): Promise<boolean> { try { // 解码令牌获取kid字段 const decoded = jwt.decode(token, { complete: true }); if (!decoded || !decoded.header?.kid) { console.log('令牌格式无效或缺少kid字段'); return false; } // 通过kid获取对应签名密钥 const key = await client.getSigningKey(decoded.header.kid); const publicKey = key.getPublicKey(); const verifyOptions: VerifyOptions = { ignoreExpiration: true, // 调试阶段临时忽略过期,上线后建议关闭 ignoreNotBefore: true, algorithms: ['RS256'], audience: API_CLIENT_ID, // 必须验证受众 issuer: ISSUER // 必须验证签发者 }; // 执行令牌验证 jwt.verify(token, publicKey, verifyOptions); console.log('令牌验证通过'); return true; } catch (err) { console.log('令牌验证失败:', err); return false; } }
额外检查项
- 确认Flutter请求令牌时,
scope参数包含你的API权限范围(例如api://<API客户端ID>/access_as_user) - 检查令牌的
iss字段是否与你设置的ISSUER一致,可以手动解码令牌查看字段值 - 确保Azure AD中API应用的「允许的令牌受众」包含你的API客户端ID
内容的提问来源于stack exchange,提问作者elshamjs
相关产品推荐
相关产品推荐

