{"record":{"id":"f40889f2277013e6","repo":"dgraph-io/dgraph","slug":"invalid-kid","errorCode":null,"errorMessage":"Invalid kid","messagePattern":"Invalid kid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":364,"sourceCode":"\t\tif a.isExpired(i) {\n\t\t\terr = a.refreshJWK(i)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, errors.Wrap(err, \"while refreshing JWK from the URL\")\n\t\t\t}\n\t\t}\n\n\t\ttoken, err = jwt.ParseWithClaims(\n\t\t\tjwtStr,\n\t\t\t&CustomClaims{authMeta: a},\n\t\t\tfunc(token *jwt.Token) (interface{}, error) {\n\t\t\t\tkid := token.Header[\"kid\"]\n\t\t\t\tif kid == nil {\n\t\t\t\t\treturn nil, errors.Errorf(\"kid not present in JWT\")\n\t\t\t\t}\n\n\t\t\t\tsigningKeys := a.jwkSet[i].Key(kid.(string))\n\t\t\t\tif len(signingKeys) == 0 {\n\t\t\t\t\treturn nil, errors.Errorf(\"Invalid kid\")\n\t\t\t\t}\n\t\t\t\treturn signingKeys[0].Key, nil\n\t\t\t},\n\t\t)\n\n\t\tif err == nil {\n\t\t\treturn token, nil\n\t\t}\n\t}\n\treturn nil, err\n}\n\nfunc (a *AuthMeta) validateJWTCustomClaims(jwtStr string) (*CustomClaims, error) {\n\tvar token *jwt.Token\n\tvar err error\n\t// Verification through JWKUrl\n\tif len(a.JWKUrls) != 0 {\n\t\ttoken, err = a.validateThroughJWKUrl(jwtStr)","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L346-L382","documentation":"The token carried a `kid` header, but no key in the fetched JWK set for the current URL matches that key ID (jwkSet[i].Key(kid) returned zero keys). The library cannot find the public key needed to verify the signature, so validation fails. Often means the IdP rotated keys and the cached/local JWK set is stale, or the token comes from a different issuer.","triggerScenarios":"Inside the ParseWithClaims keyfunc after kid is present: a.jwkSet[i].Key(kid.(string)) returns an empty slice — kid not present in the key set fetched from JWKUrls[i]. Loop then tries the next JWK URL and, if all fail, validation fails.","commonSituations":"IdP rotated signing keys while the service cached an old JWK set; token issued by a different environment/tenant (e.g. staging token validated against prod JWKS URL); wrong JWKS URL configured; multi-region IdPs serving different keys.","solutions":["Force a JWK refresh (restart or call FetchJWKs/refreshJWK) so the cache contains the IdP's current keys.","Confirm the JWKUrls point to the same issuer/tenant that mints the tokens.","Compare the token's kid against the kids in the fetched JWKS JSON to spot the mismatch.","If a rotation just happened, wait for or trigger a cache expiry so isExpired triggers refreshJWK.","Support multiple JWK URLs (one per issuer/region) so all valid tokens find their key."],"exampleFix":"// before: stale cache causes mismatch\n// (cached jwkSet missing new kid)\n// after: refresh keys when kid is unknown\nsigningKeys := a.jwkSet[i].Key(kid.(string))\nif len(signingKeys) == 0 {\n    if err := a.refreshJWK(i); err == nil {\n        signingKeys = a.jwkSet[i].Key(kid.(string))\n    }\n}\nif len(signingKeys) == 0 {\n    return nil, errors.Errorf(\"Invalid kid: %s\", kid)\n}","handlingStrategy":"retry","validationCode":"// verify kid is present in current JWKS before validation\nresp, _ := http.Get(jwksURL)\nb, _ := io.ReadAll(resp.Body)\nvar set struct{ Keys []struct{ Kid string `json:\"kid\"` } `json:\"keys\"` }\njson.Unmarshal(b, &set)\nfor _, k := range set.Keys {\n    if k.Kid == tokenKid { return nil }\n}\nreturn fmt.Errorf(\"kid %s not in JWKS; refresh keys\", tokenKid)","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"Invalid kid\") {\n    if rerr := auth.FetchJWKs(); rerr == nil {\n        claims, err = auth.ExtractCustomClaims(ctx, jwtStr) // retry once with fresh keys\n    }\n}","preventionTips":["Refresh JWK sets on unknown kid instead of failing outright","Keep TTL for JWK cache shorter than the IdP rotation interval","Subscribe to IdP rotation events/webhooks where available","Verify staging and prod use matching JWKS URLs per environment"],"tags":["jwt","jwk","kid","key-rotation"],"backgroundTag":"jwt-kid-not-found","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}