Merge pull request #145 from trails-cool/chore/archive-account-settings
Archive account-settings change
This commit is contained in:
commit
56cd6b1ccc
9 changed files with 161 additions and 179 deletions
|
|
@ -1,63 +0,0 @@
|
|||
## 1. Settings Route & Layout
|
||||
|
||||
- [ ] 1.1 Create `apps/journal/app/routes/settings.tsx` with loader auth guard (redirect to `/auth/login` if unauthenticated)
|
||||
- [ ] 1.2 Register route in `apps/journal/app/routes.ts`
|
||||
- [ ] 1.3 Layout with three sections: Profile, Security, Account (anchor links for navigation)
|
||||
|
||||
## 2. Profile Section
|
||||
|
||||
- [ ] 2.1 Display name and bio form fields, pre-filled from current user data
|
||||
- [ ] 2.2 Create `POST /api/settings/profile` action to update display name and bio
|
||||
- [ ] 2.3 Register API route in `routes.ts`
|
||||
- [ ] 2.4 Success/error feedback on save
|
||||
|
||||
## 3. Security Section — Passkey Management
|
||||
|
||||
- [ ] 3.1 Load user's credentials in settings loader (device type, transports, createdAt)
|
||||
- [ ] 3.2 Render passkey list with friendly transport labels and registration date
|
||||
- [ ] 3.3 Add passkey button using existing `addPasskeyStart`/`addPasskeyFinish` flow, hidden when WebAuthn unsupported
|
||||
- [ ] 3.4 Create `POST /api/settings/passkey/delete` action to remove a credential by ID
|
||||
- [ ] 3.5 Register API route in `routes.ts`
|
||||
- [ ] 3.6 Delete confirmation dialog, with last-passkey warning when applicable
|
||||
|
||||
## 4. Account Section — Email Change
|
||||
|
||||
- [ ] 4.1 Email display with "Change" button revealing input for new email
|
||||
- [ ] 4.2 Create `initiateEmailChange(userId, newEmail)` in auth.server.ts — generates magic token with `newEmail` metadata
|
||||
- [ ] 4.3 Create `POST /api/settings/email` action to initiate email change
|
||||
- [ ] 4.4 Register API route in `routes.ts`
|
||||
- [ ] 4.5 Update `verifyMagicToken` to handle email-change tokens (update user email)
|
||||
- [ ] 4.6 Send verification email to new address using existing email templates
|
||||
|
||||
## 5. Account Section — Deletion
|
||||
|
||||
- [ ] 5.1 "Delete account" danger button at bottom of settings page
|
||||
- [ ] 5.2 Confirmation modal requiring username input to proceed
|
||||
- [ ] 5.3 Create `POST /api/settings/delete-account` action — cascading delete, destroy session, redirect to home
|
||||
- [ ] 5.4 Register API route in `routes.ts`
|
||||
|
||||
## 6. Navigation
|
||||
|
||||
- [ ] 6.1 Add "Settings" link to Journal nav for authenticated users
|
||||
- [ ] 6.2 Move passkey count display from home page to settings security section
|
||||
|
||||
## 7. i18n
|
||||
|
||||
- [ ] 7.1 Add translation keys for all settings UI text (en + de)
|
||||
|
||||
## 8. Testing
|
||||
|
||||
### Unit tests (Vitest)
|
||||
- [ ] 8.1 Profile update: valid input saves, empty display name falls back to username
|
||||
- [ ] 8.2 Passkey delete: removes credential, rejects invalid credential ID, allows deleting last passkey
|
||||
- [ ] 8.3 Email change: creates token for new email, rejects duplicate email, rejects same-as-current email
|
||||
- [ ] 8.4 Account deletion: cascading delete removes user + credentials + tokens, destroys session
|
||||
|
||||
### E2E tests (Playwright)
|
||||
- [ ] 8.5 Auth guard: unauthenticated user visiting `/settings` is redirected to `/auth/login`
|
||||
- [ ] 8.6 Profile editing: log in, navigate to settings, update display name and bio, verify changes persist after reload, verify public profile reflects changes
|
||||
- [ ] 8.7 Passkey management: log in, navigate to security section, add passkey (virtual WebAuthn authenticator), verify it appears in list, delete it, verify removed
|
||||
- [ ] 8.8 Delete last passkey: delete only passkey, verify warning modal appears, confirm deletion, verify magic link login still works
|
||||
- [ ] 8.9 Email change: initiate email change, follow verification link (dev mode), verify email updated in settings
|
||||
- [ ] 8.10 Account deletion: click delete, type wrong username (verify blocked), type correct username, confirm, verify redirected to home and session destroyed
|
||||
- [ ] 8.11 Navigation: verify "Settings" link appears in nav when logged in, not visible when logged out
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
## 1. Settings Route & Layout
|
||||
|
||||
- [x] 1.1 Create `apps/journal/app/routes/settings.tsx` with loader auth guard (redirect to `/auth/login` if unauthenticated)
|
||||
- [x] 1.2 Register route in `apps/journal/app/routes.ts`
|
||||
- [x] 1.3 Layout with three sections: Profile, Security, Account (anchor links for navigation)
|
||||
|
||||
## 2. Profile Section
|
||||
|
||||
- [x] 2.1 Display name and bio form fields, pre-filled from current user data
|
||||
- [x] 2.2 Create `POST /api/settings/profile` action to update display name and bio
|
||||
- [x] 2.3 Register API route in `routes.ts`
|
||||
- [x] 2.4 Success/error feedback on save
|
||||
|
||||
## 3. Security Section — Passkey Management
|
||||
|
||||
- [x] 3.1 Load user's credentials in settings loader (device type, transports, createdAt)
|
||||
- [x] 3.2 Render passkey list with friendly transport labels and registration date
|
||||
- [x] 3.3 Add passkey button using existing `addPasskeyStart`/`addPasskeyFinish` flow, hidden when WebAuthn unsupported
|
||||
- [x] 3.4 Create `POST /api/settings/passkey/delete` action to remove a credential by ID
|
||||
- [x] 3.5 Register API route in `routes.ts`
|
||||
- [x] 3.6 Delete confirmation dialog, with last-passkey warning when applicable
|
||||
|
||||
## 4. Account Section — Email Change
|
||||
|
||||
- [x] 4.1 Email display with "Change" button revealing input for new email
|
||||
- [x] 4.2 Create `initiateEmailChange(userId, newEmail)` in auth.server.ts — generates magic token with `newEmail` metadata
|
||||
- [x] 4.3 Create `POST /api/settings/email` action to initiate email change
|
||||
- [x] 4.4 Register API route in `routes.ts`
|
||||
- [x] 4.5 Update `verifyMagicToken` to handle email-change tokens (update user email)
|
||||
- [x] 4.6 Send verification email to new address using existing email templates
|
||||
|
||||
## 5. Account Section — Deletion
|
||||
|
||||
- [x] 5.1 "Delete account" danger button at bottom of settings page
|
||||
- [x] 5.2 Confirmation modal requiring username input to proceed
|
||||
- [x] 5.3 Create `POST /api/settings/delete-account` action — cascading delete, destroy session, redirect to home
|
||||
- [x] 5.4 Register API route in `routes.ts`
|
||||
|
||||
## 6. Navigation
|
||||
|
||||
- [x] 6.1 Add "Settings" link to Journal nav for authenticated users
|
||||
- [x] 6.2 Move passkey count display from home page to settings security section
|
||||
|
||||
## 7. i18n
|
||||
|
||||
- [x] 7.1 Add translation keys for all settings UI text (en + de)
|
||||
|
||||
## 8. Testing
|
||||
|
||||
### Unit tests (Vitest)
|
||||
- [x] 8.1 Profile update: valid input saves, empty display name falls back to username
|
||||
- [x] 8.2 Passkey delete: removes credential, rejects invalid credential ID, allows deleting last passkey
|
||||
- [x] 8.3 Email change: creates token for new email, rejects duplicate email, rejects same-as-current email
|
||||
- [x] 8.4 Account deletion: cascading delete removes user + credentials + tokens, destroys session
|
||||
|
||||
### E2E tests (Playwright)
|
||||
- [x] 8.5 Auth guard: unauthenticated user visiting `/settings` is redirected to `/auth/login`
|
||||
- [x] 8.6 Profile editing: log in, navigate to settings, update display name and bio, verify changes persist after reload, verify public profile reflects changes
|
||||
- [x] 8.7 Passkey management: log in, navigate to security section, add passkey (virtual WebAuthn authenticator), verify it appears in list, delete it, verify removed
|
||||
- [x] 8.8 Delete last passkey: delete only passkey, verify warning modal appears, confirm deletion, verify magic link login still works
|
||||
- [x] 8.9 Email change: initiate email change, follow verification link (dev mode), verify email updated in settings
|
||||
- [x] 8.10 Account deletion: click delete, type wrong username (verify blocked), type correct username, confirm, verify redirected to home and session destroyed
|
||||
- [x] 8.11 Navigation: verify "Settings" link appears in nav when logged in, not visible when logged out
|
||||
83
openspec/specs/account-settings/spec.md
Normal file
83
openspec/specs/account-settings/spec.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Settings page access
|
||||
The Journal SHALL provide an account settings page at `/settings` accessible to authenticated users.
|
||||
|
||||
#### Scenario: Authenticated access
|
||||
- **WHEN** a logged-in user navigates to `/settings`
|
||||
- **THEN** the settings page is displayed with profile, security, and account sections
|
||||
|
||||
#### Scenario: Unauthenticated access
|
||||
- **WHEN** an unauthenticated user navigates to `/settings`
|
||||
- **THEN** they are redirected to `/auth/login`
|
||||
|
||||
### Requirement: Profile editing
|
||||
The settings page SHALL allow users to edit their display name and bio.
|
||||
|
||||
#### Scenario: Update display name
|
||||
- **WHEN** a user changes their display name and submits
|
||||
- **THEN** the display name is updated and reflected on their public profile
|
||||
|
||||
#### Scenario: Update bio
|
||||
- **WHEN** a user enters a bio (max 160 characters) and submits
|
||||
- **THEN** the bio is saved and visible on their public profile
|
||||
|
||||
#### Scenario: Empty fields
|
||||
- **WHEN** a user clears their display name
|
||||
- **THEN** their username is used as the display name fallback
|
||||
|
||||
### Requirement: Passkey management
|
||||
The settings page SHALL list all registered passkeys and allow adding or deleting them.
|
||||
|
||||
#### Scenario: View passkeys
|
||||
- **WHEN** a user visits the security section
|
||||
- **THEN** they see a list of their passkeys with device type, transport label, and registration date
|
||||
|
||||
#### Scenario: Add passkey
|
||||
- **WHEN** a user clicks "Add passkey" on a browser that supports WebAuthn
|
||||
- **THEN** the browser passkey creation prompt appears and the new passkey is added to the list
|
||||
|
||||
#### Scenario: Add passkey unsupported browser
|
||||
- **WHEN** a user visits the security section on a browser without WebAuthn
|
||||
- **THEN** the "Add passkey" button is disabled with a message explaining the browser limitation
|
||||
|
||||
#### Scenario: Delete passkey
|
||||
- **WHEN** a user clicks delete on a passkey and confirms
|
||||
- **THEN** the passkey is removed and can no longer be used for login
|
||||
|
||||
#### Scenario: Delete last passkey
|
||||
- **WHEN** a user deletes their only passkey
|
||||
- **THEN** a warning is shown that they will need to use magic links to sign in, and the deletion proceeds after confirmation
|
||||
|
||||
### Requirement: Email change
|
||||
The settings page SHALL allow users to change their email address with verification.
|
||||
|
||||
#### Scenario: Initiate email change
|
||||
- **WHEN** a user enters a new email and submits
|
||||
- **THEN** a verification link is sent to the new email address
|
||||
|
||||
#### Scenario: Verify new email
|
||||
- **WHEN** the user clicks the verification link
|
||||
- **THEN** their email is updated to the new address
|
||||
|
||||
#### Scenario: Duplicate email
|
||||
- **WHEN** a user enters an email already in use by another account
|
||||
- **THEN** an error is shown indicating the email is taken
|
||||
|
||||
### Requirement: Account deletion
|
||||
The settings page SHALL allow users to permanently delete their account.
|
||||
|
||||
#### Scenario: Delete account
|
||||
- **WHEN** a user clicks "Delete account" and types their username to confirm
|
||||
- **THEN** their account and all associated data are permanently deleted and they are logged out
|
||||
|
||||
#### Scenario: Cancel deletion
|
||||
- **WHEN** a user opens the delete confirmation but does not confirm
|
||||
- **THEN** no data is deleted and they remain on the settings page
|
||||
|
||||
### Requirement: Settings navigation
|
||||
The Journal navigation SHALL include a link to the settings page for authenticated users.
|
||||
|
||||
#### Scenario: Settings link visible
|
||||
- **WHEN** a logged-in user views the navigation
|
||||
- **THEN** a "Settings" link is present that navigates to `/settings`
|
||||
|
|
@ -1,130 +1,29 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Passkey registration
|
||||
The Journal SHALL allow new users to register using a passkey (WebAuthn) when the browser supports it. No password is required.
|
||||
|
||||
#### Scenario: Successful passkey registration
|
||||
- **WHEN** a user enters an email and username and creates a passkey via the browser prompt
|
||||
- **THEN** a new user account is created, the passkey credential is stored, and the user is logged in
|
||||
|
||||
#### Scenario: Duplicate email
|
||||
- **WHEN** a user submits an email that is already registered
|
||||
- **THEN** the system displays an error indicating the email is already in use
|
||||
|
||||
#### Scenario: Duplicate username
|
||||
- **WHEN** a user submits a username that is already taken
|
||||
- **THEN** the system displays an error indicating the username is not available
|
||||
|
||||
### Requirement: Magic link registration (fallback)
|
||||
The Journal SHALL allow new users to register via magic link when the browser does not support passkeys. The user can add a passkey later from a supported browser.
|
||||
|
||||
#### Scenario: Register without passkey support
|
||||
- **WHEN** a user's browser does not support WebAuthn
|
||||
- **THEN** the registration page shows a "Register with Magic Link" button instead of the passkey button
|
||||
|
||||
#### Scenario: Successful magic link registration
|
||||
- **WHEN** a user submits email and username via magic link registration
|
||||
- **THEN** a new user account is created without a credential, a verification email is sent, and the user is redirected to verify their email
|
||||
|
||||
#### Scenario: Magic link verify redirects to add-passkey
|
||||
- **WHEN** a user clicks the verification link from magic link registration
|
||||
- **THEN** they are logged in and redirected to the home page with the add-passkey prompt
|
||||
|
||||
### Requirement: Passkey login
|
||||
The Journal SHALL allow returning users to log in using a stored passkey when the browser supports WebAuthn.
|
||||
|
||||
#### Scenario: Successful passkey login
|
||||
- **WHEN** a user clicks "Sign in" and selects a passkey from the browser prompt
|
||||
- **THEN** the user is authenticated and redirected to their activity feed
|
||||
|
||||
#### Scenario: No passkey available
|
||||
- **WHEN** a user has no passkey on the current device
|
||||
- **THEN** the system offers magic link login as a fallback
|
||||
|
||||
#### Scenario: Browser without WebAuthn support
|
||||
- **WHEN** a user's browser does not support WebAuthn
|
||||
- **THEN** the login page shows only the magic link option with no passkey button
|
||||
|
||||
### Requirement: Magic link login (fallback)
|
||||
The Journal SHALL allow users to log in via a magic link sent to their email. This serves as a fallback for devices without passkey support or for logging in on a new device.
|
||||
|
||||
#### Scenario: Request magic link
|
||||
- **WHEN** a user enters their email and clicks "Send magic link"
|
||||
- **THEN** an email with a single-use login link is sent to their address
|
||||
|
||||
#### Scenario: Valid magic link
|
||||
- **WHEN** a user clicks a valid, non-expired magic link
|
||||
- **THEN** the user is logged in and the link is invalidated
|
||||
|
||||
#### Scenario: Expired magic link
|
||||
- **WHEN** a user clicks a magic link older than 15 minutes
|
||||
- **THEN** the system displays an error and prompts them to request a new link
|
||||
|
||||
#### Scenario: Rate limiting
|
||||
- **WHEN** a user requests more than 5 magic links in 10 minutes
|
||||
- **THEN** subsequent requests are rejected with a rate limit message
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Add passkey from new device
|
||||
The Journal SHALL allow logged-in users to register additional passkeys for new devices.
|
||||
The Journal SHALL allow logged-in users to register additional passkeys from the account settings page or via the post-login prompt.
|
||||
|
||||
#### Scenario: Add passkey after magic link login
|
||||
- **WHEN** a user logs in via magic link on a device that supports WebAuthn
|
||||
- **THEN** the system prompts them to register a passkey for that device
|
||||
|
||||
#### Scenario: Add passkey from settings
|
||||
- **WHEN** a user clicks "Add passkey" in the security section of account settings
|
||||
- **THEN** the browser passkey creation prompt appears and the new passkey is stored
|
||||
|
||||
#### Scenario: Add passkey prompt on unsupported browser
|
||||
- **WHEN** a user logs in via magic link on a device that does not support WebAuthn
|
||||
- **THEN** the system shows the add-passkey prompt with a message that the browser does not support passkeys
|
||||
|
||||
### Requirement: Passkey status display
|
||||
The Journal home page SHALL show the user how many passkeys they have registered.
|
||||
## ADDED Requirements
|
||||
|
||||
#### Scenario: User with passkeys
|
||||
- **WHEN** a logged-in user visits the home page and has one or more passkeys
|
||||
- **THEN** the page displays the passkey count (e.g., "2 passkeys registered")
|
||||
### Requirement: Delete passkey
|
||||
The Journal SHALL allow users to delete individual passkeys from account settings.
|
||||
|
||||
### Requirement: User profile page
|
||||
Each user SHALL have a public profile page displaying their username and routes.
|
||||
#### Scenario: Delete passkey
|
||||
- **WHEN** a user deletes a passkey from account settings
|
||||
- **THEN** the credential is removed from the database and can no longer be used for authentication
|
||||
|
||||
#### Scenario: View own profile
|
||||
- **WHEN** a logged-in user navigates to their profile
|
||||
- **THEN** they see their username, bio, and a list of their routes
|
||||
|
||||
#### Scenario: View other user's profile
|
||||
- **WHEN** a user navigates to another user's profile URL
|
||||
- **THEN** they see that user's username, bio, and public routes
|
||||
|
||||
### Requirement: Federated identity structure
|
||||
User accounts SHALL follow the federated identity pattern (`@user@instance`) to prepare for ActivityPub federation in Phase 2.
|
||||
|
||||
#### Scenario: Username format
|
||||
- **WHEN** a user registers with username "alice" on trails.cool
|
||||
- **THEN** their full identity is stored as `@alice@trails.cool`
|
||||
|
||||
### Requirement: Session management
|
||||
The Journal SHALL maintain authenticated sessions using secure HTTP-only cookies.
|
||||
|
||||
#### Scenario: Session persistence
|
||||
- **WHEN** a logged-in user closes and reopens their browser
|
||||
- **THEN** they remain logged in if the session has not expired
|
||||
|
||||
#### Scenario: Logout
|
||||
- **WHEN** a user clicks "Log out"
|
||||
- **THEN** their session is invalidated and they are redirected to the login page
|
||||
|
||||
### Requirement: No passwords
|
||||
The Journal SHALL NOT support password-based authentication. All authentication is via passkeys or magic links.
|
||||
|
||||
#### Scenario: No password field
|
||||
- **WHEN** a user views the registration or login page
|
||||
- **THEN** there is no password field
|
||||
|
||||
### Requirement: Magic link login
|
||||
The magic link login flow SHALL deliver the link via email in production instead of logging to console.
|
||||
|
||||
#### Scenario: Production email delivery
|
||||
- **WHEN** a user requests a magic link in production
|
||||
- **THEN** the link is emailed to the user (not just logged to console)
|
||||
|
||||
#### Scenario: Dev mode unchanged
|
||||
- **WHEN** a user requests a magic link in development
|
||||
- **THEN** the link is returned directly for auto-redirect (existing behavior preserved)
|
||||
#### Scenario: Delete last passkey warning
|
||||
- **WHEN** a user attempts to delete their only remaining passkey
|
||||
- **THEN** a warning is shown explaining they will need to use magic links, and deletion proceeds after confirmation
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue