Skip to content

Hash SignUp confirmation keys and expire signup links - #682

Merged
vagisha merged 3 commits into
developfrom
fb_signup-hash-expire-key
Oct 27, 2026
Merged

vagisha merged 3 commits into
developfrom
fb_signup-hash-expire-key

Conversation

@vagisha

@vagisha vagisha commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Rationale

Store only a hash of the SignUp confirmation key, expire signup links after seven days, and let each link be used once, so that the contents of signup.temporaryuser alone cannot be used to create an account for a pending signup. This applies the approach of LabKey/platform#8211 to the SignUp module.

Related Pull Requests

Changes

  • Confirmation keys are stored as SHA-256 hashes, with a keyTimeout set to LoginManager.SETUP_LINK_TIMEOUT (seven days)
  • A repeat signup for the same email issues a new key, which stops the earlier link from working
  • The key is cleared when the account is created, so each link works once
  • The confirmation email includes the link expiration
  • The admin ConfirmationURL column masks the key, and is empty once the key is used or has expired
  • signup-26.000-26.001.sql clears the keys of signups that already have an account, and hashes the remaining keys in place with an expiry seven days after upgrade
  • SignUpManager.TestCase integration tests cover hashed storage, wrong, expired, replaced and used keys, and repeat signups

Co-Authored-By: Claude noreply@anthropic.com

* Stored only a SHA-256 hash of the confirmation key in signup.temporaryuser, with a keyTimeout seven days out
* Changed a repeat signup to replace the key and the stored name, organization and folder
* Cleared the key when the account is created, so each link works once
* Added the link expiration to the confirmation email
* Masked the key in the admin ConfirmationURL column, and left the column empty for used keys
* Added signup-26.000-26.001.sql, which hashes pending keys, sets them to expire seven days after upgrade, and clears used keys
* Added SignUpManager unit tests

Ports the token hashing and expiration from LabKey/platform#8211.

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Confirmation-key validation and consumption are not atomic, allowing a concurrent repeat signup to revoke a key that still succeeds in creating an account.

1 open issue
What changed in this PR

Extends the platform’s verification-token protections to SignUp by hashing confirmation keys, adding seven-day expiration, and clearing keys after account creation.

Changes:

  • Replaces keys and signup details on repeat signup, and adds lifecycle tests.
  • Includes expiration in confirmation emails and masks keys in the admin view.
  • Migrates existing keys to hashes and clears keys for completed signups.
File Description
signup/​src/​org/​labkey/​signup/​TempUser.java Adds the key-expiration field and documents hashed storage.
signup/​src/​org/​labkey/​signup/​SignUpSchema.java Masks confirmation keys and hides used links.
signup/​src/​org/​labkey/​signup/​SignUpModule.java Advances the schema version and registers unit tests.
signup/​src/​org/​labkey/​signup/​SignUpManager.java Implements key hashing, expiration, replacement, consumption, and tests.
signup/​src/​org/​labkey/​signup/​SignUpController.java Integrates key management and expiration-aware emails.
signup/​resources/​schemas/​signup.xml Declares the key-expiration column.
signup/​resources/​schemas/​dbscripts/​postgresql/​signup-26.000-26.001.sql Migrates existing keys and assigns pending-link expiration.

Review effort: Balanced

* Added SignUpManager.clearKeyIfValid, which clears the key only if it still matches and has not expired, and changed ConfirmAction to call it first in the account transaction and roll back when it returns false
* Changed a repeat signup to replace only the key, keeping the first signup's name, organization and folder
* Replaced the full-row saves in saveWithNewKey and setLabkeyUserId with Table.update calls that set only the changed columns
* Changed the ConfirmationURL column to show the masked link only for unused, unexpired keys, in the grid, exports and the JSON API
* Moved SignUpManager.TestCase to getIntegrationTests, since it needs the database
* Added tests for replaced, used and expired keys

Co-Authored-By: Claude <noreply@anthropic.com>
@vagisha
vagisha marked this pull request as ready for review October 26, 2026 21:27

@labkey-adam labkey-adam left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

* Marked the return value and the key parameter @nullable

Co-Authored-By: Claude <noreply@anthropic.com>
@vagisha
vagisha merged commit f9a0a62 into develop Oct 27, 2026
6 checks passed
@vagisha
vagisha deleted the fb_signup-hash-expire-key branch October 27, 2026 16:46
labkey-adam added a commit to LabKey/platform that referenced this pull request Oct 28, 2026
## Rationale
Update a few methods to take `@NotNull` link timeouts. The only caller
who passed `null` has been migrated.

Also, test a couple invariants in `HasPermissionOption.ALL.accept()`.

## Related Pull Requests
- LabKey/MacCossLabModules#682
XingY pushed a commit to LabKey/platform that referenced this pull request Oct 29, 2026
## Rationale
Update a few methods to take `@NotNull` link timeouts. The only caller
who passed `null` has been migrated.

Also, test a couple invariants in `HasPermissionOption.ALL.accept()`.

## Related Pull Requests
- LabKey/MacCossLabModules#682
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants