Problem Statement
The API has no password reset mechanism (forgot-password + reset-password endpoints). Users who forget their password have no way to recover their account. The auth controller (api/src/auth/auth.controller.ts) only exposes register and login. No email integration exists.
Evidence
// api/src/auth/auth.controller.ts — no reset-password endpoints
@Controller("auth") export class AuthController {
@Post("register") register(...) { ... }
@Post("login") login(...) { ... }
}
Impact
Zero account recovery capability. Users locked out permanently if they forget their password. No admin override for password resets. This makes the system unusable for real user accounts.
Proposed Solution
- Add
POST /auth/forgot-password — accepts email, sends reset token (via email service)
- Add
POST /auth/reset-password — accepts reset token + new password, updates hash
- Store reset tokens in
password_reset_tokens table with TTL (1 hour) and one-time-use enforcement
- Rate limit both endpoints aggressively (3 attempts per hour per email)
Technical Requirements
- Reset tokens must be cryptographically random (crypto.randomBytes)
- Reset tokens must be hashed in DB (SHA-256)
- Tokens must expire after 1 hour
- One-time use enforced
- Email address must not leak whether it exists (always return success message)
Acceptance Criteria
File Map
api/src/auth/auth.controller.ts — add endpoints
api/src/auth/auth.service.ts — add reset logic
api/src/auth/password-reset.service.ts — new service
database/migrations/2026061502_add_password_reset_tokens.up.sql
database/migrations/2026061502_add_password_reset_tokens.down.sql
Dependencies
- Blocked by: REPO-003 (need DB) or REPO-004 (connection pool)
Testing Strategy
- Unit: Test token generation, hashing, expiry validation
- Unit: Test one-time-use enforcement
- Integration: Test full forgot-password → reset-password → login flow
- Test anti-enumeration (email existence not leaked)
Security Considerations
Reset tokens must be single-use, time-limited, rate-limited, and stored hashed. Email must never reveal whether it exists. Password change must invalidate all existing sessions. Reset URL must be served over HTTPS.
Labels: feature, security, high impact
Priority: High | Difficulty: Intermediate | Estimated Effort: 2d
Labels: feature,security,high impact
Priority: High | Difficulty: Intermediate | Estimated Effort: 2d
Backlog ID: REPO-016
Problem Statement
The API has no password reset mechanism (
forgot-password+reset-passwordendpoints). Users who forget their password have no way to recover their account. The auth controller (api/src/auth/auth.controller.ts) only exposes register and login. No email integration exists.Evidence
Impact
Zero account recovery capability. Users locked out permanently if they forget their password. No admin override for password resets. This makes the system unusable for real user accounts.
Proposed Solution
POST /auth/forgot-password— accepts email, sends reset token (via email service)POST /auth/reset-password— accepts reset token + new password, updates hashpassword_reset_tokenstable with TTL (1 hour) and one-time-use enforcementTechnical Requirements
Acceptance Criteria
File Map
api/src/auth/auth.controller.ts— add endpointsapi/src/auth/auth.service.ts— add reset logicapi/src/auth/password-reset.service.ts— new servicedatabase/migrations/2026061502_add_password_reset_tokens.up.sqldatabase/migrations/2026061502_add_password_reset_tokens.down.sqlDependencies
Testing Strategy
Security Considerations
Reset tokens must be single-use, time-limited, rate-limited, and stored hashed. Email must never reveal whether it exists. Password change must invalidate all existing sessions. Reset URL must be served over HTTPS.
Labels: feature, security, high impact
Priority: High | Difficulty: Intermediate | Estimated Effort: 2d
Labels: feature,security,high impact
Priority: High | Difficulty: Intermediate | Estimated Effort: 2d
Backlog ID: REPO-016