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..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,61 +1,68 @@ /* 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..label Button text (defaults to ) * login.oauth..clientId OAuth client id (required) * login.oauth..clientSecret OAuth client secret (required) * login.oauth..type "oidc" (default) or "github" * login.oauth..issuer OIDC issuer; endpoints are auto-discovered from * /.well-known/openid-configuration * login.oauth..authUrl Explicit endpoints (used when there is no issuer, * login.oauth..tokenUrl or to override discovery) * login.oauth..userinfoUrl * login.oauth..scopes Space-separated (default "openid email profile") + * login.oauth..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..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; char *provider; /* provider short name, e.g. "google" */ char *subject; /* stable, unique id from the provider */ char *email; /* email reported by provider, or NULL */ boolean emailVerified; /* TRUE if the provider asserts the email is verified */ char *displayName; /* full name from provider, or NULL */ }; boolean oauthAnyProviderEnabled(); /* Return TRUE if at least one social login provider is configured. */ boolean oauthProviderEnabled(char *name); /* Return TRUE if the named provider is configured (clientId and clientSecret set). */ struct slName *oauthProviderNames(); /* Return the short names of all configured providers, in the order listed in hg.conf. * Do not free (owned by an internal cache). */ char *oauthProviderLabel(char *name); /* Return the display label for a provider (falls back to the name). Do not free. */ char *oauthLoginUrl(char *name, char *redirectUri, char *state); /* Return the provider's authorization URL to redirect the browser to, or NULL. Allocd. */ struct oauthIdentity *oauthFetchIdentity(char *name, char *code, char *redirectUri); /* Exchange the authorization code for tokens and fetch the authenticated identity, or NULL * on any failure. Dispose of the result with oauthIdentityFree(). */ void oauthIdentityFree(struct oauthIdentity **pId); /* Free an oauthIdentity. */ #endif /* OAUTHLOGIN_H */