ソースを参照

RHL-044 docs(frontend): describe gate flow and profile UX behavior

Code_Uwe 1 ヶ月 前
コミット
61032b1c10
2 ファイル変更50 行追加12 行削除
  1. 18 2
      Docs/frontend-api-usage.md
  2. 32 10
      Docs/frontend-ui.md

+ 18 - 2
Docs/frontend-api-usage.md

@@ -10,6 +10,7 @@ Scope:
 - PDF streaming/opening behavior in the Explorer and in Search.
 - Search **date filters** (`from` / `to`) and **shareable URL sync** (RHL-025).
 - Password change for authenticated users (RHL-009).
+- Must-change-password enforcement flow (RHL-044).
 - Admin user management API usage (RHL-012).
 
 > UI developers: For the app shell layout and frontend route scaffold (public vs protected routes, placeholder pages), see **`docs/frontend-ui.md`**.
@@ -27,6 +28,7 @@ Notes:
 - The backend provides a binary PDF stream/download endpoint (RHL-015). The Explorer integrates it for “Open PDF” (RHL-023).
 - The Search UI integrates the same “Open PDF” pattern (RHL-024).
 - The Search UI supports **optional date filters** (`from` / `to`) with a date range picker and presets (RHL-025).
+- The protected UI enforces `mustChangePassword` via centralized auth gating (RHL-044).
 - User management endpoints are implemented under `/api/admin/users` and are guarded by the user-management capability (RHL-041 + RHL-012).
 
 ---
@@ -308,7 +310,8 @@ Success (authenticated):
 		"userId": "...",
 		"role": "branch|admin|superadmin|dev",
 		"branchId": "NL01",
