- 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
8.4 KiB
8.4 KiB
📱 WhatsApp OTP & Phone Number Implementation
✅ Completed Features:
1. Google Avatar Fix ✅
- Problem: Avatar not loading for Google OAuth users
- Fix: Always update avatar from Google profile (not just when null)
- File:
apps/api/src/auth/auth.service.ts
2. Phone Number Field ✅
- Added
phonefield to User model - Unique constraint on phone number
- Migration created and applied
3. WhatsApp OTP System ✅
- Full WhatsApp OTP implementation with mode support
- Check number validity
- Send OTP (test/live modes)
- Verify OTP
- Enable/disable WhatsApp OTP
📊 Database Changes:
Schema Updates (schema.prisma):
model User {
// ... existing fields
phone String? @unique
otpEmailEnabled Boolean @default(false)
otpWhatsappEnabled Boolean @default(false)
otpTotpEnabled Boolean @default(false)
otpTotpSecret String?
}
Migration:
- ✅ Migration created:
20251010132022_add_phone_and_whatsapp_otp - ✅ Applied successfully
- ✅ Prisma Client regenerated
🔧 Backend Implementation:
1. OTP Service (otp.service.ts):
New Methods:
// Send WhatsApp OTP
async sendWhatsappOtp(userId: string, mode: 'test' | 'live' = 'test')
// Verify WhatsApp OTP (for setup)
async verifyWhatsappOtp(userId: string, code: string)
// Verify WhatsApp OTP (for login)
async verifyWhatsappOtpForLogin(userId: string, code: string): Promise<boolean>
// Disable WhatsApp OTP
async disableWhatsappOtp(userId: string)
// Check if number is registered on WhatsApp
async checkWhatsappNumber(phone: string)
Webhook Payload Structure:
Email OTP:
{
"method": "email",
"mode": "test", // or "live"
"to": "user@example.com",
"subject": "Tabungin - Your OTP Code",
"message": "Your OTP code is: 123456...",
"code": "123456"
}
WhatsApp OTP:
{
"method": "whatsapp",
"mode": "test", // or "live"
"phone": "+1234567890",
"message": "Your Tabungin OTP code is: 123456...",
"code": "123456"
}
Check WhatsApp Number:
{
"method": "whatsapp",
"mode": "checknumber",
"phone": "+1234567890"
}
Expected Response:
{
"isRegistered": true,
"message": "Number is valid"
}
2. OTP Controller (otp.controller.ts):
New Endpoints:
// Send WhatsApp OTP (for setup in profile)
POST /api/otp/whatsapp/send
Body: { mode?: 'test' | 'live' }
Auth: Required
// Verify WhatsApp OTP (enable feature)
POST /api/otp/whatsapp/verify
Body: { code: string }
Auth: Required
// Disable WhatsApp OTP
POST /api/otp/whatsapp/disable
Auth: Required
// Check if phone number is registered on WhatsApp
POST /api/otp/whatsapp/check
Body: { phone: string }
Auth: Required
Updated Endpoints:
// Get OTP status (now includes phone and whatsappEnabled)
GET /api/otp/status
Response: {
phone: string | null,
emailEnabled: boolean,
whatsappEnabled: boolean,
totpEnabled: boolean,
totpSecret?: string
}
3. Users Service (users.service.ts):
New Methods:
async updateProfile(userId: string, data: {
name?: string;
phone?: string
})
Features:
- Update name
- Update phone number
- Unique phone validation
- Error handling for duplicate phone
4. Users Controller (users.controller.ts):
New Endpoints:
// Update user profile
PUT /api/users/profile
Body: { name?: string, phone?: string }
Auth: Required
5. Auth Service (auth.service.ts):
Updated Methods:
Login Flow:
async login(email: string, password: string) {
// ... authentication
// Check if OTP required
const requiresOtp = user.otpEmailEnabled ||
user.otpWhatsappEnabled ||
user.otpTotpEnabled;
if (requiresOtp) {
// Send email OTP if enabled
if (user.otpEmailEnabled) {
await this.otpService.sendEmailOtp(user.id);
}
// Send WhatsApp OTP if enabled (LIVE mode)
if (user.otpWhatsappEnabled) {
await this.otpService.sendWhatsappOtp(user.id, 'live');
}
return {
requiresOtp: true,
availableMethods: {
email: user.otpEmailEnabled,
whatsapp: user.otpWhatsappEnabled,
totp: user.otpTotpEnabled,
},
tempToken: this.generateTempToken(user.id, user.email),
};
}
}
Google OAuth Flow:
- Same logic as login
- Always updates avatar from Google
- Sends WhatsApp OTP if enabled
OTP Verification:
async verifyOtpAndLogin(
tempToken: string,
otpCode: string,
method: 'email' | 'whatsapp' | 'totp'
) {
// ... verify temp token
if (method === 'whatsapp') {
const isValid = await this.otpService.verifyWhatsappOtpForLogin(
userId,
otpCode
);
if (!isValid) {
throw new UnauthorizedException('Invalid WhatsApp OTP');
}
}
// ... generate full JWT
}
📝 Mode Parameter Usage:
Email OTP:
mode: "test"- For setup in Profile page (logs to console)mode: "live"- For login page (sends actual email)
WhatsApp OTP:
mode: "checknumber"- Check if number is registered on WhatsAppmode: "test"- For setup in Profile page (logs to console)mode: "live"- For login page (sends actual WhatsApp message)
🔄 Complete Flow:
Setup WhatsApp OTP (Profile Page):
- User enters phone number
- Frontend calls
POST /api/users/profilewith{ phone: "+1234567890" } - Frontend calls
POST /api/otp/whatsapp/checkwith{ phone: "+1234567890" } - If valid, frontend calls
POST /api/otp/whatsapp/sendwith{ mode: "test" } - Backend sends OTP via webhook with
mode: "test" - User enters OTP code
- Frontend calls
POST /api/otp/whatsapp/verifywith{ code: "123456" } - WhatsApp OTP enabled!
Login with WhatsApp OTP:
- User logs in with email/password or Google
- Backend detects
otpWhatsappEnabled: true - Backend calls
sendWhatsappOtp(userId, 'live') - Webhook receives request with
mode: "live" - User receives WhatsApp message
- User enters code on OTP page
- Frontend calls
POST /api/auth/verify-otpwith{ method: "whatsapp", code: "123456" } - Login successful!
🎯 Next Steps:
Frontend Implementation (TODO):
- ✅ Update Profile page to include phone number field
- ✅ Add WhatsApp OTP setup UI
- ✅ Add phone number validation
- ✅ Add "Check Number" button
- ✅ Update OTP verification page to support WhatsApp
- ✅ Restore original auth UI design from Git
n8n Webhook Configuration (TODO):
- Update webhook to handle
method: "whatsapp" - Handle
mode: "checknumber"- check if number is registered - Handle
mode: "test"- log to console or test endpoint - Handle
mode: "live"- send actual WhatsApp message - Return proper response format
📊 API Summary:
| Endpoint | Method | Auth | Body | Purpose |
|---|---|---|---|---|
/api/users/profile |
PUT | ✅ | { phone } |
Update phone |
/api/otp/whatsapp/check |
POST | ✅ | { phone } |
Check number |
/api/otp/whatsapp/send |
POST | ✅ | { mode } |
Send OTP |
/api/otp/whatsapp/verify |
POST | ✅ | { code } |
Verify & enable |
/api/otp/whatsapp/disable |
POST | ✅ | - | Disable |
/api/otp/status |
GET | ✅ | - | Get status |
/api/auth/verify-otp |
POST | - | { tempToken, code, method } |
Login verify |
✅ Files Modified:
Backend:
prisma/schema.prisma- Added phone and otpWhatsappEnabledsrc/otp/otp.service.ts- Added WhatsApp methodssrc/otp/otp.controller.ts- Added WhatsApp endpointssrc/users/users.service.ts- Added updateProfilesrc/users/users.controller.ts- Added PUT /profilesrc/auth/auth.service.ts- Updated login/OAuth flows
Database:
- Migration:
20251010132022_add_phone_and_whatsapp_otp
🎉 Status:
✅ Backend Complete - All WhatsApp OTP endpoints implemented
✅ Database Updated - Phone field and WhatsApp OTP flag added
✅ Google Avatar Fixed - Always updates from Google profile
⏳ Frontend Pending - Need to add UI components
⏳ Auth UI Pending - Need to restore original design from Git
Backend is ready for WhatsApp OTP! Frontend implementation next. 🚀