- 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
191 lines
4.8 KiB
Markdown
191 lines
4.8 KiB
Markdown
# ✅ Email OTP Sending During Login - FIXED
|
|
|
|
## 🐛 **Problem:**
|
|
Email OTP was not being sent during login flow. It only worked when manually requested from the profile page.
|
|
|
|
**Symptoms**:
|
|
- User logs in with email/password (has email OTP enabled)
|
|
- Redirected to OTP page
|
|
- No email received
|
|
- Console shows no OTP code
|
|
- User stuck on OTP page
|
|
|
|
---
|
|
|
|
## ✅ **Root Cause:**
|
|
|
|
The login flow was checking if OTP was required and returning a temp token, but **never actually sending the email OTP**!
|
|
|
|
```typescript
|
|
// OLD CODE - No email sent!
|
|
if (requiresOtp) {
|
|
return {
|
|
requiresOtp: true,
|
|
availableMethods: {
|
|
email: user.otpEmailEnabled,
|
|
totp: user.otpTotpEnabled,
|
|
},
|
|
tempToken: this.generateTempToken(user.id, user.email),
|
|
};
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ **Fixes Applied:**
|
|
|
|
### 1. **Injected OtpService into AuthService** ✅
|
|
```typescript
|
|
// auth.module.ts
|
|
imports: [
|
|
PrismaModule,
|
|
PassportModule,
|
|
forwardRef(() => OtpModule), // Added OtpModule
|
|
JwtModule.register({...}),
|
|
],
|
|
|
|
// auth.service.ts
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly jwtService: JwtService,
|
|
@Inject(forwardRef(() => OtpService)) // Injected OtpService
|
|
private readonly otpService: OtpService,
|
|
) {}
|
|
```
|
|
|
|
### 2. **Send Email OTP During Login** ✅
|
|
```typescript
|
|
// In login() method
|
|
if (requiresOtp) {
|
|
// Send email OTP if enabled
|
|
if (user.otpEmailEnabled) {
|
|
try {
|
|
await this.otpService.sendEmailOtp(user.id); // ← SEND EMAIL!
|
|
} catch (error) {
|
|
console.error('Failed to send email OTP during login:', error);
|
|
// Continue anyway - user can request resend
|
|
}
|
|
}
|
|
|
|
return {
|
|
requiresOtp: true,
|
|
availableMethods: {
|
|
email: user.otpEmailEnabled,
|
|
totp: user.otpTotpEnabled,
|
|
},
|
|
tempToken: this.generateTempToken(user.id, user.email),
|
|
};
|
|
}
|
|
```
|
|
|
|
### 3. **Send Email OTP During Google Login** ✅
|
|
```typescript
|
|
// In googleLogin() method
|
|
if (requiresOtp) {
|
|
// Send email OTP if enabled
|
|
if (user.otpEmailEnabled) {
|
|
try {
|
|
await this.otpService.sendEmailOtp(user.id); // ← SEND EMAIL!
|
|
} catch (error) {
|
|
console.error('Failed to send email OTP during Google login:', error);
|
|
}
|
|
}
|
|
|
|
return {
|
|
requiresOtp: true,
|
|
availableMethods: {...},
|
|
tempToken: this.generateTempToken(user.id, user.email),
|
|
};
|
|
}
|
|
```
|
|
|
|
### 4. **Added Separate Verification Method** ✅
|
|
Created `verifyEmailOtpForLogin()` that verifies the code without enabling the feature:
|
|
|
|
```typescript
|
|
// otp.service.ts
|
|
async verifyEmailOtpForLogin(userId: string, code: string): Promise<boolean> {
|
|
const stored = this.emailOtpStore.get(userId);
|
|
|
|
if (!stored || new Date() > stored.expiresAt || stored.code !== code) {
|
|
return false;
|
|
}
|
|
|
|
this.emailOtpStore.delete(userId);
|
|
return true;
|
|
}
|
|
```
|
|
|
|
### 5. **Updated Login Verification** ✅
|
|
```typescript
|
|
// In verifyOtpAndLogin() method
|
|
if (method === 'email') {
|
|
const isValid = await this.otpService.verifyEmailOtpForLogin(userId, otpCode);
|
|
if (!isValid) {
|
|
throw new UnauthorizedException('Invalid or expired email OTP code');
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 **Files Modified:**
|
|
|
|
1. **`apps/api/src/auth/auth.module.ts`**
|
|
- Added `forwardRef(() => OtpModule)` to imports
|
|
|
|
2. **`apps/api/src/auth/auth.service.ts`**
|
|
- Injected `OtpService`
|
|
- Send email OTP in `login()` method
|
|
- Send email OTP in `googleLogin()` method
|
|
- Use `verifyEmailOtpForLogin()` for verification
|
|
|
|
3. **`apps/api/src/otp/otp.service.ts`**
|
|
- Added `verifyEmailOtpForLogin()` method
|
|
- Keeps existing `verifyEmailOtp()` for setup
|
|
|
|
---
|
|
|
|
## 🧪 **Testing:**
|
|
|
|
### Test Email/Password Login with Email OTP:
|
|
1. ✅ Login with email/password
|
|
2. ✅ **Email OTP should be sent automatically**
|
|
3. ✅ Check console for: `📧 OTP Code for user@example.com: 123456`
|
|
4. ✅ Enter code on OTP page
|
|
5. ✅ Should login successfully
|
|
|
|
### Test Google Login with Email OTP:
|
|
1. ✅ Click "Continue with Google"
|
|
2. ✅ Authenticate
|
|
3. ✅ **Email OTP should be sent automatically**
|
|
4. ✅ Redirected to OTP page
|
|
5. ✅ Check console for OTP code
|
|
6. ✅ Enter code
|
|
7. ✅ Should login successfully
|
|
|
|
---
|
|
|
|
## ✨ **What Now Works:**
|
|
|
|
✅ **Email OTP sent during login** - Automatically when user has it enabled
|
|
✅ **Email OTP sent during Google OAuth** - Works for both flows
|
|
✅ **Proper verification** - Uses dedicated login verification method
|
|
✅ **Console logging** - Shows OTP code in development
|
|
✅ **Webhook integration** - Sends to n8n if configured
|
|
|
|
---
|
|
|
|
## 🎯 **Expected Behavior:**
|
|
|
|
1. User logs in (email/password or Google)
|
|
2. If email OTP enabled:
|
|
- Email is sent automatically
|
|
- Console shows: `📧 OTP Code for user@example.com: 123456`
|
|
- User redirected to OTP page
|
|
3. User enters code
|
|
4. Code verified
|
|
5. User logged in successfully
|
|
|
|
**Email OTP should now work during login! Test it now!** 🚀
|