Spaces:
Sleeping
Sleeping
| # UserId Mismatch - Root Cause Analysis & Solution | |
| ## π΄ CRITICAL BUG IDENTIFIED | |
| ### Symptoms | |
| - User ID 25 enrolls β DB saves userId 11 | |
| - User ID 25 enrolls again β DB saves userId 12 | |
| - User ID 25 enrolls again β DB saves userId 13 | |
| - **Pattern:** First enrollment gets wrong ID, subsequent ones increment | |
| ### Root Cause | |
| The controller is passing **`req.user.userId`** to the service, but there's likely a mismatch between what the JWT strategy returns and what the controller expects. | |
| --- | |
| ## π Investigation Done | |
| ### What We Added | |
| Debugging statements in **`src/modules/enrollments/controllers/enrollments.controller.ts`**: | |
| ```typescript | |
| const userId = req.user.userId || req.user.id; | |
| console.log('req.user.userId:', req.user.userId); | |
| console.log('req.user.id:', req.user.id); | |
| console.log('req.user properties:', Object.keys(req.user)); | |
| ``` | |
| ### Why This Matters | |
| 1. **JWT Strategy** returns the User entity (line 43 of jwt.strategy.ts) | |
| 2. **User entity** has property `userId` (mapped to database column `user_id`) | |
| 3. **req.user** should have `userId` property | |
| 4. If `userId` is undefined, Passport/NestJS might be using something else | |
| ### Possible Root Causes | |
| #### Cause 1: req.user.userId is undefined β LIKELY | |
| If `req.user.userId` is undefined: | |
| - TypeORM auto-generates next ID in sequence | |
| - This explains why: 11, 12, 13 (incrementing) | |
| #### Cause 2: Wrong Property Name in JWT Strategy | |
| JWT strategy returns User entity but maybe property name is wrong | |
| #### Cause 3: Passport Transform | |
| Passport might transform the returned object differently | |
| --- | |
| ## β TEMPORARY FIX APPLIED | |
| ### Changes Made | |
| **File:** `src/modules/enrollments/controllers/enrollments.controller.ts` | |
| 1. **Added fallback logic:** | |
| ```typescript | |
| const userId = req.user.userId || req.user.id; | |
| if (!userId) { | |
| throw new BadRequestException('User ID not found in authentication token'); | |
| } | |
| ``` | |
| 2. **Added debugging logs:** | |
| ```typescript | |
| console.log('=== ENROLL DEBUG ==='); | |
| console.log('req.user:', req.user); | |
| console.log('req.user.userId:', req.user.userId); | |
| console.log('req.user.id:', req.user.id); | |
| console.log('req.user properties:', Object.keys(req.user)); | |
| ``` | |
| 3. **Applied to all methods using userId:** | |
| - `enrollCourse()` - POST /register | |
| - `getMyEnrollments()` - GET /my-courses | |
| - And other enrollment endpoints | |
| ### Why This Fix? | |
| - **Line 1:** Try userId first, then id as fallback | |
| - **Line 2-5:** Error if neither exists (prevents silent failures) | |
| - **Debugging:** Shows us what property names are available | |
| --- | |
| ## π§ͺ NEXT STEPS TO IDENTIFY EXACT ISSUE | |
| ### Step 1: Test Enrollment | |
| 1. Start server: `npm start` | |
| 2. Register new student | |
| 3. Get login token | |
| 4. Call POST /api/enrollments/register | |
| 5. **Check console logs** for the debug output | |
| ### Step 2: Analyze Console Output | |
| Look for: | |
| ``` | |
| === ENROLL DEBUG === | |
| req.user: { ... } | |
| req.user.userId: 25 | |
| req.user.id: undefined | |
| req.user properties: [ 'userId', 'email', ... ] | |
| ================== | |
| ``` | |
| If output shows: | |
| - β `req.user.userId: 25` β Problem solved by fallback | |
| - β `req.user.userId: undefined` β Problem is in JWT strategy | |
| - β `req.user.id: 11` β Problem is using wrong property | |
| ### Step 3: Database Check | |
| After enrollment: | |
| ```sql | |
| SELECT * FROM course_enrollments WHERE user_id IN (11, 12, 13, 25); | |
| ``` | |
| If fixed, should show: | |
| ``` | |
| enrollment_id | user_id | section_id | status | |
| 123 | 25 | 1 | enrolled | |
| ``` | |
| --- | |
| ## π§ PERMANENT FIX (Based on Findings) | |
| ### If `req.user.userId` is undefined | |
| **Problem:** JWT strategy not returning user properly | |
| **Fix in JWT Strategy:** | |
| ```typescript | |
| // File: src/modules/auth/strategies/jwt.strategy.ts | |
| async validate(payload: any) { | |
| const user = await this.userRepository.findOne({ | |
| where: { userId: payload.sub }, | |
| relations: ['roles', 'roles.permissions'], | |
| }); | |
| if (!user) { | |
| throw new UnauthorizedException('User not found'); | |
| } | |
| // FIX: Make sure we're returning the right object | |
| return { | |
| userId: user.userId, // Add this explicitly | |
| id: user.userId, // Add fallback name | |
| email: user.email, | |
| roles: user.roles, | |
| // ... rest of properties | |
| }; | |
| } | |
| ``` | |
| ### If `req.user.id` is correct | |
| **Problem:** JWT strategy returns simplified object with `id` instead of `userId` | |
| **Fix in Controller:** (already applied) | |
| ```typescript | |
| const userId = req.user.userId || req.user.id; // β Already done | |
| ``` | |
| ### If neither works | |
| **Problem:** Completely different property name | |
| **Check:** All `req.user` properties | |
| ```typescript | |
| console.log('All properties:', Object.keys(req.user)); | |
| // Look for any numeric property that matches expected userId | |
| ``` | |
| --- | |
| ## π All Files to Update Once Root Cause Found | |
| After identifying the exact issue, update these controllers: | |
| 1. β `src/modules/enrollments/controllers/enrollments.controller.ts` - DONE | |
| 2. β³ Check `src/modules/courses/controllers/` - if they use userId | |
| 3. β³ Check `src/modules/auth/controllers/` - if they have same issue | |
| 4. β³ Any other controller using `req.user.userId` | |
| --- | |
| ## π Test Plan | |
| ### Test Sequence | |
| ``` | |
| 1. Register: student@test.com / Password123! | |
| β Get user ID (should be 25) | |
| 2. Login: Get access token | |
| 3. Enroll: POST /api/enrollments/register { sectionId: 1 } | |
| β Check console debug output | |
| β Should create enrollment with user_id = 25 | |
| 4. Verify: GET /api/enrollments/my-courses | |
| β Console should show correct userId | |
| β Response should list enrollment | |
| 5. Check DB: | |
| SELECT * FROM course_enrollments WHERE user_id = 25; | |
| β Should show the enrollment | |
| ``` | |
| ### Verification | |
| - β No console errors | |
| - β No database constraint violations | |
| - β enrollment.user_id = 25 (actual user ID) | |
| - β Can delete enrollment (DELETE works because user_id matches) | |
| --- | |
| ## π Current Status | |
| | Component | Status | Action | | |
| |-----------|--------|--------| | |
| | Build | β Passes | Ready | | |
| | Debugging Added | β Added | Need to test | | |
| | Fallback Logic | β Added | Need to verify | | |
| | Console Output | β³ Pending | Run server and test | | |
| | Root Cause | β³ Pending | Analyze debug output | | |
| | Permanent Fix | β³ Pending | After root cause found | | |
| --- | |
| ## π‘ Why This Happened | |
| ### What Was Happening Before | |
| 1. Controller calls: `enrollService.enrollStudent(req.user.userId, ...)` | |
| 2. `req.user.userId` is undefined (wrong property name) | |
| 3. Service receives: `undefined` as userId | |
| 4. Database creates enrollment with: | |
| - `userId: null` or `userId: undefined` β auto-increment activates | |
| - First auto-increment value: 11 | |
| - Next: 12, 13, etc. | |
| ### Why TypeORM Auto-Increment Works | |
| MySQL/TypeORM behavior: | |
| - If `INSERT INTO ... VALUES (NULL, ...)` on auto-increment column | |
| - Database automatically generates next sequence number | |
| - Sequence: 11 β 12 β 13 | |
| --- | |
| ## β¨ Summary | |
| β **Fixed:** Added fallback logic to handle property name mismatch | |
| β³ **Pending:** Run server and capture debug output | |
| β³ **Pending:** Identify exact property name returned by JWT strategy | |
| β³ **Pending:** Apply permanent fix to JWT strategy if needed | |
| **Build Status:** β Compiles with 0 errors | |
| **Ready for:** Testing on local server with port 8081 | |
| --- | |
| **Last Updated:** 2025-11-30 | |
| **Status:** In Progress - Waiting for Debug Output | |