- Remove OtpGateGuard from transactions controller (OTP verified at login) - Fix categories controller to use authenticated user instead of TEMP_USER_ID - Add comprehensive implementation plan document - Update .env.example with WEB_APP_URL - Prepare for admin dashboard development
226 lines
5.3 KiB
Markdown
226 lines
5.3 KiB
Markdown
# ✅ Final Fixes - Change Password & Resend OTP
|
|
|
|
## 🐛 **Issues Fixed:**
|
|
|
|
### 1. ✅ **Change Password Not Functioning**
|
|
### 2. ✅ **Resend OTP Email Error**
|
|
|
|
---
|
|
|
|
## 🔧 **Fix 1: Change Password Implementation**
|
|
|
|
### **Backend Changes:**
|
|
|
|
#### **Added Endpoint** (`auth.controller.ts`):
|
|
```typescript
|
|
@Post('change-password')
|
|
@UseGuards(JwtAuthGuard)
|
|
async changePassword(
|
|
@Req() req: RequestWithUser,
|
|
@Body() body: { currentPassword: string; newPassword: string },
|
|
) {
|
|
return this.authService.changePassword(
|
|
req.user.userId,
|
|
body.currentPassword,
|
|
body.newPassword,
|
|
);
|
|
}
|
|
```
|
|
|
|
#### **Added Service Method** (`auth.service.ts`):
|
|
```typescript
|
|
async changePassword(
|
|
userId: string,
|
|
currentPassword: string,
|
|
newPassword: string,
|
|
) {
|
|
// Get user with password hash
|
|
const user = await this.prisma.user.findUnique({
|
|
where: { id: userId },
|
|
select: { passwordHash: true },
|
|
});
|
|
|
|
if (!user || !user.passwordHash) {
|
|
throw new BadRequestException('Cannot change password for this account');
|
|
}
|
|
|
|
// Verify current password
|
|
const isValid = await bcrypt.compare(currentPassword, user.passwordHash);
|
|
if (!isValid) {
|
|
throw new UnauthorizedException('Current password is incorrect');
|
|
}
|
|
|
|
// Hash new password
|
|
const newPasswordHash = await bcrypt.hash(newPassword, 10);
|
|
|
|
// Update password
|
|
await this.prisma.user.update({
|
|
where: { id: userId },
|
|
data: { passwordHash: newPasswordHash },
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
message: 'Password changed successfully',
|
|
};
|
|
}
|
|
```
|
|
|
|
### **Frontend Changes:**
|
|
|
|
#### **Added States** (`Profile.tsx`):
|
|
```typescript
|
|
const [currentPassword, setCurrentPassword] = useState("")
|
|
const [newPassword, setNewPassword] = useState("")
|
|
const [confirmPassword, setConfirmPassword] = useState("")
|
|
const [passwordLoading, setPasswordLoading] = useState(false)
|
|
const [passwordError, setPasswordError] = useState("")
|
|
const [passwordSuccess, setPasswordSuccess] = useState("")
|
|
```
|
|
|
|
#### **Added Handler**:
|
|
```typescript
|
|
const handleChangePassword = async () => {
|
|
// Validation
|
|
if (!currentPassword || !newPassword || !confirmPassword) {
|
|
setPasswordError("All fields are required")
|
|
return
|
|
}
|
|
|
|
if (newPassword !== confirmPassword) {
|
|
setPasswordError("New passwords do not match")
|
|
return
|
|
}
|
|
|
|
if (newPassword.length < 6) {
|
|
setPasswordError("New password must be at least 6 characters")
|
|
return
|
|
}
|
|
|
|
// Call API
|
|
await axios.post(`${API}/auth/change-password`, {
|
|
currentPassword,
|
|
newPassword
|
|
})
|
|
|
|
setPasswordSuccess("Password changed successfully!")
|
|
// Clear fields
|
|
}
|
|
```
|
|
|
|
#### **Updated UI**:
|
|
- Connected inputs to state
|
|
- Added onClick handler to button
|
|
- Added loading state
|
|
- Added error/success alerts
|
|
- Added validation
|
|
|
|
---
|
|
|
|
## 🔧 **Fix 2: Resend OTP Email**
|
|
|
|
### **Problem:**
|
|
The resend endpoint required a full JWT token, but during OTP verification we only have a temp token.
|
|
|
|
### **Solution:**
|
|
Created a special resend endpoint that accepts temp tokens.
|
|
|
|
### **Backend Changes:**
|
|
|
|
#### **Added Endpoint** (`otp.controller.ts`):
|
|
```typescript
|
|
@Post('email/resend')
|
|
async resendEmailOtp(@Body() body: { tempToken: string }) {
|
|
try {
|
|
// Verify temp token
|
|
const payload = this.jwtService.verify(body.tempToken);
|
|
|
|
if (!payload.temp) {
|
|
throw new UnauthorizedException('Invalid token type');
|
|
}
|
|
|
|
const userId = payload.userId || payload.sub;
|
|
|
|
// Send OTP
|
|
return this.otpService.sendEmailOtp(userId);
|
|
} catch (error) {
|
|
throw new UnauthorizedException('Invalid or expired token');
|
|
}
|
|
}
|
|
```
|
|
|
|
### **Frontend Changes:**
|
|
|
|
#### **Updated Resend Handler** (`OtpVerification.tsx`):
|
|
```typescript
|
|
// OLD - Used wrong endpoint
|
|
await axios.post(`${API_URL}/api/otp/email/send`, {}, {
|
|
headers: { Authorization: `Bearer ${tempToken}` }
|
|
})
|
|
|
|
// NEW - Use resend endpoint with temp token
|
|
await axios.post(`${API_URL}/api/otp/email/resend`, {
|
|
tempToken
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 **Files Modified:**
|
|
|
|
### Backend:
|
|
1. **`apps/api/src/auth/auth.controller.ts`**
|
|
- Added `change-password` endpoint
|
|
|
|
2. **`apps/api/src/auth/auth.service.ts`**
|
|
- Added `changePassword()` method
|
|
|
|
3. **`apps/api/src/otp/otp.controller.ts`**
|
|
- Added `email/resend` endpoint
|
|
- Injected `JwtService`
|
|
|
|
### Frontend:
|
|
1. **`apps/web/src/components/pages/Profile.tsx`**
|
|
- Added password change states
|
|
- Added `handleChangePassword()` handler
|
|
- Updated UI with inputs, validation, alerts
|
|
|
|
2. **`apps/web/src/components/pages/OtpVerification.tsx`**
|
|
- Updated `handleResendEmail()` to use new endpoint
|
|
|
|
---
|
|
|
|
## 🧪 **Testing:**
|
|
|
|
### **Test Change Password:**
|
|
1. ✅ Go to Profile page
|
|
2. ✅ Enter current password
|
|
3. ✅ Enter new password
|
|
4. ✅ Confirm new password
|
|
5. ✅ Click "Update Password"
|
|
6. ✅ See success message
|
|
7. ✅ Logout and login with new password
|
|
|
|
### **Test Resend OTP:**
|
|
1. ✅ Login with email OTP enabled
|
|
2. ✅ On OTP page, wait 30 seconds
|
|
3. ✅ Click "Resend Code"
|
|
4. ✅ Check console for new OTP code
|
|
5. ✅ Enter new code
|
|
6. ✅ Login successfully
|
|
|
|
---
|
|
|
|
## ✨ **What Works Now:**
|
|
|
|
✅ **Change Password**: Full implementation with validation
|
|
✅ **Resend OTP**: Works with temp token
|
|
✅ **Error Handling**: Proper error messages
|
|
✅ **Success Feedback**: Clear success indicators
|
|
✅ **Loading States**: Shows loading during operations
|
|
✅ **Validation**: Client-side validation before API call
|
|
|
|
---
|
|
|
|
**Both features are now fully functional! Test them out!** 🚀
|