api/docs/history/2025-07-22-forgot-password.md
Yoga Pangestu 104a58e6e7 Add password reset functionality with email notifications
- Implemented endpoints for forgot password and reset password
- Added PasswordResetToken model and relationships
- Integrated email service using Resend for sending reset links
- Updated configuration for email and frontend URL
- Enhanced user model to include password reset tokens
- Added necessary schemas for password reset requests
- Updated requirements to include Resend SDK
- Created comprehensive documentation for the new feature
2026-07-22 20:22:58 +07:00

98 lines
3.0 KiB
Markdown

# 2025-07-22: Forgot Password - Backend
## Summary
Menambahkan fitur Lupa Kata Sandi: endpoint `POST /v1/auth/forgot-password` dan `POST /v1/auth/reset-password` dengan email via Resend.
---
## Changes
| File | Change |
|------|--------|
| `app/config.py` | Tambah `RESEND_API_KEY`, `EMAIL_FROM`, `FRONTEND_URL` |
| `.env.example` | **BARU** - Template env lengkap (DB, JWT, Google, Resend, Frontend URL) |
| `app/models/password_reset_token.py` | **BARU** - Model `password_reset_tokens` (id, user_id, token, expires_at, used_at, created_at) |
| `app/models/user.py` | Tambah relationship `password_reset_tokens` |
| `app/models/__init__.py` | Register `PasswordResetToken` |
| `app/schemas/auth.py` | **BARU** - `ForgotPasswordRequest(email)`, `ResetPasswordRequest(token, password, password_confirmation)` |
| `app/services/email_service.py` | **BARU** - Kirim email HTML via Resend SDK (green theme, Poppins font, rounded UI) |
| `app/services/auth_service.py` | **BARU** - Logic `forgot_password()` dan `reset_password()` |
| `app/routers/auth.py` | Tambah endpoint `POST /v1/auth/forgot-password` dan `POST /v1/auth/reset-password` |
| `requirements.txt` | Tambah `resend==2.10.0` |
---
## API Endpoints
### POST /v1/auth/forgot-password
Request:
```json
{ "email": "user@example.com" }
```
Logic:
1. Cek email ada di DB → kalau tidak, return 404
2. Cek apakah ada token aktif (unused + belum expired) → kalau ada, return "link masih aktif"
3. Invalidate token lama, buat token baru (bcrypt hashed), simpan ke DB
4. Kirim email via Resend dengan link reset
5. Token berlaku 1 jam, single-use
Response (200):
```json
{ "message": "Link reset password telah dikirim ke email Anda" }
```
### POST /v1/auth/reset-password
Request:
```json
{ "token": "...", "password": "newpassword123", "password_confirmation": "newpassword123" }
```
Logic:
1. Cari token unused di DB, verifikasi hash
2. Cek expiry (1 jam)
3. Update password user
4. Invalidate SEMUA token lama user tersebut
---
## Database Table
```sql
CREATE TABLE password_reset_tokens (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
token VARCHAR(255) NOT NULL,
expires_at DATETIME NOT NULL,
used_at DATETIME NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_token (token),
UNIQUE INDEX idx_token_unique (token),
FOREIGN KEY (user_id) REFERENCES users(id)
);
```
---
## Security Notes
- Token asli dikirim ke user via email, yang disimpan di DB adalah **bcrypt hash**
- Token berlaku **1 jam**
- **Single-use**: setelah dipakai, `used_at` diisi
- Semua token lama user di-invalidate saat:
- Request forgot-password baru
- Password berhasil di-reset
- Anti-spam: jika token aktif masih ada, tidak kirim email baru
---
## Troubleshooting
- Email tidak sampai: cek `RESEND_API_KEY` di `.env`, cek folder spam, pastikan domain verified di Resend dashboard
- Error 500: cek terminal backend, pastikan tabel `password_reset_tokens` sudah dibuat
- Server harus di-restart setelah tambah env vars baru