feat(auth): forced name-onboarding gate for new Bunyip-JIT users #106
No reviewers
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
psa-systems/mokosh-apps!106
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/force-name-onboarding"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
mokosh-server JIT-creates Bunyip OIDC users with a synthetic name derived from the email local-part (the at+jwt carries no name claims). Until now the user landed on the dashboard with "User abc12" in the topbar and had to discover the Profile page on their own to fix it. New users are now redirected to a forced onboarding screen until they confirm first + last name; everything else (timezone, preferences, etc.) stays optional and lives on the existing Profile page.
The server side ships
profile_completed: boolonGET /api/v1/auth/me(paired patch: mokosh-serverfeat/force-profile-setup-on-jit). This SPA reads the flag, refuses to render anything else, and only releases the gate when the next/mereportstrue.Mechanics:
CurrentUsergainsprofile_completed: boolwith#[serde(default = "default_true")]so legacy server responses (no field) don't surprise-trap existing users.refresh_user_from_me(the background/mereconciler called after every token refresh and on memberships load) pulls the new field and writes it back to the in-memoryCurrentUser. Auth callback + sessionStorage rehydration default totrue; the first/mereconciles within one tick./onboarding/profileunder theAuthGuardlayout but EXEMPTED from its own redirect via a pathname check (web_sys::window().location().pathname() so we read it synchronously inside render, no signal re-entry).AuthGuardrender-time redirect: if the user is authenticated ANDprofile_completed = falseAND the current pathname is not/onboarding/profile,nav.replace(Route::Onboarding {}). No frame of authenticated UI ever paints for an un-onboarded user.pages/onboarding.rsis a single-purpose page: full-screen centred card, two required Inputs (first/last name), Continue button. Submit callsPUT /api/v1/auth/mevia the existingput_authed_typedhelper, reflects the response back into the in-memoryCurrentUser, thennav.replace(Dashboard). Defence-in-depth: if a completed user hits the URL directly, ause_effectbounces them to the dashboard.Server's
update_userhandler appendsprofile_completed_at = COALESCE(profile_completed_at, NOW())whenever the PUT body has non-empty first + last name (matching server patch), so the SAMEPUT /mecall the existing Profile page uses also marks onboarding complete - no separate endpoint.Backwards compat for the existing user base is handled by the server-side migration backfilling all current rows to
profile_completed_at = NOW(); the SPA just inherits the resultingtrue.mokosh-server JIT-creates Bunyip OIDC users with a synthetic name derived from the email local-part (the at+jwt carries no name claims). Until now the user landed on the dashboard with "User abc12" in the topbar and had to discover the Profile page on their own to fix it. New users are now redirected to a forced onboarding screen until they confirm first + last name; everything else (timezone, preferences, etc.) stays optional and lives on the existing Profile page. The server side ships `profile_completed: bool` on `GET /api/v1/auth/me` (paired patch: mokosh-server `feat/force-profile-setup-on-jit`). This SPA reads the flag, refuses to render anything else, and only releases the gate when the next `/me` reports `true`. Mechanics: - `CurrentUser` gains `profile_completed: bool` with `#[serde(default = "default_true")]` so legacy server responses (no field) don't surprise-trap existing users. - `refresh_user_from_me` (the background `/me` reconciler called after every token refresh and on memberships load) pulls the new field and writes it back to the in-memory `CurrentUser`. Auth callback + sessionStorage rehydration default to `true`; the first `/me` reconciles within one tick. - New route `/onboarding/profile` under the `AuthGuard` layout but EXEMPTED from its own redirect via a pathname check (web_sys::window().location().pathname() so we read it synchronously inside render, no signal re-entry). - `AuthGuard` render-time redirect: if the user is authenticated AND `profile_completed = false` AND the current pathname is not `/onboarding/profile`, `nav.replace(Route::Onboarding {})`. No frame of authenticated UI ever paints for an un-onboarded user. - `pages/onboarding.rs` is a single-purpose page: full-screen centred card, two required Inputs (first/last name), Continue button. Submit calls `PUT /api/v1/auth/me` via the existing `put_authed_typed` helper, reflects the response back into the in-memory `CurrentUser`, then `nav.replace(Dashboard)`. Defence-in-depth: if a completed user hits the URL directly, a `use_effect` bounces them to the dashboard. Server's `update_user` handler appends `profile_completed_at = COALESCE(profile_completed_at, NOW())` whenever the PUT body has non-empty first + last name (matching server patch), so the SAME `PUT /me` call the existing Profile page uses also marks onboarding complete - no separate endpoint. Backwards compat for the existing user base is handled by the server-side migration backfilling all current rows to `profile_completed_at = NOW()`; the SPA just inherits the resulting `true`.d33627f333e45216aaad