2.3 KiB
2.3 KiB
Password Reset
Endpoints
POST /api/v1/auth/forgot-password
Request:
{
"data": {
"type": "auth_forgot_password",
"attributes": {
"email": "user@example.com"
}
}
}
Response (200 always for valid payload):
{
"data": {
"type": "auth_forgot_password",
"attributes": {
"message": "If the account exists, a password reset link has been sent."
}
}
}
POST /api/v1/auth/reset-password
Request:
{
"data": {
"type": "auth_reset_password",
"attributes": {
"token": "<raw-token>",
"new_password": "StrongPassphrase123!",
"confirm_password": "StrongPassphrase123!"
}
}
}
Success response (200):
{
"data": {
"type": "auth_reset_password",
"attributes": {
"message": "Password has been reset successfully. Please log in again."
}
}
}
Invalid/expired/used token response (400):
{
"errors": [
{
"status": "400",
"title": "Invalid reset link",
"detail": "This reset link is invalid or expired."
}
]
}
Environment Variables
AUTH_PASSWORD_RESET_URLfrontend reset page URL; backend appends?token=...AUTH_PASSWORD_RESET_TTLreset token TTL (default20m, enforced 15-30m range)AUTH_FORGOT_PASSWORD_EMAIL_COOLDOWNcooldown per email identifier (default2m)AUTH_FORGOT_PASSWORD_MIN_DURATIONminimum forgot-password response duration (default150ms)AUTH_FORGOT_PASSWORD_IP_RATE_MAXper-IP max attempts in window (default10)AUTH_FORGOT_PASSWORD_IP_RATE_WINDOWper-IP window duration (default15m)
Security Decisions
- Uses
crypto/randfor reset token entropy. - Stores only SHA-256 token hashes in DB (
password_reset_tokens.token_hash). - Raw token is only delivered via email link.
- Tokens are single-use (
used_at) and expire (expires_at). - New forgot-password request invalidates previous active reset tokens for that user.
- Successful reset invalidates all active reset tokens for the user.
- Successful reset revokes active sessions by incrementing the MySQL-backed user session version used in JWT claims.
- Forgot-password response is generic to prevent account enumeration.
- Forgot-password throttling is applied per IP and per email identifier.