Сообщение об ошибке
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 …».
Почему это происходит
Токен от другого провайдера или из другого тенанта
частоТокен из тенанта staging-окружения, из другого realm или вообще от другого поставщика удостоверений. Прежде чем подозревать ключи, проверьте `iss` в полезной нагрузке.
Объект проверки загружает не тот набор ключей
частоВручную заданный URL в `NewRemoteKeySet`, который указывает на ключи другого тенанта, или прокси/кеш, отдающий устаревший JWKS после ротации.
Один идентификатор ключа у разных ключей
иногдаИздатели, которые повторно используют `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}'