dgraph-io/dgraph · error
a valid JWT is required but was not provided
Error message
a valid JWT is required but was not provided
What it means
`ExtractCustomClaims` pulls the JWT out of the gRPC context metadata. When no JWT is present in metadata and the AuthMeta is configured with ClosedByDefault (the GraphQL API is closed to unauthenticated access), it returns this error instead of allowing anonymous access. If ClosedByDefault is false, a missing JWT is silently allowed.
Solutions
- Attach a valid JWT to every request via the header named in the Dgraph.Authorization config (e.g. `Authorization: Bearer <token>` or X-Auth-Token)
- Or set ClosedByDefault=false if the schema should permit anonymous queries
- Verify the client actually sends the header (check proxy/ingress isn't stripping it) and that the header key matches the configured Header field
Example fix
// before
curl -X POST https://host/graphql -d '{"query":"{ me { name } }"}'
// after
curl -X POST https://host/graphql -H 'Authorization: Bearer eyJhbGciOi...' -d '{"query":"{ me { name } }"}' Defensive patterns
Strategy: try-catch
Validate before calling
if (closedByDefault && !getAuthHeader()) {
throw new Error('This schema requires a JWT on every request');
} Try / catch
const res = await client.query(q);
if (res.errors?.some(e => e.message.includes('a valid JWT is required'))) {
await refreshSession(); // attach token, then retry
} Prevention
- Centralize request building so the auth header is always attached
- Confirm proxies/ingress forward the Authorization header
- Match the header name to the Header field in Dgraph.Authorization
When it happens
Trigger: Querying a GraphQL schema whose Dgraph.Authorization header sets ClosedByDefault=true without attaching any JWT to the request — no value under the AuthJwtCtxKey in metadata, or the client never sent the configured auth header.
Common situations: Testing the API with curl/GraphiQL without logging in; the auth header name in Dgraph.Authorization (Header field) not matching the header the client sends; a proxy stripping the Authorization header; enabling closed-by-default security and forgetting to update clients.
Related errors
- ACL is disabled
- audience value was expected but not provided
- claims in jwt token is not map claims
- claims in jwt token is not map claims
- expecting either JWKUrl/JWKUrls or (VerificationKey, Algo)…
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/0ae9afb0314dd1df.
Report an issue: GitHub.
Appendix: source
Thrown at graphql/authorization/auth.go:317
}
}
}
if !match {
return fmt.Errorf("JWT `aud` value doesn't match with the audience")
}
return nil
}
func (a *AuthMeta) ExtractCustomClaims(ctx context.Context) (*CustomClaims, error) {
if a == nil {
return &CustomClaims{}, nil
}
// return CustomClaims containing jwt and authvariables.
md, _ := metadata.FromIncomingContext(ctx)
jwtToken := md.Get(string(AuthJwtCtxKey))
if len(jwtToken) == 0 {
if a.ClosedByDefault {
return &CustomClaims{}, fmt.Errorf("a valid JWT is required but was not provided")
}
return &CustomClaims{}, nil
}
if len(jwtToken) > 1 {
return nil, fmt.Errorf("invalid jwt auth token")
}
return a.validateJWTCustomClaims(jwtToken[0])
}
func GetJwtToken(ctx context.Context) string {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return ""
}
jwtToken := md.Get(string(AuthJwtCtxKey))
if len(jwtToken) != 1 {
return ""
}View on GitHub (pinned to 759e242be6)