fix(auth): preserve deep-link route across cold-load re-auth (MAPPS-323) #359

Merged
nrupard merged 2 commits from fix/MAPPS-323-deeplink-return-to into main 2026-06-25 23:54:45 +02:00
Owner

What

A cold load or bookmark of an authed deep link (e.g. /tickets/new) while the user holds a valid OP session bounced to /dashboard instead of the requested page. Fixes MAPPS-323.

Two gaps:

  1. The AuthGuard kickoff fired start_login(&cfg, "") with an EMPTY return_to (src/lib.rs:59), dropping the path the user actually asked for.
  2. /auth/callback (src/pages/auth_callback.rs) collapsed every same-origin return_to to Route::Dashboard (the inline comment admitted it: "the Dioxus router only knows Route variants, so an internal path beyond /dashboard collapses to Dashboard for the moment").

How

  • New oidc::current_return_to(): the current pathname plus the preserved original query. Query comes from the INITIAL_SEARCH snapshot (taken in main()), because the Dioxus router strips the live location.search during init. Returns "" for the auth-plumbing routes (/login, /auth/callback, root) and off-wasm.
  • AuthGuard now passes current_return_to() to start_login, so the requested deep link survives the authorize round-trip.
  • /auth/callback restores a concrete same-origin return_to via location.set_href. The token bundle is already saved to sessionStorage just above (lines 78-86), so rehydrate_from_storage re-authes the rebooted tree on the next boot - no re-login loop. Empty / "/" still router-push to Dashboard (no reload), and a protocol-relative //host is rejected (off-origin guard). A leading single / constrains the target to a same-origin path, so no scheme injection is possible.

Test

  • just pre-commit green: cargo fmt --all --check, cargo clippy --all-targets -- -D warnings, cargo check --target wasm32-unknown-unknown, cargo test --lib (184 passed).
  • Manual / e2e: the PMS-519 form-validation spec was rewritten to in-app nav specifically to dodge this bug; once this lands, that spec could optionally revert to a hard goto('/tickets/new') as a regression guard.

Discovered while verifying PMS-519 (mokosh-server e2e).

## What A cold load or bookmark of an authed deep link (e.g. `/tickets/new`) while the user holds a valid OP session bounced to `/dashboard` instead of the requested page. Fixes MAPPS-323. Two gaps: 1. The AuthGuard kickoff fired `start_login(&cfg, "")` with an EMPTY `return_to` (`src/lib.rs:59`), dropping the path the user actually asked for. 2. `/auth/callback` (`src/pages/auth_callback.rs`) collapsed every same-origin `return_to` to `Route::Dashboard` (the inline comment admitted it: "the Dioxus router only knows `Route` variants, so an internal path beyond `/dashboard` collapses to `Dashboard` for the moment"). ## How - New `oidc::current_return_to()`: the current `pathname` plus the preserved original query. Query comes from the `INITIAL_SEARCH` snapshot (taken in `main()`), because the Dioxus router strips the live `location.search` during init. Returns `""` for the auth-plumbing routes (`/login`, `/auth/callback`, root) and off-wasm. - AuthGuard now passes `current_return_to()` to `start_login`, so the requested deep link survives the authorize round-trip. - `/auth/callback` restores a concrete same-origin `return_to` via `location.set_href`. The token bundle is already saved to `sessionStorage` just above (lines 78-86), so `rehydrate_from_storage` re-authes the rebooted tree on the next boot - no re-login loop. Empty / `"/"` still router-push to `Dashboard` (no reload), and a protocol-relative `//host` is rejected (off-origin guard). A leading single `/` constrains the target to a same-origin path, so no scheme injection is possible. ## Test - `just pre-commit` green: `cargo fmt --all --check`, `cargo clippy --all-targets -- -D warnings`, `cargo check --target wasm32-unknown-unknown`, `cargo test --lib` (184 passed). - Manual / e2e: the PMS-519 form-validation spec was rewritten to in-app nav specifically to dodge this bug; once this lands, that spec could optionally revert to a hard `goto('/tickets/new')` as a regression guard. Discovered while verifying PMS-519 (mokosh-server e2e).
fix(auth): preserve deep-link route across cold-load re-auth (MAPPS-323)
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 1m17s
221191e77e
A cold load or bookmark of an authed deep link (e.g. /tickets/new) while the user holds a valid OP session bounced to /dashboard instead of the requested page. Two gaps: the AuthGuard kickoff fired start_login with an empty return_to (src/lib.rs), dropping the current path, and /auth/callback collapsed every same-origin path to Route::Dashboard.

- Add oidc::current_return_to(): the current pathname plus the preserved original query (read from the INITIAL_SEARCH snapshot, since the Dioxus router strips the live location.search during init). Returns "" for the auth-plumbing routes (/login, /auth/callback, root) and off-wasm.
- AuthGuard now passes current_return_to() to start_login so the requested deep link survives the authorize round-trip.
- /auth/callback restores a concrete same-origin return_to via location.set_href; the token bundle saved to sessionStorage just above lets rehydrate_from_storage re-auth the rebooted tree, so there is no re-login loop. Empty / "/" still router-push to Dashboard (no reload), and a protocol-relative "//host" is rejected.

#MAPPS-323
fix(auth): keep /dashboard soft on re-auth + unit-test return_to (MAPPS-323)
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 1m17s
Create release / Create release from merged PR (pull_request) Has been skipped
20dc5ab921
Self-review of the prior commit caught a regression: the interactive /login flow passes start_login(cfg, "/dashboard"), and the callback's new same-origin branch hard-navigated it via location.set_href - a redundant full-page reload (the callback is already a fresh boot) where the old code did a soft navigator.push(Dashboard).

- Extract the wasm-free logic into pure, unit-tested helpers: sanitize_return_to(path, search) (the capture-side filter) and classify_return_to(return_to) -> ReturnTarget { Dashboard, Restore } (the callback-side decision). is_auth_plumbing is shared.
- classify_return_to keeps "/dashboard" (plus "", "/", off-origin, protocol-relative, scheme, and the auth-plumbing routes) on the soft Dashboard push; only a concrete same-origin deep link is Restore (hard nav). /auth/callback now matches on it.
- Add 5 unit tests: concrete deep links restore; /dashboard + empty + "/" stay soft (regression guard); //host, cross-origin, javascript:, and /login + /auth/callback all fall back to Dashboard.

#MAPPS-323
nrupard scheduled this pull request to auto merge when all checks succeed 2026-06-25 23:29:45 +02:00
nrupard deleted branch fix/MAPPS-323-deeplink-return-to 2026-06-25 23:54:45 +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/mokosh-apps!359
No description provided.