Tencent/WeKnora · error
no matching JWKS key for id_token
Error message
no matching JWKS key for id_token
What it means
During id_token verification, no RSA key in the fetched JWKS matched the token. If the token carried a kid, no key with that kid existed; if it carried no kid, the JWKS had no usable RSA keys at all. Verification cannot proceed without the provider's signing key.
Source
Thrown at internal/application/service/user.go:1894
if k.Kty != "" && !strings.EqualFold(k.Kty, "RSA") {
continue
}
if kid != "" && k.Kid != kid {
continue
}
if _, err := k.rsaPublicKey(); err != nil {
continue
}
usable = append(usable, k)
}
if kid != "" {
if len(usable) == 0 {
return nil, fmt.Errorf("no matching JWKS RSA key for kid %q", kid)
}
return usable[0].rsaPublicKey()
}
if len(usable) == 0 {
return nil, errors.New("no matching JWKS key for id_token")
}
if len(usable) > 1 {
return nil, errors.New("id_token missing kid and JWKS contains multiple RSA signing keys")
}
return usable[0].rsaPublicKey()
}
// fetchOIDCJWKS loads the provider's JWKS document over the SSRF-safe client.
func (s *userService) fetchOIDCJWKS(ctx context.Context, jwksURI string) (*oidcJWKS, error) {
if err := validateOIDCEndpoint("jwks", jwksURI, true); err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, jwksURI, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
View on GitHub (pinned to 988cbb0330)
Solutions
- Confirm cfg.JwksURI matches the provider's advertised jwks_uri from discovery (.well-known/openid-configuration).
- Decode the id_token header and check its kid exists in the JWKS document.
- Re-fetch/refresh the JWKS — the key may have been rotated recently.
- Ensure the provider includes kid in token headers and publishes the corresponding key.
Example fix
// before JwksURI: "https://old-provider.example.com/.well-known/jwks.json" // after JwksURI: discovery.Doc.JWKSURI // from the provider's openid-configuration
Defensive patterns
Strategy: retry
Validate before calling
// decode token header and pre-check kid against cached JWKS
parts := strings.Split(idToken, ".")
hdr := decodeHeader(parts[0])
if !jwksHasKid(jwks, hdr.Kid) {
jwks = refreshJWKS(ctx, cfg.JwksURI) // key rotation?
} Type guard
func jwksHasKid(j *oidcJWKS, kid string) bool {
for _, k := range j.Keys {
if k.Kid == kid && k.Kty == "RSA" { return true }
}
return false
} Try / catch
key, err := svc.VerifyOIDCIDToken(ctx, cfg, idToken)
if err != nil && strings.Contains(err.Error(), "no matching JWKS") {
// refresh JWKS once, then retry verification
if rerr := svc.RefreshJWKS(ctx, cfg.JwksURI); rerr == nil {
key, err = svc.VerifyOIDCIDToken(ctx, cfg, idToken)
}
} Prevention
- Derive jwks_uri from provider discovery instead of hardcoding it.
- Refresh JWKS on kid-miss (standard rotation handling).
- Monitor provider key-rotation announcements.
When it happens
Trigger: verifyOIDCIDToken selects rsaKeyForKid(kid) (or the kid-less path) and the filtered `usable` slice is empty — token kid not present in JWKS, or JWKS has zero RSA keys when the token has no kid.
Common situations: Provider rotated signing keys and the configured jwks_uri points at a stale copy; jwks_uri misconfigured to the wrong provider's JWKS; token signed with a key not yet published; token missing kid while provider publishes multiple keys elsewhere.
Related errors
- id_token missing kid and JWKS contains multiple RSA signing
- empty JWK modulus or exponent
- invalid JWK exponent value
- JWKS document contains no keys
- OIDC provider returned no user claims
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/0c4a7ddc1f4f3222.
Report an issue: GitHub.