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
- 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.
- 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.
- Verify the IdP discovery document exposes a userinfo_endpoint and that the server can reach that URL.
- 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
- Always request the email scope so the userinfo fallback rarely runs.
- Monitor egress to the IdP's userinfo endpoint from your server.
- Verify the discovery document exposes userinfo_endpoint for your issuer.
- Wrap and log the discarded underlying error in a fork/patch.
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
- error parsing user info claims
- globals.messages.invalidFields
- error getting user from OIDC
- error requesting subscription URL: %v
- non 200 response on subscription URL: %v
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/18ab2b1379cd83e8.
Report an issue: GitHub.