bug: Post-migration bugs from UUID identity migration (#206) #220
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
PR: #219
Context
PR #219 migrated all user identity from
auth0_sub VARCHAR(255)touser_profiles.id UUID. The app has bugs after this migration. This issue documents all changes made for a debugging session.Architecture Change
auth0_subnow exists ONLY inuser_profilestable for JWT-to-profile resolution. All other tables referenceuser_profiles.idUUID viauser_idFK.Files Changed (38 files, 9 commits)
Migration SQL
backend/src/core/identity-migration/migrations/001_migrate_user_id_to_uuid.sql(NEW) - 5-phase migration: add UUID columns, backfill, admin restructure, constraints, drop/rename. Two patches already applied foruser_preferencesdedup/mixed-format rows.backend/src/_system/migrations/run-all.ts- Addedcore/identity-migrationto end of MIGRATION_ORDER.Auth Plugin (core change)
backend/src/core/plugins/auth.plugin.ts- RenameduserIdtoauth0Subfor JWT sub. AftergetOrCreate(auth0Sub), setsuserId = profile.id(UUID). Auth0 Management API calls useauth0Sub.updateEmail()andupdateEmailVerified()called withuserId(UUID).backend/src/core/plugins/admin-guard.plugin.ts- Query changed fromWHERE auth0_sub = $1toWHERE user_profile_id = $1. SELECT now returnsid, user_profile_idinstead ofauth0_sub.Admin System (full refactor)
backend/src/features/admin/domain/admin.types.ts-AdminUser.auth0Subreplaced withid+userProfileId. Revoke/reinstate/bulk requests useidinstead ofauth0Sub.backend/src/features/admin/data/admin.repository.ts-getAdminByAuth0Sub()replaced withgetAdminById()+getAdminByUserProfileId().createAdmin()acceptsuserProfileIdinstead ofauth0Sub.revokeAdmin()/reinstateAdmin()useid.mapRowToAdminUsermapsid+user_profile_id. RemovedupdateAuth0SubByEmail().backend/src/features/admin/domain/admin.service.ts- AddedgetAdminByUserProfileId().createAdmin()takesuserProfileId+createdByAdminId.revokeAdmin()/reinstateAdmin()takeid+ actor admin ID. RemovedlinkAdminAuth0Sub().backend/src/features/admin/api/admin.controller.ts-verifyAccessusesgetAdminByUserProfileId(userId)(removed email fallback + linkAdmin logic + debug logs).createAdminlooks upuser_profilesby email to get UUID. Revoke/reinstate get actor's admin record for admin ID. Response returnsid/userProfileIdinstead ofauth0Sub.backend/src/features/admin/api/users.controller.ts- All route params changed from:auth0Subto:userId. All methods useuserId(UUID) from params.promoteToAdminlooks up actor admin record.backend/src/features/admin/api/admin.routes.ts- Routes::auth0Sub->:id(admin routes),:auth0Sub->:userId(user routes).backend/src/features/admin/api/admin.validation.ts-adminAuth0SubSchema->adminIdSchemawith.uuid()validation. Bulk schemas useidsarray with.uuid().backend/src/features/admin/api/users.validation.ts-userAuth0SubSchema->userIdSchemawith.uuid()validation.User Profile (parameter type change)
backend/src/features/user-profile/data/user-profile.repository.ts- AddedgetById(id). Changed 15+ methods fromauth0Subparameter touserId(UUID):update,updateSubscriptionTier,deactivateUser,reactivateUser,adminUpdateProfile,updateEmailVerified,markOnboardingComplete,updateEmail,requestDeletion,cancelDeletion,hardDeleteUser,getUserWithAdminStatus,getUserVehiclesForAdmin. All SQL changed fromWHERE auth0_sub = $1toWHERE id = $1.listAllUsersJOIN changed:v.user_id = up.id,au.user_profile_id = up.id.mapRowToUserWithAdminStatus:isAdminchecksrow.admin_idinstead ofrow.admin_auth0_sub.backend/src/features/user-profile/domain/user-profile.service.ts- All methods changed fromauth0SubtouserId(UUID). UsesgetById()instead ofgetByAuth0Sub()for lookups.adminHardDeleteUsernow callsauth0ManagementClient.deleteUser(profile.auth0Sub)using profile's auth0Sub.backend/src/features/user-profile/api/user-profile.controller.ts-getProfileusesuserProfileRepository.getById(userId)instead ofgetOrCreateProfile(). All methods useuserIdfromuserContext. AddeduserProfileRepositoryas class field.getDeletionStatusalso changed to usegetById().Supporting Code
backend/src/features/audit-log/data/audit-log.repository.ts- JOIN changed fromup.auth0_subtoup.id.backend/src/features/backup/api/backup.controller.ts- UsesuserContext.userIdinstead ofauth0Sub.backend/src/features/ocr/api/ocr.controller.ts- All 7 endpoints changed from(request as any).user?.subtorequest.userContext?.userId.Frontend
frontend/src/features/admin/types/admin.types.ts-AdminUser.auth0Sub->id+userProfileId.frontend/src/features/admin/api/admin.api.ts-revokeAdmin/reinstateAdminuseid. Allusers.*methods useuserIdparam. RemovedencodeURIComponent()wrappers.frontend/src/features/admin/hooks/useAdmins.ts- Mutation functions useidinstead ofauth0Sub.frontend/src/features/admin/hooks/useUsers.ts- All mutation shapes useuserIdinstead ofauth0Sub. Query keys useuserId.frontend/src/pages/admin/AdminUsersPage.tsx- Alluser.auth0Subreferences ->user.id. Vehicle expansion, tier change, deactivate, reactivate, edit, promote, delete all useuser.id.frontend/src/features/admin/mobile/AdminUsersMobileScreen.tsx- Same changes as desktop:user.auth0Sub->user.idthroughout.Tests
backend/src/features/admin/tests/unit/admin.service.test.ts- Test mocks useid/userProfileIdinstead ofauth0Sub.backend/src/features/admin/tests/integration/admin.integration.test.ts- Updated for new admin schema and UUID params.backend/src/features/auth/tests/unit/auth.service.test.ts- AddeddeletionRequestedAt/deletionScheduledFormock fields.backend/src/features/auth/tests/integration/auth.integration.test.ts- Minor fixture update.backend/src/features/admin/tests/unit/admin.guard.test.ts- UUID test values.backend/src/features/audit-log/__tests__/audit-log.integration.test.ts- UUID test values.backend/src/core/middleware/require-tier.test.ts- UUID test value.backend/src/core/plugins/tests/tier-guard.plugin.test.ts- UUID test values.backend/src/features/fuel-logs/tests/fixtures/fuel-logs.fixtures.json- UUID userId.backend/src/features/maintenance/tests/fixtures/maintenance.fixtures.json- UUID userId.backend/src/features/stations/tests/integration/community-stations.api.test.ts- UUID test values.frontend/src/features/admin/__tests__/*- UUID test values.Key Invariants (post-migration)
userContext.userIdis always UUID after auth pluginrequest.user.subis always auth0_sub format (raw JWT)auth0_subonly exists inuser_profilestableadmin_usershas ownid UUID PK+user_profile_id UUID FK(not renamed to user_id)user_id UUIDreferencesuser_profiles.id/admin/admins/:id/...and/admin/users/:userId/...