dgraph-io/dgraph · error

kid not present in JWT

Error message

kid not present in JWT

What it means

The JWT key-lookup callback runs for every token parsed against a JWK URL set. It requires a `kid` (key ID) header so it can select the correct public key from the fetched JWK set. If the token's header has no `kid`, the library cannot pick a key and rejects the token with this message. It is surfaced to the caller wrapped as "unable to parse jwt token:...".

Source

Thrown at graphql/authorization/auth.go:359

// JWKUrl.
func (a *AuthMeta) validateThroughJWKUrl(jwtStr string) (*jwt.Token, error) {
	var err error
	var token *jwt.Token
	for i := range a.JWKUrls {
		if a.isExpired(i) {
			err = a.refreshJWK(i)
			if err != nil {
				return nil, errors.Wrap(err, "while refreshing JWK from the URL")
			}
		}

		token, err = jwt.ParseWithClaims(
			jwtStr,
			&CustomClaims{authMeta: a},
			func(token *jwt.Token) (interface{}, error) {
				kid := token.Header["kid"]
				if kid == nil {
					return nil, errors.Errorf("kid not present in JWT")
				}

				signingKeys := a.jwkSet[i].Key(kid.(string))
				if len(signingKeys) == 0 {
					return nil, errors.Errorf("Invalid kid")
				}
				return signingKeys[0].Key, nil
			},
		)

		if err == nil {
			return token, nil
		}
	}
	return nil, err
}

func (a *AuthMeta) validateJWTCustomClaims(jwtStr string) (*CustomClaims, error) {

View on GitHub (pinned to 759e242be6)

Solutions

  1. Fix the token issuer so it includes a kid header matching an entry in the JWK set.
  2. If only one key is used, consider static-key verification (set Algo + VerificationKey instead of JWKUrls).
  3. Verify you are validating tokens from the correct issuer/environment.
  4. Decode the token header (base64 of the first dot-separated segment) to confirm kid is truly missing.
  5. Pin/upgrade the signing library at the issuer to one that sets kid.

Example fix

// issuer side: before
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
// after
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
token.Header["kid"] = "my-key-id-2024"
Defensive patterns

Strategy: validation

Validate before calling

func tokenHasKid(jwtStr string) bool {
    parts := strings.Split(jwtStr, ".")
    if len(parts) != 3 { return false }
    hdr, err := base64.RawURLEncoding.DecodeString(parts[0])
    if err != nil { return false }
    var h map[string]interface{}
    if json.Unmarshal(hdr, &h) != nil { return false }
    _, ok := h["kid"].(string)
    return ok
}

Type guard

func hasString(v map[string]interface{}, key string) (string, bool) {
    s, ok := v[key].(string)
    return s, ok && s != ""
}

Try / catch

if _, err := auth.ExtractCustomClaims(ctx, jwtStr); err != nil && strings.Contains(err.Error(), "kid not present") {
    return nil, status.Error(codes.Unauthenticated, "token missing kid header; obtain a token from a compliant issuer")
}

Prevention

When it happens

Trigger: jwt.ParseWithClaims invokes the keyfunc; token.Header["kid"] is nil because the JWT issuer signed the token without embedding a kid header, or the token is malformed/手工 crafted.

Common situations: Using an IdP or token signer configured to omit kid (common with single-key HS256 signers); token produced by an older service or library version that omits kid; testing with hand-rolled tokens; wrong endpoint issuing tokens from a different signer configuration.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/b9b30cb26dfafd47. Report an issue: GitHub.