-		"email": "nl01@example.com"
+		"email": "nl01@example.com",
+		"mustChangePassword": true
 	}
 }
 ```
@@ -316,6 +319,7 @@ Success (authenticated):
 Notes:
 
 - `email` is optional and may be `null`.
+- `mustChangePassword` is always returned as a boolean.
 
 Success (unauthenticated):
 
@@ -348,6 +352,7 @@ Weak password details:
 
 - The backend returns structured `details` for `VALIDATION_WEAK_PASSWORD`, including `minLength`, `requireLetter`, `requireNumber`, `disallowSameAsCurrent`, and `reasons`.
 - Frontend UIs should use these fields to show actionable user feedback.
+- On success, the backend refreshes the session cookie with `mustChangePassword=false`.
 
 Example UI usage:
 
@@ -361,7 +366,8 @@ export async function changePasswordExample() {
 			newPassword: "NewPassw0rd",
 		});
 
-		// Success UI: show a toast and clear form state
+		// Success UI: show a toast, clear form state, and revalidate session
+		// (e.g., call getMe() or trigger the central auth retry).
 		return { ok: true };
 	} catch (err) {
 		if (err instanceof ApiClientError) {
@@ -389,6 +395,16 @@ Security note:
 - Never log passwords.
 - Avoid storing passwords in persistent client state.
 
+#### `mustChangePassword` frontend flow (RHL-044)
+
+Recommended behavior:
+
+1. Read `user.mustChangePassword` from `getMe()`.
+2. If `true`, gate protected routes and redirect to `/profile`.
+3. Allow password change only on `/profile`.
+4. After successful `changePassword(...)`, revalidate auth state (`getMe`) so the gate lifts immediately.
+5. Optional UX: preserve and resume a previous target route (`next`) after the gate is lifted.
+
 ### 4.2 Branch navigation
 
 All endpoints below require a valid session.

+ 32 - 10
Docs/frontend-ui.md

@@ -1,4 +1,4 @@
-# Frontend UI: App Shell, Routing, Auth/RBAC, Explorer, Search, Date Range Filter, Navigation Polish, Profile Password Change, User Management, and Role Refinement (RHL-019 / RHL-020 / RHL-021 / RHL-022 / RHL-023 / RHL-024 / RHL-025 / RHL-037 / RHL-032 / RHL-009 / RHL-012 / RHL-041)
+# Frontend UI: App Shell, Routing, Auth/RBAC, Explorer, Search, Date Range Filter, Navigation Polish, Profile Password Change, User Management, Role Refinement, and Must-Change-Password Gate (RHL-019 / RHL-020 / RHL-021 / RHL-022 / RHL-023 / RHL-024 / RHL-025 / RHL-037 / RHL-032 / RHL-009 / RHL-012 / RHL-041 / RHL-044)
 
 This document describes the **frontend routing scaffold**, the **application shell layout**, and the core UI modules for the RHL Lieferscheine app.
 
@@ -16,6 +16,7 @@ Timeline:
 - **RHL-009**: Profile password change UI (Change Password form + toasts).
 - **RHL-041**: Role refinement (`superadmin`) + capability separation (branch access vs user management).
 - **RHL-012**: User management UI (list/create/update/delete users) + guarded admin endpoints.
+- **RHL-044**: Must-change-password enforcement (central protected-route gate, `/profile` redirect, and post-change resume flow).
 
 > **Language policy**
 >
@@ -27,7 +28,7 @@ Timeline:
 
 ## 1. Scope
 
-### 1.1 Implemented (as of RHL-012 + RHL-041 + RHL-037 + RHL-025 + RHL-032 + RHL-009)
+### 1.1 Implemented (as of RHL-012 + RHL-041 + RHL-037 + RHL-025 + RHL-032 + RHL-009 + RHL-044)
 
 - **Public** `/login` route with a functional login form (shadcn/ui primitives).
 
@@ -57,6 +58,20 @@ Timeline:
   - Uses inline validation for actionable form errors.
   - Uses toast notifications (Sonner) for success/error feedback.
 
+- **Must-change-password enforcement (RHL-044)**:
+  - Session identity (`GET /api/auth/me`) includes `mustChangePassword`.
+  - `AuthGate` centrally enforces:
+    - if `mustChangePassword=true`, redirect any protected non-profile route to `/profile`.
+    - keep users on `/profile` until password is changed.
+  - Redirect state is preserved with:
+    - `/profile?mustChangePasswordGate=1&next=<safe-internal-url>`
+  - After successful password change:
+    - backend clears `mustChangePassword` and refreshes the session cookie.
+    - frontend revalidates auth state and automatically resumes `next` when available.
+  - `/profile` shows:
+    - required-action alert (red) while `mustChangePassword=true`
+    - success alert (green) after successful password change once the gate is lifted.
+
 - **Global toast notifications (Sonner)**:
   - Sonner Toaster is mounted once in the root layout.
   - UI code uses a small wrapper (`lib/frontend/ui/toast.js`) to keep copy and behavior consistent.
@@ -164,9 +179,6 @@ Timeline:
   - branch user creation UX (fixed `NL` prefix + numeric-only input)
   - optional branch existence check / warning when creating branch users
 
-- `mustChangePassword` enforcement (separate follow-up ticket):
-  - gate/redirect until password change is completed
-
 ---
 
 ## 2. Route Groups & URL Structure
@@ -183,7 +195,8 @@ The app uses Next.js App Router **Route Groups** to separate public and protecte
   - Routes that render inside the AppShell.
   - Protected routes are guarded by:
     1. Session check (AuthProvider)
-    2. UI RBAC check for branch routes (BranchGuard)
+    2. Must-change-password gate (`AuthGate`, RHL-044)
+    3. UI RBAC check for branch routes (BranchGuard)
 
 ### 2.2 URL map
 
@@ -417,7 +430,7 @@ Behavior:
 Notes:
 
 - `GET /api/auth/me` returns minimal identity data for the UI:
-  - `userId`, `role`, `branchId`, and (optionally) `email`.
+  - `userId`, `role`, `branchId`, `email`, `mustChangePassword`.
 
 - Role is one of: `branch | admin | superadmin | dev`.
 
@@ -430,6 +443,11 @@ Behavior:
 - While session is loading: render a minimal in-shell state.
 - On auth errors: show an in-shell error card + retry.
 - On unauthenticated: show a short “redirecting” message while redirect happens.
+- On authenticated + `mustChangePassword=true`:
+  - redirect to `/profile` unless already on `/profile`.
+  - preserve a safe resume target via `next`.
+- On authenticated + `mustChangePassword=false` and gate marker present:
+  - resume to `next` automatically (if present and safe).
 
 RHL-032 note:
 
@@ -689,10 +707,13 @@ The UI uses the API client wrappers from `lib/frontend/apiClient.js`:
 
 - `components/profile/ProfilePage.jsx`
   - Shows read-only account/session info (role, branch, email).
+  - Shows required-action alert when `mustChangePassword=true`.
+  - Shows success alert after a successful forced password change.
 
 - `components/profile/ChangePasswordCard.jsx`
   - Calls `apiClient.changePassword({ currentPassword, newPassword })`.
   - Maps password policy errors into user-friendly German hints.
+  - Triggers auth revalidation after success (`retry()` from AuthContext).
 
 ---
 
@@ -755,6 +776,10 @@ npm run build
   - branch user → forbidden on foreign branches
   - admin-like → notfound on non-existent branches
   - admin/branch → forbidden on `/admin/users`
+- Validate must-change-password behavior:
+  - new user with `mustChangePassword=true` is forced to `/profile`
+  - gate lifts immediately after password change (no logout/login required)
+  - user resumes the previously blocked protected route when `next` is available
 
 ---
 
@@ -764,6 +789,3 @@ npm run build
   - delete with explicit username confirmation
   - branch user creation UX (fixed `NL` prefix + numeric-only input)
   - optional branch existence check / warning when creating branch users
-
-- `mustChangePassword` enforcement (separate ticket):
-  - redirect/gate until password is changed