Tencent/WeKnora · error
id_token missing sub claim
Error message
id_token missing sub claim
What it means
This error is returned after an OIDC id_token has been cryptographically verified (signature, audience) but the claims payload does not contain a non-empty `sub` (subject) claim. The `sub` claim is mandated by the OIDC spec as the stable, unique identifier of the authenticated user, so without it the service cannot map the token to a user record. It signals a spec-non-compliant or misconfigured identity provider.
Source
Thrown at internal/application/service/user.go:1976
return nil, fmt.Errorf("unexpected id_token signing method: %v", token.Header["alg"])
}
kid, _ := token.Header["kid"].(string)
return jwks.rsaKeyForKid(kid)
}
claims := jwt.MapClaims{}
if _, err := jwt.NewParser(
jwt.WithValidMethods([]string{"RS256", "RS384", "RS512"}),
jwt.WithExpirationRequired(),
jwt.WithLeeway(oidcIDTokenLeeway),
jwt.WithIssuer(strings.TrimSpace(cfg.IssuerURL)),
jwt.WithAudience(strings.TrimSpace(cfg.ClientID)),
).ParseWithClaims(idToken, claims, keyFunc); err != nil {
return nil, fmt.Errorf("id_token verification failed: %w", err)
}
verified := map[string]interface{}(claims)
if strings.TrimSpace(extractClaimAsString(verified, "sub")) == "" {
return nil, errors.New("id_token missing sub claim")
}
return verified, nil
}
func extractClaimAsString(claims map[string]interface{}, key string) string {
key = strings.TrimSpace(key)
if key == "" {
return ""
}
value, ok := claims[key]
if !ok || value == nil {
return ""
}
switch v := value.(type) {
case string:
return strings.TrimSpace(v)
default:
return strings.TrimSpace(fmt.Sprint(v))View on GitHub (pinned to 988cbb0330)
Solutions
- Verify the IdP actually issues OIDC-compliant ID tokens that include `sub`; inspect a decoded token (e.g. jwt.io) to confirm.
- Check OIDC provider configuration/scopes so the issued id_token includes the subject claim.
- If using a custom token issuer, add the `sub` claim when minting the token.
- Confirm the correct token is being passed (id_token, not access_token) to the verification function.
Example fix
// before: custom issuer omits subject
claims := map[string]interface{}{"iss": "my-idp", "aud": cfg.ClientID}
// after: include the mandatory sub claim
claims := map[string]interface{}{"iss": "my-idp", "aud": cfg.ClientID, "sub": userUUID} Defensive patterns
Strategy: validation
Validate before calling
claims, _ := parseUnverified(idToken)
if strings.TrimSpace(asString(claims["sub"])) == "" {
return errors.New("id_token has no sub claim; check IdP configuration")
} Type guard
func hasSub(claims map[string]interface{}) bool {
s, ok := claims["sub"].(string)
return ok && strings.TrimSpace(s) != ""
} Prevention
- Decode a sample id_token from your IdP and confirm the `sub` claim exists.
- Keep IdP/OIDC scopes configured so issued ID tokens are spec-compliant.
- Pass the id_token (not the access_token) into the verification function.
When it happens
Trigger: Calling the OIDC login/callback flow with an id_token that parses and verifies but whose claims map has no `sub` key or an empty/whitespace-only `sub` value (checked via extractClaimAsString).
Common situations: Pointing ClientID/OIDC config at an IdP that omits `sub` (some legacy SAML-bridged or custom token issuers); a token endpoint returning a non-OIDC access token instead of an id_token; misconfigured scopes causing a slimmed-down token template.
Related errors
- OIDC provider returned no user claims
- %w: %w
- invalid external user id: %v
- empty JWK modulus or exponent
- invalid JWK exponent value
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/8274ee470b8fdae7.
Report an issue: GitHub.