hashicorp/nomad · error
invalid JWT issuer: %v
Error message
invalid JWT issuer: %v
What it means
The JWT validator rejects a token because its `iss` (issuer) claim is not present in the auth method's configured BoundIssuers list. Nomad requires that every JWT used for login be signed by a trusted issuer; this check runs only when the auth method specifies BoundIssuers. The token itself was decodable, but its issuer is not one of the allowed values.
Source
Thrown at lib/auth/jwt/validator.go:82
return nil, err
}
claims, err := validator.Validate(ctx, token, expected)
if err != nil {
return nil, fmt.Errorf("unable to verify signature of JWT token: %v", err)
}
// validate issuer manually, because we allow users to specify an array
if len(methodConf.BoundIssuer) > 0 {
if _, ok := claims["iss"]; !ok {
return nil, fmt.Errorf(
"auth method specifies BoundIssuers but the provided token does not contain issuer information",
)
}
if iss, ok := claims["iss"].(string); !ok {
return nil, fmt.Errorf("unable to read iss property of provided token")
} else if !slices.Contains(methodConf.BoundIssuer, iss) {
return nil, fmt.Errorf("invalid JWT issuer: %v", claims["iss"])
}
}
return claims, nil
}
func usingStaticKeys(keys []string) (jwt.KeySet, error) {
var parsedKeys []crypto.PublicKey
for _, v := range keys {
key, err := jwt.ParsePublicKeyPEM([]byte(v))
parsedKeys = append(parsedKeys, key)
if err != nil {
return nil, fmt.Errorf("unable to parse public key for JWT auth: %v", err)
}
}
return jwt.NewStaticKeySet(parsedKeys)
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Decode the token (e.g. jwt.io or `nomad acl login`) and read its `iss` claim, then add that exact string to the auth method's BoundIssuers via `nomad acl auth-method update`.
- Verify the auth method's BoundIssuers values match the provider's documented issuer URL exactly (scheme, host, trailing slash).
- If the issuer is dynamic/unexpected, confirm the client is fetching tokens from the intended OIDC provider and not a stale or test IdP.
Example fix
// before
authMethod := &structs.ACLAuthMethod{
Name: "keycloak",
Config: &structs.ACLAuthMethodConfig{
BoundIssuers: []string{"https://keycloak.example.com/realms/old"},
},
}
// after: issuer updated to match the token's iss claim
authMethod := &structs.ACLAuthMethod{
Name: "keycloak",
Config: &structs.ACLAuthMethodConfig{
BoundIssuers: []string{"https://keycloak.example.com/realms/prod"},
},
} Defensive patterns
Strategy: validation
Validate before calling
// decode token's iss claim client-side before login
parts := strings.Split(token, ".")
if len(parts) != 3 { return fmt.Errorf("not a JWT") }
payload, _ := base64.RawURLEncoding.DecodeString(parts[1])
var claims map[string]interface{}
json.Unmarshal(payload, &claims)
iss, _ := claims["iss"].(string)
allowed := authMethodConfig.BoundIssuers
if len(allowed) > 0 && !slices.Contains(allowed, iss) {
return fmt.Errorf("issuer %q not in BoundIssuers %v", iss, allowed)
} Type guard
func hasTrustedIssuer(claims map[string]interface{}, bound []string) bool {
iss, ok := claims["iss"].(string)
return ok && iss != "" && slices.Contains(bound, iss)
} Prevention
- Copy BoundIssuers values from the provider's discovery document `issuer` field verbatim.
- After any IdP migration, decode a fresh token and diff its iss against auth method config.
- Note oidc CLI errors include the rejected issuer — use it to update config quickly.
When it happens
Trigger: ACL auth method login with JWT auth: `nomad acl login -jwt <token>` against an auth method with JWTAuthMethod.BoundIssuers set, where the token's `iss` claim does not exactly match any entry in BoundIssuer.
Common situations: Misconfigured auth method (typo'd or missing issuer in BoundIssuers, e.g. `https://accounts.google.com` vs `accounts.google.com`); pointing clients at the wrong OIDC provider; provider changed its issuer URL after a tenant/region migration; using a token from a different environment's IdP.
Related errors
- auth method specifies BoundIssuers but the provided token do
- unable to read iss property of provided token
- no signed workload identity available
- JWT login returned an empty secret
- JWT login did not return a token
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/fb997e64427db9d4.
Report an issue: GitHub.