800ff0dc7028712146bb5ea2a691ae42d299d25b
max
  Wed Sep 9 06:40:29 2026 -0700
hgLogin: tighten activation link handling - treat a missing or empty token as invalid, and apply the seven-day expiry that the confirmation mail already promises, refs #38302

diff --git src/hg/hgLogin/hgLogin.c src/hg/hgLogin/hgLogin.c
index 72d1a7db983..50db23eebf0 100644
--- src/hg/hgLogin/hgLogin.c
+++ src/hg/hgLogin/hgLogin.c
@@ -862,45 +862,52 @@
 printSocialButtons(TRUE, FALSE, "Sign in");
 hPrintf(
     "</div><!-- END - loginBox -->"
     "\n"
     "\n"
     "</body>"
     "</html>");
 }
 
 void activateAccount(struct sqlConnection *conn)
 /* activate account */
 {
 char query[256];
 char *token = cgiUsualString("token", "");
 char *username = cgiUsualString("user","");
+/* Let the database decide whether the token is still current: setupNewAccount sets
+ * emailTokenExpires seven days out and the activation mail says the code expires then, so a
+ * link older than that must no longer work.  An unknown user name gives NULL here, and an
+ * account that has already been activated has an empty emailToken, so both fall through to
+ * the same message as a wrong token. */
 sqlSafef(query,sizeof(query),
-    "SELECT emailToken FROM gbMembers WHERE userName='%s'", username);
+    "SELECT emailToken FROM gbMembers WHERE userName='%s' AND emailTokenExpires > NOW()",
+    username);
 char *emailToken = sqlQuickString(conn, query);
-if (sameString(emailToken, token))
+if (isNotEmpty(emailToken) && sameString(emailToken, token))
     {
     sqlSafef(query,sizeof(query), "UPDATE gbMembers SET lastUse=NOW(), dateActivated=NOW(), emailToken='', emailTokenExpires='', accountActivated='Y' WHERE userName='%s'",
     username);
     sqlUpdate(conn, query);
     freez(&errMsg);
     errMsg = cloneString("Your account has been activated.");
     } 
 else
     {
     freez(&errMsg);
-    errMsg = cloneString("This activation link is not valid or has already been used.");
+    errMsg = cloneString("This activation link is not valid, has expired, or has already "
+        "been used.");
     }
 cartSetString(cart, "hgLogin_userName", username);
 
 displayLoginPage(conn);
 return;
 }
 
 /* -------- functions ---- */
 
 void changePasswordPage(struct sqlConnection *conn)
 /* change password page */
 {
 hPrintf("<div id=\"changePwBox\" class=\"centeredContainer formBox\">"
     "\n"
     "<h2>%s</h2>", brwName);