エラーメッセージ
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` が未知の場合は 1 回だけ再取得します)、公開されているどの鍵でもトークンの署名を検証できませんでした。go-oidc は「この kid を持つ鍵がない」場合と「この kid を持つ鍵はあるが署名が誤っている」場合を区別しません。どちらも、このプロバイダーが現在公開している鍵でトークンが署名されていないことを意味します。鍵セットをまったく取得できなかった場合は、代わりにメッセージの後に「fetching keys …」が続きます。
発生する理由
トークンが別のプロバイダーやテナントのもの
よくあるステージング用のテナント、別のレルム、あるいはまったく別の ID プロバイダーのトークンです。鍵を疑う前に、ペイロードの `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}'