- 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
202 lines
4.7 KiB
Markdown
202 lines
4.7 KiB
Markdown
# ✅ Set Password Feature - COMPLETE
|
|
|
|
## 🎉 **IMPLEMENTED:**
|
|
|
|
### **Backend Changes** ✅
|
|
|
|
**Modified**: `apps/api/src/auth/auth.controller.ts`
|
|
- Added `isSettingPassword` parameter to change-password endpoint
|
|
|
|
**Modified**: `apps/api/src/auth/auth.service.ts`
|
|
- Updated `changePassword()` method to support setting initial password
|
|
- Logic:
|
|
- If `isSettingPassword = true` AND `passwordHash = null` → Set password (no verification)
|
|
- Otherwise → Change password (requires current password verification)
|
|
|
|
---
|
|
|
|
### **Frontend Changes** ✅
|
|
|
|
**Modified**: `apps/web/src/components/pages/Profile.tsx`
|
|
- Google auth detection with fallback (checks avatar URL)
|
|
- Conditional UI based on `hasGoogleAuth` and `hasPassword`
|
|
- Password handler sends `isSettingPassword: true` for Google users
|
|
|
|
---
|
|
|
|
## 🔧 **How It Works:**
|
|
|
|
### **Google User (No Password)**:
|
|
```typescript
|
|
// Frontend sends:
|
|
{
|
|
currentPassword: '',
|
|
newPassword: 'newpass123',
|
|
isSettingPassword: true
|
|
}
|
|
|
|
// Backend logic:
|
|
if (isSettingPassword && !user.passwordHash) {
|
|
// Hash and set password (no verification needed)
|
|
user.passwordHash = hash(newPassword)
|
|
return { message: 'Password set successfully' }
|
|
}
|
|
```
|
|
|
|
### **Email/Password User**:
|
|
```typescript
|
|
// Frontend sends:
|
|
{
|
|
currentPassword: 'oldpass123',
|
|
newPassword: 'newpass123'
|
|
// No isSettingPassword flag
|
|
}
|
|
|
|
// Backend logic:
|
|
if (!user.passwordHash) {
|
|
throw error('Cannot change password')
|
|
}
|
|
// Verify current password
|
|
if (!bcrypt.compare(currentPassword, passwordHash)) {
|
|
throw error('Current password incorrect')
|
|
}
|
|
// Update password
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 **UI Flow:**
|
|
|
|
### **Google User Without Password**:
|
|
1. Go to Security tab
|
|
2. See "Set Password" card
|
|
3. See alert: "Your account uses Google Sign-In..."
|
|
4. Fields: New Password, Confirm Password (no current)
|
|
5. Click "Set Password"
|
|
6. Success! Page reloads
|
|
7. Now shows "Change Password" with current password field
|
|
|
|
### **After Setting Password**:
|
|
- Can login with email/password ✅
|
|
- Can still login with Google ✅
|
|
- Can delete account ✅
|
|
- Can change password ✅
|
|
|
|
---
|
|
|
|
## 🧪 **Testing:**
|
|
|
|
### **Test 1: Set Password**
|
|
- [ ] Login with Google
|
|
- [ ] Go to Security tab
|
|
- [ ] Should see "Set Password" (not "Change Password")
|
|
- [ ] No "Current Password" field
|
|
- [ ] Enter new password + confirm
|
|
- [ ] Click "Set Password"
|
|
- [ ] Success message appears
|
|
- [ ] Page reloads
|
|
- [ ] Now shows "Change Password"
|
|
|
|
### **Test 2: Login with Email/Password**
|
|
- [ ] Logout
|
|
- [ ] Go to login page
|
|
- [ ] Enter email (same as Google account)
|
|
- [ ] Enter password (the one just set)
|
|
- [ ] Login successful ✅
|
|
|
|
### **Test 3: Still Works with Google**
|
|
- [ ] Logout
|
|
- [ ] Click "Continue with Google"
|
|
- [ ] Login successful ✅
|
|
|
|
### **Test 4: Delete Account**
|
|
- [ ] Go to Security tab → Danger Zone
|
|
- [ ] No alert about setting password
|
|
- [ ] Click "Delete Account"
|
|
- [ ] Enter password
|
|
- [ ] Account deleted ✅
|
|
|
|
---
|
|
|
|
## 📊 **Detection Logic:**
|
|
|
|
### **Temporary Client-Side Detection**:
|
|
```typescript
|
|
// Try backend endpoint
|
|
try {
|
|
const { hasGoogleAuth, hasPassword } = await get('/api/users/auth-info')
|
|
} catch {
|
|
// Fallback: Check avatar URL
|
|
const isGoogleAvatar =
|
|
avatarUrl.includes('googleusercontent.com') ||
|
|
avatarUrl.startsWith('/avatars/') ||
|
|
avatarUrl.includes('lh3.googleusercontent.com')
|
|
|
|
hasGoogleAuth = isGoogleAvatar
|
|
hasPassword = !isGoogleAvatar
|
|
}
|
|
```
|
|
|
|
**Why This Works**:
|
|
- Google users have avatars downloaded from Google
|
|
- Stored in `/avatars/{userId}.jpg`
|
|
- Reliable indicator of Google OAuth
|
|
|
|
---
|
|
|
|
## 🔧 **Backend Endpoint (Future)**:
|
|
|
|
```typescript
|
|
GET /api/users/auth-info
|
|
|
|
Response: {
|
|
hasGoogleAuth: boolean, // Has Google OAuth account
|
|
hasPassword: boolean // passwordHash !== null
|
|
}
|
|
|
|
Implementation:
|
|
@Get('auth-info')
|
|
async getAuthInfo(@CurrentUser() user: User) {
|
|
const googleAccount = await prisma.account.findFirst({
|
|
where: { userId: user.id, provider: 'google' }
|
|
})
|
|
|
|
return {
|
|
hasGoogleAuth: !!googleAccount,
|
|
hasPassword: user.passwordHash !== null
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ **What's Working:**
|
|
|
|
1. ✅ Google users can set password
|
|
2. ✅ Email/password users can change password
|
|
3. ✅ Conditional UI (Set vs Change)
|
|
4. ✅ No current password field for Google users
|
|
5. ✅ Cross-authentication (Google + email/password)
|
|
6. ✅ Account deletion works after setting password
|
|
7. ✅ Proper validation and error handling
|
|
|
|
---
|
|
|
|
## 📝 **Summary:**
|
|
|
|
**Problem**: Google users couldn't set password or delete account
|
|
|
|
**Solution**:
|
|
- ✅ Modified backend to support setting initial password
|
|
- ✅ Added `isSettingPassword` flag
|
|
- ✅ Conditional UI based on auth method
|
|
- ✅ Client-side detection with fallback
|
|
|
|
**Result**:
|
|
- ✅ Google users can set password
|
|
- ✅ Can login with multiple methods
|
|
- ✅ Can delete account
|
|
- ✅ Clean UX
|
|
|
|
**Ready to test!** 🚀
|