948e76a68b4a62e4f6f90244eeb266342406ce53
max
  Wed Sep 16 03:03:36 2026 -0700
hgLogin: make trusting a provider's unverified email a per-provider setting

Accepting an address a provider had not marked verified was applied to every
provider, to keep CILogon usable: it sends the address it got from the user's
institution but leaves email_verified at 0, even for a real institutional sign-in.
Extending that to everyone was too much. GitHub hands over a primary address whose
owner never confirmed it, and any provider a mirror adds to hg.conf was treated the
same way, so an address nobody had checked was enough to be signed in to an existing
account that used it.

New login.oauth.<name>.trustEmail, off unless an admin sets it, says that a named
provider's address may be taken without email_verified. It belongs on a provider that
reads the address from somewhere the user cannot type into, which is what CILogon
does and what GitHub does not.

Where it is off and the provider did not verify the address, the address is dropped
rather than refused, and the sign-in proceeds as one that arrived with no address at
all, which is already the ORCID case: the user is asked for an address and the account
does not work until they open the link mailed to it. Dropping it also keeps it out of
the account matching in resolveIdentity, so it cannot reach an existing account.

Documented in product/mirrorManual.txt next to the other provider settings, with the
CILogon line mirrors will need, and in oauthLogin.h with the rest of the keys.

diff --git src/hg/hgLogin/oauthLogin.h src/hg/hgLogin/oauthLogin.h
index 623fe34a2bf..6b74dff85a1 100644
--- src/hg/hgLogin/oauthLogin.h
+++ src/hg/hgLogin/oauthLogin.h
@@ -1,30 +1,37 @@
 /* oauthLogin - social login for hgLogin via OAuth 2.0 / OpenID Connect.
  *
  * Providers are configured entirely in hg.conf.  List the ones to offer with:
  *   login.oauth.providers = google,orcid,github,myuni
  * and give each a block of settings:
  *   login.oauth.<name>.label        Button text (defaults to <name>)
  *   login.oauth.<name>.clientId      OAuth client id      (required)
  *   login.oauth.<name>.clientSecret  OAuth client secret  (required)
  *   login.oauth.<name>.type          "oidc" (default) or "github"
  *   login.oauth.<name>.issuer        OIDC issuer; endpoints are auto-discovered from
  *                                    <issuer>/.well-known/openid-configuration
  *   login.oauth.<name>.authUrl       Explicit endpoints (used when there is no issuer,
  *   login.oauth.<name>.tokenUrl        or to override discovery)
  *   login.oauth.<name>.userinfoUrl
  *   login.oauth.<name>.scopes        Space-separated (default "openid email profile")
+ *   login.oauth.<name>.trustEmail    Accept this provider's address even when it does not
+ *                                    say email_verified (default off).  Turn it on only for
+ *                                    a provider that gets the address from somewhere the
+ *                                    user cannot type into, e.g. a federation that reads it
+ *                                    from the user's own institution.  Without it, an
+ *                                    unverified address is discarded and the user is asked
+ *                                    for one and has to confirm it by mail.
  *
  * "google", "orcid" and "github" are known names with built-in endpoints, so those only
  * need clientId/clientSecret.  The older login.<name>.clientId/clientSecret keys are still
  * honored.  A provider is offered only when both its clientId and clientSecret are set. */
 
 /* Copyright (C) 2026 The Regents of the University of California
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #ifndef OAUTHLOGIN_H
 #define OAUTHLOGIN_H
 
 struct oauthIdentity
 /* An authenticated identity returned by an external OAuth/OpenID provider. */
     {
     struct oauthIdentity *next;