# Feature: Account Deletion (Mark for Removal) ## Overview **Goal:** Allow users to mark their account for deletion from the Profile page. **User Story:** As a user, I want to delete my account from the Profile page. When I click "Delete My Account", I want a confirmation dialog that warns me about data loss. After confirming by entering my email, my account will be marked for deletion, I will be signed out, and I will not be able to log in again. --- ## Data Model Changes ### Backend Model (`backend/models/user.py`) Add the following fields to the `User` class: ```python marked_for_deletion: bool = False marked_for_deletion_at: datetime | None = None ``` - Update `to_dict()` and `from_dict()` methods to serialize these fields. - Import `datetime` from Python standard library if not already imported. ### Frontend Model (`frontend/vue-app/src/common/models.ts`) Add matching fields to the `User` interface: ```typescript marked_for_deletion: boolean; marked_for_deletion_at: string | null; ``` --- ## Backend Implementation ### New Error Codes (`backend/api/error_codes.py`) Add the following error code: ```python ACCOUNT_MARKED_FOR_DELETION = 'ACCOUNT_MARKED_FOR_DELETION' ALREADY_MARKED = 'ALREADY_MARKED' ``` ### New API Endpoint (`backend/api/user_api.py`) **Endpoint:** `POST /api/user/mark-for-deletion` **Authentication:** Requires valid JWT (authenticated user). **Request:** ```json {} ``` (Empty body; user is identified from JWT token) **Response:** - **Success (200):** ```json { "success": true } ``` - **Error (400/401/403):** ```json { "error": "Error message", "code": "INVALID_USER" | "ALREADY_MARKED" } ``` **Logic:** 1. Extract current user from JWT token. 2. Validate user exists in database. 3. Check if already marked for deletion: - If `marked_for_deletion == True`, return error with code `ALREADY_MARKED` (or make idempotent and return success). 4. Set `marked_for_deletion = True` and `marked_for_deletion_at = datetime.now(timezone.utc)`. 5. Save user to database using `users_db.update()`. 6. Trigger SSE event: `send_event_for_current_user('user_marked_for_deletion', { 'user_id': user.id })`. 7. Return success response. ### Login Blocking (`backend/api/auth_api.py`) In the `/api/login` endpoint, after validating credentials: 1. Check if `user.marked_for_deletion == True`. 2. If yes, return: ```json { "error": "This account has been marked for deletion and cannot be accessed.", "code": "ACCOUNT_MARKED_FOR_DELETION" } ``` with HTTP status `403`. ### Password Reset Blocking (`backend/api/user_api.py`) In the `/api/user/request-reset` endpoint: 1. After finding the user by email, check if `user.marked_for_deletion == True`. 2. If yes, **silently ignore the request**: - Do not send an email. - Return success response (to avoid leaking account status). ### SSE Event (`backend/events/types/event_types.py`) Add new event type: ```python USER_MARKED_FOR_DELETION = 'user_marked_for_deletion' ``` --- ## Frontend Implementation ### Files Affected - `frontend/vue-app/src/components/parent/UserProfile.vue` - `frontend/vue-app/src/common/models.ts` - `frontend/vue-app/src/common/errorCodes.ts` ### Error Codes (`frontend/vue-app/src/common/errorCodes.ts`) Add: ```typescript export const ACCOUNT_MARKED_FOR_DELETION = "ACCOUNT_MARKED_FOR_DELETION"; export const ALREADY_MARKED = "ALREADY_MARKED"; ``` ### UI Components (`UserProfile.vue`) #### 1. Delete Account Button - **Label:** "Delete My Account" - **Style:** `.btn-danger-link` (use `--danger` color from `colors.css`) - **Placement:** Below "Change Password" link, with `24px` margin-top - **Behavior:** Opens warning modal on click #### 2. Warning Modal (uses `ModalDialog.vue`) - **Title:** "Delete Your Account?" - **Body:** "This will permanently delete your account and all associated data. This action cannot be undone." - **Email Confirmation Input:** - Require user to type their email address to confirm. - Display message: "Type your email address to confirm:" - Input field with `v-model` bound to `confirmEmail` ref. - **Buttons:** - **"Cancel"** (`.btn-secondary`) — closes modal - **"Delete My Account"** (`.btn-danger`) — disabled until `confirmEmail` matches user email, triggers API call #### 3. Loading State - Disable "Delete My Account" button during API call. - Show loading spinner or "Deleting..." text. #### 4. Success Modal - **Title:** "Account Deleted" - **Body:** "Your account has been marked for deletion. You will now be signed out." - **Button:** "OK" (closes modal, triggers `logoutUser()` and redirects to `/auth/login`) #### 5. Error Modal - **Title:** "Error" - **Body:** Display error message from API using `parseErrorResponse(res).msg`. - **Button:** "Close" ### Frontend Logic 1. User clicks "Delete My Account" button. 2. Warning modal opens with email confirmation input. 3. User types email and clicks "Delete My Account". 4. Frontend calls `POST /api/user/mark-for-deletion`. 5. On success: - Close warning modal. - Show success modal. - On "OK" click: call `logoutUser()` from `stores/auth.ts`, redirect to `/auth/login`. 6. On error: - Close warning modal. - Show error modal with message from API. --- ## Testing ### Backend Tests (`backend/tests/test_user_api.py`) - [x] Test marking a valid user account (200, `marked_for_deletion = True`, `marked_for_deletion_at` is set). - [x] Test marking an already-marked account (return error with `ALREADY_MARKED` or be idempotent). - [x] Test marking with invalid JWT (401). - [x] Test marking with missing JWT (401). - [x] Test login attempt by marked user (403, `ACCOUNT_MARKED_FOR_DELETION`). - [x] Test password reset request by marked user (silently ignored, returns 200 but no email sent). - [x] Test SSE event is triggered after marking. ### Frontend Tests (`frontend/vue-app/src/components/__tests__/UserProfile.spec.ts`) - [x] Test "Delete My Account" button renders. - [x] Test warning modal opens on button click. - [x] Test "Delete My Account" button in modal is disabled until email matches. - [x] Test API call is made when user confirms with correct email. - [x] Test success modal shows after successful API response. - [x] Test error modal shows on API failure (with error message). - [x] Test user is signed out after success (calls `logoutUser()`). - [x] Test redirect to login page after sign-out. - [x] Test button is disabled during loading. --- ## Future Considerations - A background scheduler will be implemented to physically delete marked accounts after a grace period (e.g., 30 days). - Admin panel to view and manage marked accounts. - Email notification to user when account is marked for deletion (with grace period details). --- ## Acceptance Criteria (Definition of Done) ### Data Model - [x] Add `marked_for_deletion` and `marked_for_deletion_at` fields to `User` model (backend). - [x] Add matching fields to `User` interface (frontend). - [x] Update `to_dict()` and `from_dict()` methods in `User` model. ### Backend - [x] Create `POST /api/user/mark-for-deletion` endpoint. - [x] Add `ACCOUNT_MARKED_FOR_DELETION` and `ALREADY_MARKED` error codes. - [x] Block login for marked users in `/api/login`. - [x] Block password reset for marked users in `/api/user/request-reset`. - [x] Trigger `user_marked_for_deletion` SSE event after marking. - [x] All backend tests pass. ### Frontend - [x] Add "Delete My Account" button to `UserProfile.vue` below "Change Password". - [x] Implement warning modal with email confirmation. - [x] Implement success modal. - [x] Implement error modal. - [x] Implement loading state during API call. - [x] Sign out user after successful account marking. - [x] Redirect to login page after sign-out. - [x] Add `ACCOUNT_MARKED_FOR_DELETION` and `ALREADY_MARKED` to `errorCodes.ts`. - [x] All frontend tests pass.