{"record":{"id":"c7c9d0ba90b09ec9","repo":"knadh/listmonk","slug":"globals-messages-invalidfields","errorCode":null,"errorMessage":"globals.messages.invalidFields","messagePattern":"globals\\.messages\\.invalidFields","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/auth.go","lineNumber":235,"sourceCode":"\t// Validate the state.\n\tvar state oidcState\n\tstateB, err := base64.URLEncoding.DecodeString(c.QueryParam(\"state\"))\n\tif err != nil {\n\t\ta.log.Printf(\"error decoding OIDC state: %v\", err)\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T(\"globals.messages.internalError\"))\n\t}\n\tif err := json.Unmarshal(stateB, &state); err != nil {\n\t\ta.log.Printf(\"error unmarshalling OIDC state: %v\", err)\n\t\treturn echo.NewHTTPError(http.StatusInternalServerError, a.i18n.T(\"globals.messages.internalError\"))\n\t}\n\tif state.Nonce != nonce.Value {\n\t\treturn a.renderLoginPage(c, echo.NewHTTPError(http.StatusUnauthorized, a.i18n.T(\"users.invalidRequest\")))\n\t}\n\n\t// Validate e-mail from the claim.\n\temail := strings.TrimSpace(claims.Email)\n\tif email == \"\" {\n\t\treturn a.renderLoginPage(c, errors.New(a.i18n.Ts(\"globals.messages.invalidFields\", \"name\", \"email\")))\n\t}\n\tem, err := mail.ParseAddress(email)\n\tif err != nil {\n\t\treturn a.renderLoginPage(c, err)\n\t}\n\temail = strings.ToLower(em.Address)\n\tclaims.Email = email\n\n\t// Get the user by e-mail received from OIDC.\n\tuser, userErr := a.core.GetUser(0, \"\", email)\n\tif userErr != nil {\n\t\t// If the user doesn't exist, and auto-creation is enabled, create a new user.\n\t\tif httpErr, ok := userErr.(*echo.HTTPError); ok && httpErr.Code == http.StatusNotFound && a.cfg.Security.OIDC.AutoCreateUsers {\n\t\t\tu, err := a.createOIDCUser(claims, c)\n\t\t\tif err != nil {\n\t\t\t\treturn a.renderLoginPage(c, err)\n\t\t\t}\n\t\t\tuser = u","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/cmd/auth.go#L217-L253","documentation":"In OIDCFinish (cmd/auth.go), after a successful OIDC callback the ID token claims are inspected and the email claim is extracted. If the trimmed claim email is empty, the login page is re-rendered with a localized 'invalid fields' error naming the email field. The library throws it because it refuses to link an OIDC identity to a local account without a usable e-mail address.","triggerScenarios":"Completing the OIDC login flow (GET the OIDC finish/callback endpoint) when the ID token issued by the identity provider has an empty or missing email claim after strings.TrimSpace(claims.Email).","commonSituations":"Identity providers (e.g. Keycloak, Azure AD, Google) configured without the 'email' scope, users with no primary e-mail set, or OIDC clients whose requested scopes do not include the profile/email scopes so claims.Email stays empty.","solutions":["Add the 'email' (and usually 'profile') scope to the OIDC client configuration so the provider returns the email claim.","In the identity provider, ensure users have a verified primary e-mail address set.","Check that claims are correctly mapped: some providers nest email under a different claim; configure claim mapping or a custom claims provider.","Show a clearer message to the user advising them to contact their admin if their provider cannot supply an e-mail."],"exampleFix":"// before\nscopes := []string{\"openid\"}\n// after\nscopes := []string{\"openid\", \"email\", \"profile\"}","handlingStrategy":"validation","validationCode":"// before calling the OIDC finish endpoint, ensure the provider returns email\nconst hasEmail = !!(claims && typeof claims.email === 'string' && claims.email.trim().length > 0);\nif (!hasEmail) throw new Error('OIDC provider did not return an email claim');","typeGuard":"function hasEmailClaim(c: unknown): c is { email: string } {\n  return typeof c === 'object' && c !== null && 'email' in c && typeof (c as any).email === 'string' && (c as any).email.trim() !== '';\n}","tryCatchPattern":"try {\n  const res = await fetch(oidcFinishURL, { redirect: 'follow' });\n} catch (e) {\n  if (String(e).includes('invalidFields')) {\n    showError('Your identity provider did not provide an e-mail address; contact your admin.');\n  }\n}","preventionTips":["Always request 'openid email profile' scopes in the OIDC client.","Verify each provider returns the email claim during integration testing.","Handle accounts without e-mail at the provider, not just in this app."],"tags":["oidc","authentication","email","validation"],"backgroundTag":"missing-oidc-email-claim","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}