coreos/go-oidc (Go)Go
错误信息
failed to verify signature: failed to verify id token signature
也可能输出为
- failed to verify signature: fetching keys oidc: get keys failed
如果 JWKS 请求本身失败,文本为“failed to verify signature: fetching keys oidc: get keys failed …”,后面跟着 HTTP 错误或网络错误。
coreos/go-oidc (Go) — IDTokenVerifier.Verify with a RemoteKeySet
含义
go-oidc 下载了提供商的密钥集(如果 `kid` 未知,会重新获取一次),但没有任何已发布的密钥能验证令牌的签名。go-oidc 不区分“没有这个 kid 的密钥”和“有这个 kid 的密钥,但签名不对”——两者都表示令牌不是由该提供商当前发布的密钥签名的。如果根本无法获取密钥集,消息后面接的则是“fetching keys …”。
原因
令牌来自另一个提供商或租户
常见来自预发布租户、另一个领域(realm)或完全不同的身份提供商的令牌。在怀疑密钥之前,先检查载荷中的 `iss`。
验证器获取了错误的密钥集
常见手动配置的 `NewRemoteKeySet` URL 指向了另一个租户的密钥,或者代理或缓存在密钥轮换后仍提供过时的 JWKS。
多个密钥复用同一个密钥 ID
偶见如果颁发者为新密钥复用同一个 `kid`(例如每个环境使用固定名称),签发的令牌中的 `kid` 就会匹配到一个已发布、但并非签名密钥的密钥。
令牌被改动过
偶见签名后对头部或载荷的任何改动——包括重新编码——都会使签名失效。
修复方法
- 1.
基于发现文档构建验证器
`provider.Verifier` 使用提供商发现文档中的 `jwks_uri` 和提供商的颁发者,这样就排除了手动配置 URL 这一不匹配来源。
go provider, err := oidc.NewProvider(ctx, "https://accounts.example.com") // discovery gives the right jwks_uri if err != nil { return err } verifier := provider.Verifier(&oidc.Config{ClientID: clientID}) idToken, err := verifier.Verify(ctx, rawIDToken) - 2.
将令牌的 kid 与已发布的密钥比对
如果该 `kid` 没有发布,说明令牌来自别处,或者提供商尚未完成密钥轮换。如果已经发布,说明它对应的密钥并没有签名这个令牌。
bash # kid in the token header vs kids the provider publishes python3 -c 'import base64,json,sys; h=sys.argv[1].split(".")[0]; print(json.loads(base64.urlsafe_b64decode(h+"="*(-len(h)%4))))' "$ID_TOKEN" curl -s "$(curl -s https://accounts.example.com/.well-known/openid-configuration | jq -r .jwks_uri)" | jq '.keys[] | {kid, alg, kty}'