# Console Security Settings Page Specification ## Purpose A dedicated settings page at `/app/platform/security` for managing 2FA enrollment, disabling, and recovery code regeneration for platform console users. ## Requirements ### Requirement: Security Settings Route The application MUST register the route `/app/platform/security` accessible to authenticated users with a `platform_*` role. The route MUST be reachable from the sidebar navigation or the user menu. The page MUST display the user's current 2FA state: enabled or disabled. #### Scenario: Authenticated platform user visits security page - GIVEN an authenticated user with a `platform_*` role - WHEN they navigate to `/app/platform/security` - THEN the page renders without error - AND the current 2FA state (enabled/disabled) is displayed #### Scenario: Unauthenticated user redirected - GIVEN no active session - WHEN `/app/platform/security` is accessed directly - THEN the user is redirected to `/login` --- ### Requirement: TOTP Enable Flow (Enrollment UI) When 2FA is disabled, the page MUST offer an "Enable 2FA" action that initiates the enrollment flow: 1. Call `setup-start` → display QR code image (PNG rendered from `qr_code_png_base64`) AND the `secret_base32` in a copyable text field. 2. Present a 6-digit input for the first confirmation code. 3. On submit, call `setup-verify`. On success, display the 8 recovery codes. 4. The recovery codes screen MUST require an explicit "I have saved my recovery codes" confirmation before the user can dismiss it. 5. After confirmation, return to the security settings page (2FA now shown as enabled). #### Scenario: QR and manual secret displayed after setup-start - GIVEN the user clicks "Enable 2FA" - WHEN `setup-start` responds with `{ secret_base32, qr_code_png_base64, otpauth_uri }` - THEN an `` rendered from the base64 PNG is shown - AND the `secret_base32` is shown in a copyable field #### Scenario: Valid 6-digit code confirms enrollment - GIVEN the QR step is displayed and the user enters the correct code - WHEN the code is submitted - THEN `setup-verify` is called - AND on success the recovery codes screen is shown with 8 codes #### Scenario: Recovery codes require save confirmation - GIVEN the recovery codes screen is displayed - WHEN the user has NOT checked the confirmation checkbox - THEN the dismiss/continue button is disabled #### Scenario: After confirmation, page shows 2FA enabled - GIVEN the user confirmed they saved the recovery codes - WHEN they dismiss the screen - THEN the security page shows 2FA state as "enabled" --- ### Requirement: TOTP Disable Flow When 2FA is enabled, the page MUST offer a "Disable 2FA" action that renders a form requiring both the current password and a current valid TOTP code. On successful submission, the page returns to the security settings view with 2FA shown as disabled. #### Scenario: Correct password + TOTP disables 2FA - GIVEN the user enters their correct password and a valid TOTP code - WHEN the disable form is submitted - THEN `POST /api/auth/2fa/disable` is called - AND on HTTP 200 the page shows 2FA state as "disabled" #### Scenario: Incorrect inputs show error - GIVEN the user submits wrong credentials on the disable form - WHEN the server returns HTTP 401 - THEN an inline error is shown - AND 2FA state remains "enabled" --- ### Requirement: Recovery Code Regeneration Flow When 2FA is enabled, the page MUST offer a "Regenerate recovery codes" action. The action renders a form requiring a current valid TOTP code. On success, the new 8 codes are displayed once with the same save-confirmation gate as during enrollment. #### Scenario: Valid TOTP → new codes displayed once - GIVEN the user enters a valid TOTP code in the regenerate form - WHEN `POST /api/auth/2fa/regenerate-recovery-codes` returns 200 - THEN the page displays the 8 new recovery codes with save-confirmation gate #### Scenario: Invalid TOTP → error shown, codes unchanged - GIVEN the user enters an invalid TOTP code - WHEN the server returns HTTP 401 - THEN an inline error is shown and no new codes are displayed