knadh/listmonk · error

error parsing user info claims

Error message

error parsing user info claims

What it means

This error is returned when the claims from the OIDC UserInfo response cannot be unmarshalled into the OIDCclaim struct via userInfo.Claims(&claims). The userinfo response was fetched successfully, but its JSON payload does not fit the expected claim structure. The underlying error is discarded.

Source

Thrown at internal/auth/auth.go:277

	if err := idTk.Claims(&claims); err != nil {
		return "", OIDCclaim{}, errors.New("error getting user from OIDC")
	}

	// If claims doesn't have the e-mail, attempt to fetch it from the userinfo endpoint.
	if claims.Email == "" {
		provider, err := o.getProvider()
		if err != nil {
			return "", OIDCclaim{}, fmt.Errorf("error getting provider: %v", err)
		}

		userInfo, err := provider.UserInfo(context.TODO(), oauth2.StaticTokenSource(tk))
		if err != nil {
			return "", OIDCclaim{}, errors.New("error fetching user info from OIDC")
		}

		// Parse the UserInfo claims into the claims struct
		if err := userInfo.Claims(&claims); err != nil {
			return "", OIDCclaim{}, errors.New("error parsing user info claims")
		}
	}

	return rawIDTk, claims, nil
}

// Middleware is the HTTP middleware used for wrapping HTTP handlers registered on the echo router.
// It authorizes token (BasicAuth/token) based and cookie based sessions and on successful auth,
// sets the authenticated User{} on the echo context on the key UserKey. On failure, it sets an Error{}
// instead on the same key.
func (o *Auth) Middleware(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		// It's an `Authorization` header request.
		hdr := strings.TrimSpace(c.Request().Header.Get("Authorization"))

		// If cookie is set, ignore BasicAuth. This is to preserve backwards compatibility
		// in v3 -> v4 upgrade where the user browser sessions would still have old
		// BasicAuth credentials, which no longer work in the new system which expects

View on GitHub (pinned to 670c01717d)

Solutions

  1. Log the raw userinfo response (or decode into map[string]interface{}) and compare types against the OIDCclaim struct.
  2. Adjust OIDCclaim field types or json tags to match the IdP's userinfo schema (e.g. use a flexible type for email_verified).
  3. Wrap the underlying err with %w so the JSON unmarshal error is visible.
  4. If possible, request the email scope so the email comes from the ID token and this code path is not needed.

Example fix

// before
if err := userInfo.Claims(&claims); err != nil {
	return "", OIDCclaim{}, errors.New("error parsing user info claims")
}
// after
if err := userInfo.Claims(&claims); err != nil {
	return "", OIDCclaim{}, fmt.Errorf("error parsing user info claims: %w", err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// fetch userinfo JSON manually first and sanity-check types
var ui map[string]interface{}
json.NewDecoder(userInfoResp.Body).Decode(&ui)
if v, ok := ui["email_verified"]; ok {
	if _, isBool := v.(bool); !isBool { /* normalize or reject */ }
}

Type guard

func isString(v interface{}) bool { _, ok := v.(string); return ok }
func userinfoMatchesClaimSchema(ui map[string]interface{}) bool {
	if e, ok := ui["email"]; ok && !isString(e) { return false }
	return true
}

Try / catch

_, claims, err := auth.ExchangeOIDCToken(code, nonce)
if err != nil {
	if strings.Contains(err.Error(), "error parsing user info claims") {
		log.Printf("userinfo claim schema mismatch: %v", err)
		// fall back to a sub-based identifier if acceptable
	}
	return err
}

Prevention

When it happens

Trigger: claims.Email was empty so the userinfo fallback ran, provider.UserInfo succeeded, but userInfo.Claims(&claims) fails because the userinfo JSON contains fields whose types conflict with OIDCclaim (e.g. email_verified as a string instead of bool, nested objects) or is malformed.

Common situations: IdPs that return non-standard userinfo claim types (some return email_verified as "true"/"false" strings); custom claim transformations or middleware that reshape the response; IdP version upgrades changing the userinfo schema.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/0abefa890fcdb8bf. Report an issue: GitHub.