- 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
98 lines
3.0 KiB
Markdown
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
|