{"record":{"id":"dda2b7e0883a0c9a","repo":"dgraph-io/dgraph","slug":"jwt-token-cannot-be-validated-because-verification","errorCode":null,"errorMessage":"jwt token cannot be validated because verification algorithm is not set","messagePattern":"jwt token cannot be validated because verification algorithm is not set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":385,"sourceCode":"\t\t\t},\n\t\t)\n\n\t\tif err == nil {\n\t\t\treturn token, nil\n\t\t}\n\t}\n\treturn nil, err\n}\n\nfunc (a *AuthMeta) validateJWTCustomClaims(jwtStr string) (*CustomClaims, error) {\n\tvar token *jwt.Token\n\tvar err error\n\t// Verification through JWKUrl\n\tif len(a.JWKUrls) != 0 {\n\t\ttoken, err = a.validateThroughJWKUrl(jwtStr)\n\t} else {\n\t\tif a.Algo == \"\" {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"jwt token cannot be validated because verification algorithm is not set\")\n\t\t}\n\n\t\t// The JWT library supports comparison of `aud` in JWT against a single string. Hence, we\n\t\t// disable the `aud` claim verification at the library end using `WithoutAudienceValidation` and\n\t\t// use our custom validation function `validateAudience`.\n\t\ttoken, err =\n\t\t\tjwt.ParseWithClaims(jwtStr, &CustomClaims{authMeta: a}, func(token *jwt.Token) (interface{}, error) {\n\t\t\t\talgo, _ := token.Header[\"alg\"].(string)\n\t\t\t\tif algo != a.Algo {\n\t\t\t\t\treturn nil, errors.Errorf(\"unexpected signing method: Expected %s Found %s\",\n\t\t\t\t\t\ta.Algo, algo)\n\t\t\t\t}\n\n\t\t\t\tswitch a.SigningMethod.(type) {\n\t\t\t\tcase *jwt.SigningMethodHMAC:\n\t\t\t\t\treturn []byte(a.VerificationKey), nil\n\t\t\t\tcase *jwt.SigningMethodRSA:","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L367-L403","documentation":"When no JWKUrls are configured, the library falls back to verifying the JWT with a locally configured algorithm (a.Algo) and verification key. If a.Algo is empty, there is no way to know which signing algorithm to enforce, so validation is refused before parsing. It indicates the AuthMeta was configured for static-key verification but the algorithm field was never set.","triggerScenarios":"validateJWTCustomClaims (via ExtractCustomClaims) is called with len(a.JWKUrls)==0 and a.Algo == \"\" — i.e. AuthOptions neither set JWKUrls nor Algo.","commonSituations":"AuthOptions built programmatically and Algo forgotten; config file key for algorithm missing/typo so it binds as empty string; code path switched from JWK-based auth to static keys without updating Algo; environment variable not set.","solutions":["Set Algo in your AuthOptions (e.g. \"RS256\" or \"HS256\") to match the token issuer.","Ensure the config/env value feeding Algo is present and non-empty at startup.","Fail fast: validate configuration at boot and reject AuthMeta with empty Algo when JWKUrls is empty.","If you meant JWK-based verification, populate JWKUrls and call FetchJWKs at startup instead.","Log the effective AuthMeta (sans secrets) at startup to catch empty Algo early."],"exampleFix":"// before\nauth := &authorization.AuthOptions{ VerificationKey: key }\nclaims, err := auth.ExtractCustomClaims(ctx, tokenStr)\n// after\nauth := &authorization.AuthOptions{ Algo: \"RS256\", VerificationKey: key }\nclaims, err := auth.ExtractCustomClaims(ctx, tokenStr)","handlingStrategy":"validation","validationCode":"func (a *AuthMeta) validateConfig() error {\n    if len(a.JWKUrls) == 0 && a.Algo == \"\" {\n        return errors.New(\"auth misconfigured: set JWKUrls or Algo+VerificationKey\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := authCfg.validateConfig(); err != nil {\n    log.Fatalf(\"invalid auth configuration: %v\", err) // fail at startup, not per-request\n}","preventionTips":["Validate AuthOptions at startup and fail fast","Keep Algo and JWKUrls settings in one config struct filled from a single source","Add a config schema check (non-empty algorithm when no JWKS URLs)","Cover auth config parsing with a startup smoke test"],"tags":["jwt","configuration","authentication","misconfiguration"],"backgroundTag":"missing-config-value","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}