{"record":{"id":"0a0ef46839b13de2","repo":"dgraph-io/dgraph","slug":"jwt-aud-value-doesn-t-match-with-the-audience","errorCode":null,"errorMessage":"JWT `aud` value doesn't match with the audience","messagePattern":"JWT `aud` value doesn't match with the audience","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":303,"sourceCode":"\t\treturn nil\n\t}\n\n\t// If there is an audience claim, but no value provided, fail\n\tif c.authMeta.Audience == nil {\n\t\treturn fmt.Errorf(\"audience value was expected but not provided\")\n\t}\n\n\tvar match = false\n\tfor _, audStr := range c.Audience {\n\t\tfor _, expectedAudStr := range c.authMeta.Audience {\n\t\t\tif subtle.ConstantTimeCompare([]byte(audStr), []byte(expectedAudStr)) == 1 {\n\t\t\t\tmatch = true\n\t\t\t\tbreak\n\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 {","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L285-L321","documentation":"`validateAudience` compares each string in the JWT's `aud` claim against the configured Audience list using constant-time comparison. If no value matches, it returns this error, meaning the token is valid but was not issued for this Dgraph deployment per the configured audience.","triggerScenarios":"Presenting a JWT whose `aud` claim (any of its values, when aud is an array) does not equal any entry in the Dgraph.Authorization Audience list — e.g. token minted for audience \"web-app\" but Dgraph expects \"dgraph\".","commonSituations":"Pointing Dgraph at a new IDP/environment (dev token used against prod); the IDP changed its default audience; a copy-pasted config with a stale audience string; audience casing or trailing-slash differences.","solutions":["Decode the JWT and set the Dgraph `Audience` config to exactly match the token's `aud` value","Or request a token from the IDP with the audience configured in Dgraph (check the token request's audience/resource parameter)","Check for casing/whitespace/URL-trailing-slash differences between the two values"],"exampleFix":"// before\n\"Audience\":[\"https://api.example.com/\"]   // token aud: https://api.example.com\n// after\n\"Audience\":[\"https://api.example.com\"]","handlingStrategy":"validation","validationCode":"const payload = JSON.parse(atob(jwt.split('.')[1]));\nconst auds = Array.isArray(payload.aud) ? payload.aud : [payload.aud].filter(Boolean);\nif (auds.length && !auds.some(a => config.Audience?.includes(a))) {\n  throw new Error('JWT aud does not match configured Audience');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compare aud strings byte-for-byte (casing, slashes, whitespace)","Request tokens with the correct audience parameter from your IDP","Re-verify audience config after changing environments (dev/staging/prod)"],"tags":["jwt","audience","authentication"],"backgroundTag":"jwt-audience-mismatch","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}