Skip to content

[ST-2744] registration: Multistep Registration - #3224

Open
glanzel wants to merge 2 commits into
mainfrom
gg_26_09_registration_multistep
Open

glanzel wants to merge 2 commits into
mainfrom
gg_26_09_registration_multistep

Conversation

@glanzel

@glanzel glanzel commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Tasks

  • PR name contains story or task reference
  • Documentation (docs and inline)
  • Tests (including n+1 and django_assert_num_queries where applicable)
  • Changelog

:.::..:::::: Aligned Asana Task ::.:.::.::::

@glanzel glanzel changed the title registration: Multistep Registration [ST-2744] registration: Multistep Registration Sep 28, 2026
@liqid-dev

liqid-dev Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

The preview deployment for adhocracy-plus is ready. 🟢

Open web | Open Build Logs | Open Application Logs

Last updated at: 2026-09-28 13:23:22 CET

@glanzel

glanzel commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

//review

@github-actions

Copy link
Copy Markdown

Review: #3224 — [ST-2744] Multistep Registration

Verified OK: only get/post are overridden, so allauth's dispatch-level protections still apply (rate_limit(action="signup"), login_not_required, CloseableSignupMixin, RedirectAuthenticatedUserMixin); next is host-checked at redirect time (get_next_redirect_url → adapter.is_safe_url) and in AccountAdapter.get_email_confirmation_url; no open redirect, no XSS (all new interpolations escaped), no migrations needed (no model changes), CHANGELOG.md updated in the right place, all comments/identifiers in English per AGENTS.md, legacy single-step POSTs still work (existing tests unaffected).

Blocking

  1. IG BCE signup can never get past step 2 — apps/users/forms.py:282-291. IgbceSignupForm.clean() runs on every is_valid(), including the step-1/step-2 forms where restrict_fields_to_step (forms.py:49-54) has deleted member_number/birth_date. At step 2 clean() reaches validateMemberNumberAndDate(None, None) → birth_date.strftime raises inside the bare except BaseException (forms.py:266) → every submission fails with "Something is wrong with the setup - please try again later". Step 1 now also fires the external NAV API call (again at final submit = 2 calls). The comment at forms.py:25-27 explicitly promises custom forms keep working — they don't. Fix: guard the cross-field check (if member_number and birth_date:) / skip business validation on restricted forms, and add a test driving a custom signup form through all 3 steps.

High

  1. Plaintext password staged in session — apps/users/views.py:141-149 (asserted by tests/users/test_signup.py:195). No SESSION_ENGINE is set → default DB backend, so password1/password2 are stored unencrypted in django_session and survive abandonment until SESSION_COOKIE_AGE (2 weeks); form_valid (views.py:121-124) only clears on success. Add a TTL (timestamp the wizard blob, drop it when stale in get/get_form_kwargs), or don't stage the password (collect it on the final step).

Medium

  1. Verified-email prefill regression — apps/users/views.py:91-101 pops account_verified_email on every render, so invited users (stash set in the participant-invite flow, tests/projects/test_participant_invite_detail_views.py:13) lose the email prefill on step 1, where allauth's form.fields["email"].initial = ... is perfectly safe. Only suppress when "email" not in form.fields.

  2. Rate-limit budget is now 3× tighter — allauth's signup: 20/m/ip (config/settings/base.py:316) counts POSTs (GETs are skipped), and a full wizard pass costs 3 POSTs instead of 1; every back/edit resubmit also consumes. A user mistyping their password a few times can hit 429 within a minute — and htmx does not swap a 429 body, so the wizard fails silently. Raise the limit (e.g. "signup": "60/m/ip") and consider an htmx:responseError handler or test for the 429 path.

  3. Two parallel field maps will drift — SIGNUP_STEP_FIELDS (apps/users/forms.py:28-40) vs SIGNUP_WIZARD_DATA_FIELDS (apps/users/views.py:27-35). Adding a step-1/2 field to the first but not the second makes that field render at step 1, never get staged, and never render at step 3 → final submit fails with an error the user cannot fix (loops back to step 1 forever). Derive the staging list: [n for n, s in SIGNUP_STEP_FIELDS.items() if s < SIGNUP_LAST_STEP].

Missing tests

  • next carried from ?next= through all 3 steps to the final redirect (only the legacy path is covered, tests/users/test_views.py:137).
  • Step-3 validation error routed back to the correct step (signup_error_step, views.py:126-128), e.g. taken email/username.
  • Non-JS fallback: step 1/2 POST without HX-Request must render the full page.
  • Custom (IG BCE) form through the wizard (see apps: Recreate all migrations #1); CAPTCHA=True render after the htmx swap (apps/captcha/assets/prosopo.js:50).

Low / nits

  • Unrelated scope: donation-banner removal + social-login redesign in a registration PR. The removal is also incomplete — adhocracy-plus/templates/footer.html:4 still adds footer--banner-margin (assets/scss/components/_footer.scss:21), reserving 3.5–5.5rem of space for a banner that no longer exists.
  • SignupWizardView.get() (apps/users/views.py:82) drops allauth's get_initial() support for ?email= prefill.
  • signup_media is only rendered by the full-page template (adhocracy-plus/templates/account/signup.html:7); a partial-only entry would leave step 3 without the captcha script — render media inside the partial or document the assumption.
  • test_signup_wizard_step2_back_button_reads_session (tests/users/test_signup.py:225) actually exercises the step-1 back button.

New%20session%20-%202026-09-28T12%3A35%3A08.651Z
opencode session  |  github run

@glanzel
glanzel force-pushed the gg_26_09_registration_multistep branch from 747051b to 2763d02 Compare September 28, 2026 13:20

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant