dgraph-io/dgraph · error
unexpected signing method: Expected %s Found %s
Error message
unexpected signing method: Expected %s Found %s
What it means
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.
Source
Thrown at graphql/authorization/auth.go:396
var token *jwt.Token
var err error
// Verification through JWKUrl
if len(a.JWKUrls) != 0 {
token, err = a.validateThroughJWKUrl(jwtStr)
} else {
if a.Algo == "" {
return nil, fmt.Errorf(
"jwt token cannot be validated because verification algorithm is not set")
}
// The JWT library supports comparison of `aud` in JWT against a single string. Hence, we
// disable the `aud` claim verification at the library end using `WithoutAudienceValidation` and
// use our custom validation function `validateAudience`.
token, err =
jwt.ParseWithClaims(jwtStr, &CustomClaims{authMeta: a}, func(token *jwt.Token) (interface{}, error) {
algo, _ := token.Header["alg"].(string)
if algo != a.Algo {
return nil, errors.Errorf("unexpected signing method: Expected %s Found %s",
a.Algo, algo)
}
switch a.SigningMethod.(type) {
case *jwt.SigningMethodHMAC:
return []byte(a.VerificationKey), nil
case *jwt.SigningMethodRSA:
return a.RSAPublicKey, nil
}
return nil, errors.Errorf("couldn't parse signing method from token header: %s", algo)
})
}
if err != nil {
return nil, errors.Errorf("unable to parse jwt token:%v", err)
}
View on GitHub (pinned to 759e242be6)
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.
Example fix
// before
auth := &authorization.AuthOptions{ Algo: "RS256", ... } // token is HS256
// after (issuer switched to HS256 intentionally)
auth := &authorization.AuthOptions{ Algo: "HS256", SigningMethod: jwt.SigningMethodHS256, VerificationKey: sharedSecret } Defensive patterns
Strategy: validation
Validate before calling
func algMatches(jwtStr, expected string) bool {
parts := strings.Split(jwtStr, ".")
if len(parts) != 3 { return false }
hdr, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil { return false }
var h struct{ Alg string `json:"alg"` }
json.Unmarshal(hdr, &h)
return h.Alg == expected
} Try / catch
if _, err := auth.ExtractCustomClaims(ctx, jwtStr); err != nil && strings.Contains(err.Error(), "unexpected signing method") {
return nil, status.Error(codes.Unauthenticated, "token algorithm not accepted")
} Prevention
- 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
When it happens
Trigger: 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="").
Common situations: 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.
Related errors
- unexpected signing method in token: %v
- expecting either JWKUrl or JWKUrls, both were given
- expecting either JWKUrl/JWKUrls or (VerificationKey, Algo),
- required field missing in Dgraph.Authorization:%s
- invalid Bearer-formatted header value for JWT (%s)
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/181a0994304e5d21.
Report an issue: GitHub.