Skip to content

self service improvement - #4100

Draft
fhanik wants to merge 4 commits into
developfrom
pr/self-service-improvement
Draft

fhanik wants to merge 4 commits into
developfrom
pr/self-service-improvement

Conversation

@fhanik

@fhanik fhanik commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Self-service account confirmation now requires the owner to set their password
through the activation link. Previously the password typed on the sign-up form was
activated as soon as the account was confirmed — but confirming the link only proves
ownership of the email address, not that the person confirming chose that password.

Change

On confirmation, completeActivation now:

  • verifies the account,
  • replaces the stored password with a random value so no previously submitted
    password can be used to sign in,
  • issues an ownership-bound, single-use reset code.

/verify_user then sends the owner to the reset-password page (showing "A password
change is required due to changes in the system."
) to set their own password via the
existing code-authenticated flow.

Behavior changes

  • Signup requires setting the password via the confirmation link; the sign-up-form
    password is never used to sign in.
  • Landing after confirmation changes from the login page to the password-set page,
    then login once the password is set.
  • An explicit client redirect_uri is still honored; the signup_redirect_url
    fallback currently lands on home (see open item).

Tests

  • Added tests covering the confirm → set-password → sign-in flow.
  • Updated existing self-service unit/mock and Selenium flows. Unit suites green
    (server, uaa); integrationTest to run in CI.

Open item

  • Restore the signup_redirect_url fallback redirect and its test assertions.

fhanik added 2 commits October 7, 2026 12:19
Self-service account confirmation (/verify_user) currently marks the
account verified while leaving the password that was submitted on the
registration form in place. Clicking the confirmation link only proves
ownership of the email address; it does not prove that the clicker chose
the stored password. As a result the password supplied by whoever
registered the address becomes a usable credential once the owner
confirms the account.

Add two red MockMvc tests on the self-service flow:

- confirmingAccountDoesNotAuthorizeThePreRegisteredPassword: a single
  registration followed by confirmation must not let the registration
  password authenticate.
- confirmingAccountDoesNotAuthorizePasswordFromEarlierRegistration: when
  the address is registered twice before confirmation, the earlier
  password must not authenticate after the owner confirms via the most
  recent link.

Both tests fail today, demonstrating the gap. The following commit
closes it.
Self-service registration stored the password from the sign-up form and
activated it the moment the account was confirmed. Because confirming the
activation link only proves ownership of the email address — not that the
person confirming chose the stored password — a password set before the
address was verified could end up authorizing access to the confirmed
account.

Establish the password at confirmation instead:

- EmailAccountCreationService.completeActivation now verifies the account,
  discards the stored password by replacing it with an unguessable random
  value, flags the account as requiring a password change, and issues an
  ownership-bound, single-use reset code for the user.
- AccountsController redirects /verify_user to the reset-password page
  (with force_change=true) so the confirming owner sets their own password
  through the same code-authenticated flow used for password recovery; no
  previous password is required.
- ResetPasswordController surfaces a "password change required due to
  changes in the system" notice on that page (new reset_password.force_change
  message).

A setting-independent consequence: the password typed on the sign-up form is
never usable to authenticate; only the owner who confirms via the emailed
link can set the account's password.

Existing self-service tests are updated to complete the new
confirm-then-set-password flow, and the configuration reference documents the
behavior. The security tests added in the previous commit now pass.

Copilot AI 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.

🟡 Changes recommended

Account verification has a credential race, and the configured signup redirect fallback is lost.

4 open findings
What changed in this PR

Updates self-service signup so activation verifies email ownership, invalidates the registration password, and redirects users through password setup.

Changes:

  • Generates a reset code during account activation.
  • Adds forced-password messaging and updates browser flows.
  • Expands unit, MockMvc, and integration coverage.
File Description
server/​.../​AccountCreationService.java Adds reset code to activation responses.
server/​.../​AccountsController.java Redirects activation to password setup.
server/​.../​EmailAccountCreationService.java Invalidates signup passwords and creates reset codes.
server/​.../​ResetPasswordController.java Adds forced-change page messaging.
server/​.../​AccountsControllerTest.java Updates activation redirect tests.
server/​.../​EmailAccountCreationServiceTests.java Updates service construction and reset mocks.
uaa/​.../​AccountsControllerMockMvcTests.java Covers the new default-zone flow.
uaa/​.../​AccountsControllerMockMvcZonePathTests.java Covers zone-path activation flows.
uaa/​.../​CreateAccountIT.java Updates end-to-end signup scenarios.
uaa/​.../​IntegrationTestUtils.java Completes password setup in Selenium fixtures.
uaa/​src/​main/​resources/​messages.properties Adds forced-change text.
docs/​UAA-Configuration-Reference.md Documents the changed signup behavior.

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

fhanik added 2 commits October 7, 2026 13:34
The confirmation flow now carries the resolved signup redirect (including
the client's signup_redirect_url fallback) into the reset code, so the owner
is returned to it after setting their password. Previously only an explicit,
registered redirect_uri survived; the signup_redirect_url fallback was lost
and the owner landed on "home".

completeActivation now passes the resolved redirect location (from
getRedirect, which honours signup_redirect_url) to forgotPassword instead of
the raw redirect_uri. The reset flow re-matches it against the client's
registered redirect URIs before applying it.

Restores the redirect assertions in the self-service tests for both the
explicit and fallback client-redirect cases.

test: cover confirmation password/redirect behavior end to end

Add coverage for the self-service confirmation changes:

- EmailAccountCreationServiceTests: assert completeActivation discards the
  registration-form password (replacing it with a different value), marks the
  account as requiring a password change, and issues an ownership-bound reset
  code.
- CreateAccountIT: end-to-end test confirming that the registration-form
  password cannot sign in after confirmation, and that only the password the
  owner sets through the link works.

Also completes the configuration-reference note to describe that a configured
client/signup redirect is applied after the owner sets their password.
completeActivation verified the account before replacing the registration
password and setting the change-required flag. Because these are separate
provisioning updates, a concurrent login in the gap could authenticate with
the registration password against the already-verified account.

Replace the password and set the flag while the account is still unverified,
then verify it last (version -1, matching the password-reset flow), so the
account is never simultaneously verified and holding the registration
password. Add an ordering assertion to the service test to lock this in.

Addresses PR review feedback.
@fhanik
fhanik force-pushed the pr/self-service-improvement branch from 838c001 to 6b46280 Compare October 7, 2026 21:00
@fhanik
fhanik marked this pull request as ready for review October 7, 2026 21:29
@strehle
strehle requested a balanced review from Copilot October 8, 2026 14:09
@fhanik
fhanik marked this pull request as draft October 8, 2026 14:11

Copilot AI 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.

🟡 Changes recommended

Reset-code generation can fail after verification, leaving an activated account with an unknown password and no retryable activation link.

1 open finding
4 resolved since last review

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Development

Successfully merging this pull request may close these issues.

2 participants