- 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
3.0 KiB
3.0 KiB
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:
{ "email": "user@example.com" }
Logic:
- Cek email ada di DB → kalau tidak, return 404
- Cek apakah ada token aktif (unused + belum expired) → kalau ada, return "link masih aktif"
- Invalidate token lama, buat token baru (bcrypt hashed), simpan ke DB
- Kirim email via Resend dengan link reset
- Token berlaku 1 jam, single-use
Response (200):
{ "message": "Link reset password telah dikirim ke email Anda" }
POST /v1/auth/reset-password
Request:
{ "token": "...", "password": "newpassword123", "password_confirmation": "newpassword123" }
Logic:
- Cari token unused di DB, verifikasi hash
- Cek expiry (1 jam)
- Update password user
- Invalidate SEMUA token lama user tersebut
Database Table
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_atdiisi - 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_KEYdi.env, cek folder spam, pastikan domain verified di Resend dashboard - Error 500: cek terminal backend, pastikan tabel
password_reset_tokenssudah dibuat - Server harus di-restart setelah tambah env vars baru