eduverse-backend / Documentation /USERID_MISMATCH_FIX.md
tarekdx3's picture
Enrollment Feature DONE AND TESTED SUCCESFULLY
7333f29
|
Raw
History Blame Contribute Delete
7.23 kB

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:

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:
const userId = req.user.userId || req.user.id;
if (!userId) {
  throw new BadRequestException('User ID not found in authentication token');
}
  1. Added debugging logs:
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));
  1. 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:

// 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)

const userId = req.user.userId || req.user.id;  // βœ… Already done

If neither works

Problem: Completely different property name

Check: All req.user properties

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