{"record":{"id":"0ae9afb0314dd1df","repo":"dgraph-io/dgraph","slug":"a-valid-jwt-is-required-but-was-not-provided","errorCode":null,"errorMessage":"a valid JWT is required but was not provided","messagePattern":"a valid JWT is required but was not provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":317,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\tif !match {\n\t\treturn fmt.Errorf(\"JWT `aud` value doesn't match with the audience\")\n\t}\n\treturn nil\n}\n\nfunc (a *AuthMeta) ExtractCustomClaims(ctx context.Context) (*CustomClaims, error) {\n\tif a == nil {\n\t\treturn &CustomClaims{}, nil\n\t}\n\t// return CustomClaims containing jwt and authvariables.\n\tmd, _ := metadata.FromIncomingContext(ctx)\n\tjwtToken := md.Get(string(AuthJwtCtxKey))\n\tif len(jwtToken) == 0 {\n\t\tif a.ClosedByDefault {\n\t\t\treturn &CustomClaims{}, fmt.Errorf(\"a valid JWT is required but was not provided\")\n\t\t}\n\t\treturn &CustomClaims{}, nil\n\t}\n\tif len(jwtToken) > 1 {\n\t\treturn nil, fmt.Errorf(\"invalid jwt auth token\")\n\t}\n\treturn a.validateJWTCustomClaims(jwtToken[0])\n}\n\nfunc GetJwtToken(ctx context.Context) string {\n\tmd, ok := metadata.FromIncomingContext(ctx)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\tjwtToken := md.Get(string(AuthJwtCtxKey))\n\tif len(jwtToken) != 1 {\n\t\treturn \"\"\n\t}","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L299-L335","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ncurl -X POST https://host/graphql -d '{\"query\":\"{ me { name } }\"}'\n// after\ncurl -X POST https://host/graphql -H 'Authorization: Bearer eyJhbGciOi...' -d '{\"query\":\"{ me { name } }\"}'","handlingStrategy":"try-catch","validationCode":"if (closedByDefault && !getAuthHeader()) {\n  throw new Error('This schema requires a JWT on every request');\n}","typeGuard":null,"tryCatchPattern":"const res = await client.query(q);\nif (res.errors?.some(e => e.message.includes('a valid JWT is required'))) {\n  await refreshSession(); // attach token, then retry\n}","preventionTips":["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"],"tags":["jwt","authentication","graphql"],"backgroundTag":"jwt-token-missing","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}