Public Access
feat(password): add reset and change password flows
Forgot password with a single-use reset link (1 hour, ACTIVE users only, same answer for every address), change password (current session stays, others end), admin reset mail with audit row, 'password changed' mail. Login and refresh wait for a running password change.
This commit is contained in:
1 parent
6e7fb1e066
commit
5a2552ddf6
37 files changed
+1256
-34
No files matched your search
@@ -16,6 +16,10 @@ To Be Played is split into three repositories:
|
||||
- Registration by email confirmation (a single-use link, valid 24 hours, on whose page the owner chooses the
|
||||
password; unconfirmed requests are deleted after 7 days) and then admin approval, login with email and password (BCrypt). Emails are stored trimmed and in
|
||||
lower case (V19, unique on `lower(email)`); a password has 8 characters and at most 72 bytes (BCrypt's limit).
|
||||
- **Password reset**: "forgot password" mails a link (`/reset-password#<token>`, single use, valid 1 hour, only the
|
||||
SHA-256 stored, a new mail replaces the old link). Only an ACTIVE user gets a working link; every other address gets
|
||||
the same `204` and no mail. A reset ends all logins; "change password" (current password + new one) ends all logins
|
||||
but the current one. A PLATFORM_ADMIN can send the same mail to an active user. Both send a "password changed" mail.
|
||||
- Brute-force protection in memory: 5 failed logins per email or 20 per IP, and 10 failed or 3 taken registrations
|
||||
per IP, block for 15 minutes with `429 TOO_MANY_ATTEMPTS` and `Retry-After`. Unknown emails cost the same BCrypt time as a
|
||||
wrong password, and every failed login gets the same error.
|
||||
@@ -85,7 +89,7 @@ To Be Played is split into three repositories:
|
||||
the deploy get the page.
|
||||
- **Mails** from the platform mailbox: "a new registration waits" to the admin (`APP_MAIL_ADMIN_TO`, with the link
|
||||
to the admin page), and to the user "confirm your address" (`CONFIRM_EMAIL`, sent first; the admin mail follows the click), then "your account is active" or "your
|
||||
registration was denied", later "your account is blocked" and "your account will be deleted from <date>". The registration sends the browser language (`de` for a German browser, else `en`), which also becomes
|
||||
registration was denied", later "your account is blocked" and "your account will be deleted from <date>", and "reset your password" (`PASSWORD_RESET`, the link) and "your password was changed" (`PASSWORD_CHANGED`, after a reset or a change). The registration sends the browser language (`de` for a German browser, else `en`), which also becomes
|
||||
the user's app language. Plain text only. See "Mail flow".
|
||||
- **Broadcast mail**: a PLATFORM_ADMIN writes one mail with subject and text in English and German (plain text),
|
||||
and sends it to all active users or to picked ones, each in their app language. Every active user gets it.
|
||||
@@ -108,6 +112,9 @@ To Be Played is split into three repositories:
|
||||
| `POST` | `/api/registration/confirm` (`{token, password}`; `204`; `410 CONFIRMATION_INVALID`) | public |
|
||||
| `POST` | `/api/registration/resend` (`{email}`; always `204`) | public |
|
||||
| `POST` | `/api/auth/login` | public, sets both cookies |
|
||||
| `POST` | `/api/auth/password-reset` (`{email}`; always `204`; `429 TOO_MANY_ATTEMPTS`: 5 per IP and 15 min) | public |
|
||||
| `POST` | `/api/auth/password-reset/confirm` (`{token, password}`; `204`; `410 RESET_INVALID`; `429`) | public |
|
||||
| `POST` | `/api/auth/password` (`{currentPassword, newPassword}`; `204`; `403 INVALID_PASSWORD`; `429`; under `/api/auth` for the refresh cookie, which keeps the current login) | access token |
|
||||
| `POST` | `/api/auth/refresh` | refresh cookie, replaces both cookies |
|
||||
| `POST` | `/api/auth/logout` | refresh cookie, clears both cookies |
|
||||
| `GET` | `/api/users/me` | access token |
|
||||
@@ -121,6 +128,7 @@ To Be Played is split into three repositories:
|
||||
| `PUT` | `/api/admin/users/{id}/blocked` (`{blocked}`; `409 SELF_ACTION`, `TARGET_IS_ADMIN`, `ALREADY_DELETED`, `TARGET_IS_PENDING`) | PLATFORM_ADMIN |
|
||||
| `DELETE` | `/api/admin/users/{id}` (`409 SELF_ACTION`, `TARGET_IS_ADMIN`, `TARGET_IS_PENDING`, `TARGET_IS_UNCONFIRMED`) | PLATFORM_ADMIN |
|
||||
| `DELETE` | `/api/admin/users/{id}/unconfirmed` (hard delete of an unconfirmed request; `204`; `404`; `409 NOT_UNCONFIRMED`, `SELF_ACTION`, `TARGET_IS_ADMIN`) | PLATFORM_ADMIN |
|
||||
| `POST` | `/api/admin/users/{id}/password-reset` (`204`; `409 NOT_ACTIVE`, `SELF_ACTION`, `TARGET_IS_ADMIN`) | PLATFORM_ADMIN |
|
||||
| `GET` | `/api/hints` (`{results}`: running hints not closed for good, newest first) | access token |
|
||||
| `PUT` | `/api/hints/{id}/done` (`204`; `404 HINT_NOT_FOUND`) | access token |
|
||||
| `GET` | `/api/admin/hints` (`{results}` with `status` PLANNED/ACTIVE/ENDED by the server clock and `doneCount`, newest first) | PLATFORM_ADMIN |
|
||||
@@ -339,6 +347,28 @@ sequenceDiagram
|
||||
end
|
||||
```
|
||||
|
||||
### Password reset flow
|
||||
|
||||
The link is valid for 1 hour and works once: the reset is one `UPDATE ... WHERE password_reset_hash = ? AND expires > now
|
||||
AND <active>` that also clears the hash. At most 3 reset mails per address and hour (over all IPs) and 5 requests per IP
|
||||
per 15 minutes; failed confirms are limited to 10 per IP. Other logins keep their access token until it expires (up to
|
||||
15 minutes); their refresh tokens end at once.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as User
|
||||
participant A as Auth
|
||||
participant D as Admin
|
||||
U->>A: POST /api/auth/password-reset {email}
|
||||
A->>A: active user: store token hash + expiry, one mail row
|
||||
A-->>U: 204 (same for any address)
|
||||
A-->>U: mail "reset your password" (link with token)
|
||||
U->>A: POST /api/auth/password-reset/confirm {token, password}
|
||||
A->>A: new password, hash cleared, all refresh tokens revoked
|
||||
A-->>U: 204 and mail "your password was changed"
|
||||
Note over D,A: POST /api/admin/users/{id}/password-reset sends the same mail (active users only) and writes the audit log
|
||||
```
|
||||
|
||||
### Steam ID and block sync to Core
|
||||
|
||||
Core keeps its own copy of the Steam ID and of the block. After a verified Steam link (also for the account that
|
||||
|
||||
Reference in new issue
Block a user