knadh/listmonk · error

error fetching user info from OIDC

Error message

error fetching user info from OIDC

What it means

This error is returned when the call to provider.UserInfo() — which fetches the OpenID Connect UserInfo endpoint using the exchanged OAuth2 token — fails. It only happens when the ID token claims had no email, so the library falls back to the userinfo endpoint. The underlying HTTP/network/protocol error is discarded, so the actual cause is hidden.

Source

Thrown at internal/auth/auth.go:272

	if idTk.Nonce != nonce {
		return "", OIDCclaim{}, echo.NewHTTPError(http.StatusUnauthorized, "nonce did not match")
	}

	var claims OIDCclaim
	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.

View on GitHub (pinned to 670c01717d)

Solutions

  1. Request the email and profile scopes in the OAuth2 config so the ID token itself contains the email and the userinfo call is skipped entirely.
  2. Decode the discarded err and log it (fmt.Errorf with %w) to see if it is a 401 on userinfo (token/scopes) vs a network error.
  3. Verify the IdP discovery document exposes a userinfo_endpoint and that the server can reach that URL.
  4. Confirm the access token from cfg.Exchange is valid and not expired at userinfo call time.

Example fix

// before
userInfo, err := provider.UserInfo(context.TODO(), oauth2.StaticTokenSource(tk))
if err != nil {
	return "", OIDCclaim{}, errors.New("error fetching user info from OIDC")
}
// after
userInfo, err := provider.UserInfo(context.TODO(), oauth2.StaticTokenSource(tk))
if err != nil {
	return "", OIDCclaim{}, fmt.Errorf("error fetching user info from OIDC: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure email scope is granted before exchange
if !strings.Contains(cfg.Scopes("token")[0], "email") {
	cfg = cfg.Scopes("openid", "email", "profile")
}

Type guard

func claimsHaveEmail(c auth.OIDCclaim) bool { return c.Email != "" }

Try / catch

raw, claims, err := auth.ExchangeOIDCToken(code, nonce)
if err != nil {
	if strings.Contains(err.Error(), "error fetching user info from OIDC") {
		// check network reachability of IdP userinfo endpoint, token validity
		log.Printf("userinfo fetch failed: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: After a successful token exchange, claims.Email is empty, and provider.UserInfo(context.TODO(), oauth2.StaticTokenSource(tk)) returns an error: network failure, OIDC discovery/metadata problems, expired/invalid access token rejected by the userinfo endpoint, or the provider lacks a userinfo endpoint.

Common situations: The IdP did not issue an email claim because the email scope was not requested or not consented; the access token was rejected by userinfo; corporate proxy/firewall blocks the IdP's userinfo URL; the issuer's well-known discovery lacks userinfo_endpoint.

Related errors


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