{"record":{"id":"181a0994304e5d21","repo":"dgraph-io/dgraph","slug":"unexpected-signing-method-expected-s-found-s","errorCode":null,"errorMessage":"unexpected signing method: Expected %s Found %s","messagePattern":"unexpected signing method: Expected (.+?) Found (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":396,"sourceCode":"\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:\n\t\t\t\t\treturn a.RSAPublicKey, nil\n\t\t\t\t}\n\n\t\t\t\treturn nil, errors.Errorf(\"couldn't parse signing method from token header: %s\", algo)\n\t\t\t})\n\t}\n\n\tif err != nil {\n\t\treturn nil, errors.Errorf(\"unable to parse jwt token:%v\", err)\n\t}\n","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L378-L414","documentation":"The `alg` header inside the incoming JWT does not match the configured a.Algo. This is an algorithm-confusion guard: it refuses tokens signed with a different algorithm than the one you explicitly configured, preventing attackers from downgrading e.g. RS256 to HS256/none. The expected and actual algorithms are included in the message.","triggerScenarios":"In validateJWTCustomClaims with no JWKUrls: jwt.ParseWithClaims keyfunc reads token.Header[\"alg\"] and it differs from a.Algo — e.g. configured \"RS256\" but token says \"HS256\", or alg header missing (algo=\"\").","commonSituations":"Tokens issued by a signer reconfigured to a different algorithm while verifiers still expect the old one; token from wrong issuer/environment; attacker-supplied token with alg none or HS256 (the guard doing its job); alg header absent from a hand-crafted token.","solutions":["Make the token issuer sign with the same algorithm configured in a.Algo; redeploy/rotate tokens if needed.","Update a.Algo to match the issuer's actual algorithm if the algorithm change was intentional.","Check the token's header (decode first JWT segment) to confirm its alg value.","Reject/monitor repeated mismatches — they may indicate attempted algorithm-confusion attacks.","Ensure both issuer and verifier config come from the same source of truth."],"exampleFix":"// before\nauth := &authorization.AuthOptions{ Algo: \"RS256\", ... } // token is HS256\n// after (issuer switched to HS256 intentionally)\nauth := &authorization.AuthOptions{ Algo: \"HS256\", SigningMethod: jwt.SigningMethodHS256, VerificationKey: sharedSecret }","handlingStrategy":"validation","validationCode":"func algMatches(jwtStr, expected string) bool {\n    parts := strings.Split(jwtStr, \".\")\n    if len(parts) != 3 { return false }\n    hdr, err := base64.RawURLEncoding.DecodeString(parts[0])\n    if err != nil { return false }\n    var h struct{ Alg string `json:\"alg\"` }\n    json.Unmarshal(hdr, &h)\n    return h.Alg == expected\n}","typeGuard":null,"tryCatchPattern":"if _, err := auth.ExtractCustomClaims(ctx, jwtStr); err != nil && strings.Contains(err.Error(), \"unexpected signing method\") {\n    return nil, status.Error(codes.Unauthenticated, \"token algorithm not accepted\")\n}","preventionTips":["Keep issuer and verifier algorithm config in the same source of truth","Explicitly whitelist algorithms; never accept alg from the token alone","Test end-to-end with a real token after any signer algorithm change","Treat mismatches as security signals and log/alert on them"],"tags":["jwt","algorithm","security","authentication"],"backgroundTag":"jwt-algorithm-mismatch","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}