- 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
134 lines
3.0 KiB
Markdown
134 lines
3.0 KiB
Markdown
# 🔧 Resend OTP Fix - Public Endpoint
|
|
|
|
## 🐛 **Problem:**
|
|
Resend OTP endpoint was failing with `ERR_CONNECTION_REFUSED` because:
|
|
1. The entire `OtpController` has `@UseGuards(AuthGuard)` at class level
|
|
2. The resend endpoint requires a temp token, not a full JWT
|
|
3. AuthGuard was rejecting the request
|
|
|
|
## ✅ **Solution:**
|
|
Made the resend endpoint public by:
|
|
1. Adding `@Public()` decorator
|
|
2. Updating `AuthGuard` to respect public routes
|
|
3. Endpoint verifies temp token manually
|
|
|
|
---
|
|
|
|
## 📝 **Changes Made:**
|
|
|
|
### 1. **Updated OtpController** (`otp.controller.ts`):
|
|
|
|
```typescript
|
|
// Added Public decorator
|
|
export const IS_PUBLIC_KEY = 'isPublic';
|
|
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
|
|
|
// Marked resend endpoint as public
|
|
@Public()
|
|
@Post('email/resend')
|
|
async resendEmailOtp(@Body() body: { tempToken: string }) {
|
|
try {
|
|
// Verify temp token manually
|
|
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');
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. **Updated AuthGuard** (`auth.guard.ts`):
|
|
|
|
```typescript
|
|
import { Reflector } from '@nestjs/core';
|
|
|
|
@Injectable()
|
|
export class AuthGuard extends PassportAuthGuard('jwt') {
|
|
constructor(private reflector: Reflector) {
|
|
super();
|
|
}
|
|
|
|
canActivate(context: ExecutionContext) {
|
|
// Check if route is marked as public
|
|
const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [
|
|
context.getHandler(),
|
|
context.getClass(),
|
|
]);
|
|
|
|
if (isPublic) {
|
|
return true; // Skip authentication
|
|
}
|
|
|
|
return super.canActivate(context);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 **How It Works:**
|
|
|
|
1. **Frontend** calls `/api/otp/email/resend` with temp token
|
|
2. **AuthGuard** checks for `@Public()` decorator
|
|
3. **If public**: Skips JWT validation, allows request through
|
|
4. **Endpoint** manually verifies temp token
|
|
5. **Extracts** userId from temp token
|
|
6. **Sends** OTP email
|
|
7. **Returns** success
|
|
|
|
---
|
|
|
|
## 🧪 **Testing:**
|
|
|
|
### **Test Resend:**
|
|
1. ✅ Login with email OTP enabled
|
|
2. ✅ On OTP page, wait 30 seconds
|
|
3. ✅ Click "Resend Code"
|
|
4. ✅ **Should work now!**
|
|
5. ✅ Check console for new OTP code
|
|
6. ✅ Enter code and login
|
|
|
|
---
|
|
|
|
## ⚠️ **Current Status:**
|
|
|
|
Backend needs to restart to apply changes. If backend is not responding:
|
|
|
|
```bash
|
|
# Kill existing process
|
|
pkill -f "nest start"
|
|
|
|
# Restart
|
|
cd apps/api
|
|
npm run dev
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 **Files Modified:**
|
|
|
|
1. **`apps/api/src/otp/otp.controller.ts`**
|
|
- Added `Public` decorator
|
|
- Marked `email/resend` as public
|
|
- Injected `JwtService`
|
|
|
|
2. **`apps/api/src/auth/auth.guard.ts`**
|
|
- Injected `Reflector`
|
|
- Added public route check
|
|
- Skip auth for public routes
|
|
|
|
3. **`apps/web/src/components/pages/OtpVerification.tsx`**
|
|
- Already updated to use `/api/otp/email/resend`
|
|
|
|
---
|
|
|
|
**Once backend restarts, resend should work!** 🚀
|