{"record":{"id":"72fe2e675d6107c5","repo":"dgraph-io/dgraph","slug":"invalid-jwt-auth-token","errorCode":null,"errorMessage":"invalid jwt auth token","messagePattern":"invalid jwt auth token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":322,"sourceCode":"\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}\n\treturn jwtToken[0]\n}\n\n// validateThroughJWKUrl validates the JWT token against the given list of JWKUrls.\n// It returns an error only if the token is not validated against even one of the","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L304-L340","documentation":"After extracting JWT metadata, `ExtractCustomClaims` expects exactly one token value under the AuthJwtCtxKey metadata key. If the metadata map contains more than one value for that key, the request is considered malformed and rejected with `invalid jwt auth token`.","triggerScenarios":"Attaching the same auth-header key more than once in the request metadata (duplicate Authorization/X-Auth-Token headers), so gRPC metadata holds a slice with >1 entry for the JWT context key.","commonSituations":"A proxy or middleware adding the auth header when the client already supplied it; case-variant duplicates of the same header normalized to the same metadata key; retry logic appending the header again.","solutions":["Send the auth header exactly once per request — remove duplicates at the client or proxy","Inspect request headers after any middleware/proxy to confirm a single Authorization (or configured) header","Deduplicate headers in your ingress (e.g. nginx can merge duplicate headers into one comma-joined value, which avoids this specific multi-value metadata path but breaks JWT parsing — so drop extras instead)"],"exampleFix":"// before\nheaders: { Authorization: 'Bearer tok', authorization: 'Bearer tok' }\n// after\nheaders: { Authorization: 'Bearer tok' }","handlingStrategy":"validation","validationCode":"const authHeaders = rawHeaders.filter(h => h.toLowerCase() === 'authorization');\nif (authHeaders.length > 1) throw new Error('Duplicate Authorization header');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure middleware does not append an auth header the client already set","Audit proxies for header duplication; deduplicate rather than merge","Use a single canonical casing for the auth header in your stack"],"tags":["jwt","authentication","grpc-metadata"],"backgroundTag":"jwt-token-invalid","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}