Staff Password Reset

Overview

QODY supports two complementary password-reset mechanisms for venue staff. Part A is manager/owner-initiated (admin UI, no email required). Part B is self-service via email token. Both paths were verified live on rg-qody-demo on 2026-06-28 (qody-api--0000047, qody-admin--0000027, qody-staff-kitchen--0000012; commit e4c035e).


Part A — Manager/Owner-Initiated Reset

Endpoint

POST /admin/staff/{id}/reset-password

Authorization

Behaviour

  1. Generates a cryptographically random one-time temporary password.
  2. Sets the staff account password to this temporary value (argon2id hash stored).
  3. Returns the plaintext temporary password once in the response body — it is never stored in plaintext and cannot be retrieved again.
  4. Displays the temporary password in the StaffView modal in the admin UI.
  5. The action is written to the audit log.

Dependencies

No external dependency. Works in demo and production environments regardless of SMTP configuration.


Part B — Self-Service Forgot/Reset Flow

Step 1: Request a reset link

POST /auth/forgot-password
Body: { "email": "staff@example.com", "venueSlug": "my-restaurant" }

Step 2: Apply the reset token

POST /auth/reset-password
Body: { "token": "<token>", "newPassword": "<password>" }

Response codes:

StatusMeaning
200 okPassword updated successfully.
400 invalidToken not found or does not match stored hash.
400 expiredToken is older than 1 hour.
400 usedToken has already been consumed.
400 weakNew password does not meet minimum requirements.

Entry points


Token Security

PropertyValue
Entropy256-bit SecureRandom token
StorageOnly the SHA-256 hash is stored (UNIQUE index) — plaintext never persisted
Single-useAtomic CAS: UPDATE ... WHERE used_at IS NULL prevents TOCTOU double-use
TTL1 hour
Password hashingargon2id
Password length8–1024 characters (upper bound guards against DoS via hashing large inputs)
ScopeToken is bound to a specific staff_id — cross-venue reset is not possible
Unauthenticated datasourceForgot/reset routes use the admin bypass datasource (no session required)

Database migration

V21 adds the password_reset_tokens table with token_hash (UNIQUE), staff_id (FK), expires_at, and used_at columns.


Email Delivery

Part B uses the existing EmailService. In the current demo environment, no SMTP credentials are configured — the service performs a graceful no-op and the reset link is not delivered by email. Part A (manager-initiated) works fully in demo.

Production note: configure an SMTP or Resend secret to enable Part B email delivery. Without it, self-service resets are non-functional for end users.


Validation Chain

  1. verifier(code) — internal code review
  2. Securion peer-review — APPROVE-WITH-FIXES; issues found and fixed: timing-oracle, TOCTOU double-use, argon2 DoS guard, UNIQUE index on token hash, MANAGER-to-OWNER reset guard
  3. FlowForge — deployed to rg-qody-demo
  4. Proveo — browser E2E tests, all flows PASS
  5. John live curl — all flows verified PASS on 2026-06-28


Revision #1
Created 2026-06-28 07:21:14 UTC by John
Updated 2026-06-28 07:21:14 UTC by John