feat(auth): password reset, forgot username, and admin user management for local accounts

- Add /forgot-password and /forgot-username page routes and templates
- Update login page label to "Username or Email" (both already accepted by backend)
- Add "Forgot password?" and "Forgot username?" links to login page
- Add POST /api/auth/forgot-username endpoint + send_forgot_username_email() utility
- Add admin endpoints: PATCH /local/{id}, POST /local/{id}/send-password-reset, POST /local/{id}/set-password
- Update admin_users.html with Edit, Password, and Reset action buttons + modals
- Add 23 tests; fix code review issues (import style, display_name clearing behaviour)
- Update docs/API.md and docs/UserGuide.md

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-08 10:06:08 +00:00
parent 44ea43f9cf
commit d36ba88de7
10 changed files with 742 additions and 17 deletions
+125
View File
@@ -844,6 +844,131 @@ problem.
---
**GET** `/api/admin/users/local`
List all local (email/password) user accounts with basic metadata.
---
**POST** `/api/admin/users/local`
Create a new local user account (admin-only, immediately active — no email verification required).
**Request body**:
```json
{
"email": "user@example.com",
"username": "alice",
"display_name": "Alice Smith",
"password": "securepassword",
"is_admin": false
}
```
---
**PATCH** `/api/admin/users/local/{local_user_id}`
Update an existing local user account. Only the provided (non-null) fields are modified.
If the email is changed, the associated `UserProfile.user_id` is also updated automatically.
**Request body** (all fields optional):
```json
{
"email": "newemail@example.com",
"display_name": "Alice Wonderland",
"is_admin": true,
"is_active": false
}
```
**Error Responses**:
- `404`: Local user not found
- `409`: New email already taken by another account
---
**POST** `/api/admin/users/local/{local_user_id}/send-password-reset`
Send a password reset email to a local user on their behalf. Useful when a user is locked out.
Returns `{"sent": true}` on success or `{"sent": false, "reason": "..."}` when SMTP is not
configured or sending fails (never returns an error status so the admin always gets feedback).
**Error Responses**:
- `404`: Local user not found
---
**POST** `/api/admin/users/local/{local_user_id}/set-password`
Directly set a new password for a local user without requiring an email token (last resort when
email delivery is unavailable). The user should be advised to change their password after logging in.
**Request body**:
```json
{
"password": "temporarypassword"
}
```
**Error Responses**:
- `404`: Local user not found
- `422`: Password shorter than 8 characters
---
**DELETE** `/api/admin/users/local/{local_user_id}`
Delete a local user account by numeric ID. The associated `UserProfile` is also removed. Documents
owned by this user are **not** deleted. Returns `204 No Content` on success.
---
### Local Authentication (self-service)
These endpoints are for local (email/password) users and do not require authentication.
**POST** `/api/auth/request-password-reset`
Send a password reset email. Always returns 200 to avoid leaking whether an email is registered.
**Request body**:
```json
{ "email": "user@example.com" }
```
---
**POST** `/api/auth/reset-password`
Set a new password using a valid reset token (received via email).
**Request body**:
```json
{
"token": "the-token-from-email",
"new_password": "newpassword",
"new_password_confirm": "newpassword"
}
```
**Error Responses**:
- `400`: Token is invalid or expired
- `422`: Passwords do not match
---
**POST** `/api/auth/forgot-username`
Send a username reminder email. Always returns 200 to avoid leaking whether an email is registered.
**Request body**:
```json
{ "email": "user@example.com" }
```
---
### Settings Suggestions (Autocomplete)
**GET** `/api/settings/{key}/suggestions`
+23 -2
View File
@@ -28,8 +28,29 @@ If OpenID Connect authentication is configured:
3. Log in with your existing credentials on that platform
4. You'll be redirected back to DocuElevate after successful authentication
#### User Sessions
- Once authenticated, your session will remain active until you log out or it expires
#### Local User Accounts
If your administrator has created a local (email/password) account for you:
1. You'll see a "Sign in with username" form on the login page
2. Enter your **username or email address** — both are accepted
3. Enter your password and click **Sign in**
##### Forgot your password?
If you can't remember your password:
1. Click **Forgot password?** below the sign-in form
2. Enter your email address and click **Send reset link**
3. Check your inbox for a password reset email (valid for 24 hours)
4. Click the link in the email and enter your new password
##### Forgot your username?
If you can't remember your username:
1. Click **Forgot username?** below the sign-in form
2. Enter your email address and click **Send username reminder**
3. You'll receive an email with your username
> **Tip:** You can always sign in with your email address directly — you don't need to look up your username.
- Click the "Logout" button in the top navigation bar to end your session
- For security, sessions automatically expire after a period of inactivity