Tencent/WeKnora · error
id_token missing kid and JWKS contains multiple RSA signing
Error message
id_token missing kid and JWKS contains multiple RSA signing keys
What it means
When an id_token lacks a kid header, verification can only proceed unambiguously if the JWKS contains exactly one RSA key. With multiple RSA keys present the correct one cannot be chosen, so the service fails closed instead of guessing.
Source
Thrown at internal/application/service/user.go:1897
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")
resp, err := newOIDCHTTPClient().Do(req)
if err != nil {
return nil, errView on GitHub (pinned to 988cbb0330)
Solutions
- Fix the token issuer to include kid in the JOSE header (standard behavior for Auth0/Keycloak/Okta, etc.).
- Use only one active signing key on the provider, or
- If you control token generation, sign with the key whose kid you emit.
- For testing, generate tokens with the same kid that the test JWKS publishes.
Example fix
// before: token header {"alg":"RS256"}
// after: token header includes key id
{"alg":"RS256","kid":"key-1"} Defensive patterns
Strategy: validation
Validate before calling
hdr := decodeHeader(idToken)
rsaKeys := countRSAKeys(jwks)
if hdr.Kid == "" && rsaKeys > 1 {
return fmt.Errorf("token has no kid but JWKS has %d RSA keys; issuer must emit kid", rsaKeys)
} Type guard
func unambiguousToken(idToken string, jwks *oidcJWKS) bool {
kid := decodeHeader(idToken).Kid
if kid != "" { return true }
n := 0
for _, k := range jwks.Keys { if k.Kty == "RSA" { n++ } }
return n == 1
} Prevention
- Ensure your token issuer always sets kid in the JOSE header.
- Keep a single active signing key if you control the IdP and omit kid.
- Check provider settings for an 'include kid in token header' option.
When it happens
Trigger: Token header has no kid, rsaKeyForKid skips the kid-match branch, and `usable` contains 2+ RSA keys from the JWKS.
Common situations: Provider rotates between several active signing keys but omits kid from token headers (provider bug/misconfiguration); custom token issuer that doesn't set kid; test tokens generated without a kid header.
Related errors
- no matching JWKS key for id_token
- empty JWK modulus or exponent
- invalid JWK exponent value
- JWKS document contains no keys
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/6de998e5726ab80f.
Report an issue: GitHub.