Password reset token CRUD backed by SQLite (migration 003).
Encapsulates all password_reset_tokens queries so route handlers never
write raw SQL — per AGENT.md: "Do not write raw SQL in route handlers."
Methods
(static) claim(token) → {Object|null}
Atomically claim a token — marks it as used only if it is still unused and
not expired. Returns the token row on success, or null if the token was
missing, already used, or expired (i.e. another concurrent request won the race).
This eliminates the TOCTOU race between SELECT and UPDATE by performing a
single atomic UPDATE and checking changes > 0.
Parameters:
| Name | Type | Description |
|---|---|---|
token |
string | The reset token to claim. |
Returns:
The token row (with usedAt now set), or null.
- Type
- Object | null
(static) create(token, userId, expiresAt)
Create a new password reset token, invalidating any existing unused tokens for the same user first (only the latest token should be valid).
Parameters:
| Name | Type | Description |
|---|---|---|
token |
string | Cryptographically random base64url string. |
userId |
string | The user requesting the reset. |
expiresAt |
string | ISO 8601 expiry timestamp. |
(static) deleteExpired() → {number}
Delete all expired tokens (both used and unused) — called by the periodic cleanup job so the table stays small.
Returns:
Number of deleted rows.
- Type
- number
(static) deleteUnusedByUserId(userId) → {number}
Delete all unused tokens for a user (called after a successful reset to invalidate any other outstanding reset links).
Parameters:
| Name | Type | Description |
|---|---|---|
userId |
string |
Returns:
Number of deleted rows.
- Type
- number