fix(web): route verification-resend feedback back to the originating page #321

Merged
nrupard merged 2 commits from fix/BUNYIP-324-resend-verification-feedback into main 2026-07-02 16:05:19 +02:00
Owner

Problem

The "Resend verification email" control lives on both /settings and /onboarding, and both forms POST to /settings/verify-email/resend. The handler (settings_resend_verification) always redirected its ?ok= / ?error= feedback to /settings. But an onboarding-incomplete user is bounced off /settings right back to /onboarding by the onboarding gate (handlers::mod::guard -> needs_onboarding), and that bounce drops the query param. So a user who clicked Resend from the onboarding page saw nothing: no "sent" confirmation, no throttle notice, no failure state. This is exactly the "user cannot tell if it was queued, attempted, or failed" symptom in BUNYIP-324. (From /settings itself the feedback already worked.)

Fix

  • settings_resend_verification now chooses the redirect target from the user's onboarding state: needs_onboarding == true -> /onboarding, else /settings. The two pages are mutually exclusive by onboarding state, so this reconstructs the origin server-side with no client-supplied redirect target (no open-redirect surface). Both success and the BUNYIP-314 throttle/failure copy now land on whichever page the user was on.
  • The onboarding page previously rendered only ?error. It now also renders ?ok via a new success_box helper in views/ui.rs (the teal-check counterpart to error_box, matching the inline success banner /settings already uses), so the queued/accepted confirmation is visible.

No new internals exposed: the success copy is the same "Verification email sent to

" the settings page already shows, and the failure copy is the existing verification_message() (throttle-aware, generic otherwise).

Tests

onboarding_content render tests (bin unit tests, cargo test -p bunyip-web --bins):

  • renders_queued_success_indication - success/queued banner shows when ?ok is set.
  • renders_send_failure_indication - failure banner shows when ?error is set.
  • no_feedback_boxes_when_both_none - clean page load stays clean.

Verification

just check-container green (fmt + clippy -D warnings + workspace lib tests). The web crate is bin-only, so its tests run under --bins, not --lib: cargo test -p bunyip-web --bins -> 68 passed, 0 failed (incl. the 3 new).

Fixes BUNYIP-324.

## Problem The "Resend verification email" control lives on **both** `/settings` and `/onboarding`, and both forms POST to `/settings/verify-email/resend`. The handler (`settings_resend_verification`) always redirected its `?ok=` / `?error=` feedback to `/settings`. But an onboarding-incomplete user is bounced off `/settings` right back to `/onboarding` by the onboarding gate (`handlers::mod::guard` -> `needs_onboarding`), and that bounce **drops the query param**. So a user who clicked Resend from the onboarding page saw nothing: no "sent" confirmation, no throttle notice, no failure state. This is exactly the "user cannot tell if it was queued, attempted, or failed" symptom in BUNYIP-324. (From `/settings` itself the feedback already worked.) ## Fix - `settings_resend_verification` now chooses the redirect target from the user's onboarding state: `needs_onboarding == true` -> `/onboarding`, else `/settings`. The two pages are mutually exclusive by onboarding state, so this reconstructs the origin **server-side** with no client-supplied redirect target (no open-redirect surface). Both success and the BUNYIP-314 throttle/failure copy now land on whichever page the user was on. - The onboarding page previously rendered only `?error`. It now also renders `?ok` via a new `success_box` helper in `views/ui.rs` (the teal-check counterpart to `error_box`, matching the inline success banner `/settings` already uses), so the queued/accepted confirmation is visible. No new internals exposed: the success copy is the same "Verification email sent to <address>" the settings page already shows, and the failure copy is the existing `verification_message()` (throttle-aware, generic otherwise). ## Tests `onboarding_content` render tests (bin unit tests, `cargo test -p bunyip-web --bins`): - `renders_queued_success_indication` - success/queued banner shows when `?ok` is set. - `renders_send_failure_indication` - failure banner shows when `?error` is set. - `no_feedback_boxes_when_both_none` - clean page load stays clean. ## Verification `just check-container` green (fmt + clippy `-D warnings` + workspace lib tests). The web crate is bin-only, so its tests run under `--bins`, not `--lib`: `cargo test -p bunyip-web --bins` -> 68 passed, 0 failed (incl. the 3 new). Fixes BUNYIP-324.
fix(web): route verification-resend feedback back to the page it came from
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 34s
Check / fmt + clippy + build + tests (pull_request) Successful in 15m51s
3cbb3fb7f8
The "Resend verification email" control appears on both /settings and /onboarding, and both POST to /settings/verify-email/resend. The handler always redirected the ok/error feedback to /settings. For an onboarding-incomplete user that redirect is bounced straight back to /onboarding by the onboarding gate (handlers::mod::guard), which drops the ?ok / ?error query param, so the user saw no confirmation, no throttle notice, and no failure state after clicking Resend from onboarding.

The handler now picks the redirect target from the user's onboarding state (needs_onboarding): onboarding-incomplete users get the feedback on /onboarding, everyone else on /settings. The two pages are mutually exclusive by onboarding state, so this reconstructs the origin server-side without trusting any client-supplied redirect target (no open-redirect surface). The onboarding page previously rendered only ?error; it now also renders ?ok via a new success_box helper (the teal-check counterpart to error_box, matching the settings page's inline success banner), so the queued/accepted confirmation actually shows.

Adds onboarding_content render tests for the queued-success and send-failure indications, plus the clean no-feedback case.

#BUNYIP-324
refactor(web): apply clamp_msg inside the shared banner helpers
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 38s
Check / fmt + clippy + build + tests (pull_request) Successful in 16m9s
Create release / Create release from merged PR (pull_request) Has been skipped
485a1ea3a2
The 256-byte clamp that bounds a hand-crafted ?ok= / ?error= link (page-bloat guard, not XSS) lived as a private clamp_msg in dashboard.rs and had to be remembered at each call site. The onboarding page rendered its ?ok / ?error banners without it, so BUNYIP-324's new onboarding feedback path was unbounded.

Move clamp_msg into views/ui.rs and apply it inside error_box and success_box, so every banner caller across the app (settings, membership, onboarding, auth pages, admin) is bounded by construction. The settings and membership pages drop their inline ok-box divs and manual clamp_msg wrapping in favour of success_box(ok) / error_box(e), removing the duplicated markup.

Adds ui.rs unit tests: clamp passes short input through, bounds oversized input to 256 bytes, steps back to a char boundary on a multi-byte straddle without panicking, and both banner helpers clamp their rendered message.

#BUNYIP-324
nrupard scheduled this pull request to auto merge when all checks succeed 2026-07-02 15:56:21 +02:00
nrupard deleted branch fix/BUNYIP-324-resend-verification-feedback 2026-07-02 16:05:19 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!321
No description provided.