shanshanzhong e0b2be2058
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m36s
x
2026-03-30 21:05:21 -07:00

5.7 KiB

name, description, author, version, date
name description author version date
soft-delete-relogin-consistency Fix for missing auth/identity records after account deletion + device re-login. Use when: (1) User deletes account but device records are intentionally kept (e.g., to prevent trial abuse), (2) Re-login via device succeeds but user appears to have wrong identity type, (3) Frontend shows incorrect UI because auth_methods or similar identity records are empty/wrong after re-login, (4) Soft-deleted records cause stale cache entries that misrepresent user state. Covers GORM soft-delete, device-based auth, cache invalidation after re-creation. Codex 1.0.0 2026-03-11

Soft-Delete + Re-Login Auth Consistency

Problem

When a system uses soft-delete for auth/identity records during account deletion but intentionally keeps primary records (like device records) for abuse prevention, re-login flows may succeed at the "find existing record" step but fail to re-create the soft-deleted identity records. This causes the user to exist in an inconsistent state where they're authenticated but missing critical identity metadata.

Context / Trigger Conditions

  • Account deletion (注销) soft-deletes auth_methods (or equivalent identity records)
  • Device/hardware records are intentionally kept to prevent trial reward abuse
  • Device-based re-login finds existing device record -> reuses old user_id
  • But the "device found" code path skips identity record creation (only the "device not found" registration path creates them)
  • Result: User is logged in but auth_methods is empty or missing the expected type
  • Frontend UI breaks because it relies on auth_methods[0].auth_type to determine login mode and show/hide UI elements

Symptoms

  • Buttons or UI elements that should be hidden for device-only users appear after account deletion + re-login
  • API returns user info with empty or unexpected auth_methods array
  • isDeviceLogin() or similar identity checks return wrong results
  • Cache returns stale user data even after re-login

Solution

Step 1: Identify the re-login code path

Find the "device found" branch in the login logic. This is the code path that runs when a device record already exists (as opposed to the registration path).

Step 2: Add identity record existence check

After finding the user via device record, check if the expected identity record exists:

// After finding user via existing device record
hasDeviceAuth := false
for _, am := range userInfo.AuthMethods {
    if am.AuthType == "device" && am.AuthIdentifier == req.Identifier {
        hasDeviceAuth = true
        break
    }
}
if !hasDeviceAuth {
    // Re-create the soft-deleted auth record
    authMethod := &user.AuthMethods{
        UserId:         userInfo.Id,
        AuthType:       "device",
        AuthIdentifier: req.Identifier,
        Verified:       true,
    }
    if createErr := db.Create(authMethod).Error; createErr != nil {
        log.Error("re-create auth method failed", err)
    } else {
        // CRITICAL: Clear user cache so subsequent reads return updated data
        _ = userModel.ClearUserCache(ctx, userInfo)
    }
}

Step 3: Ensure cache invalidation

After re-creating the identity record, clear the user cache. This is critical because cached user data (with Preload("AuthMethods")) will still show the old empty state until the cache is invalidated.

Step 4: Verify GORM soft-delete behavior

GORM's soft-delete (deleted_at IS NULL filter) means:

  • Preload("AuthMethods") will NOT return soft-deleted records
  • db.Create() will create a NEW record (not undelete the old one)
  • The old soft-deleted record remains in the database (harmless)

Verification

  1. Delete account (注销)
  2. Re-login via device
  3. Call user info API - verify auth_methods contains the device type
  4. Check frontend UI - verify device-specific UI state is correct

Example

Before fix:

1. User has auth_methods: [device_A, email_A]
2. User deletes account -> auth_methods all soft-deleted
3. Device record kept (abuse prevention)
4. User re-logins via same device
5. FindOneDeviceByIdentifier finds device -> reuses user_id
6. FindOne returns user with AuthMethods=[] (soft-deleted, filtered out)
7. Frontend: isDeviceLogin() = false (no auth_methods) -> shows wrong buttons

After fix:

1-4. Same as above
5. FindOneDeviceByIdentifier finds device -> reuses user_id
6. FindOne returns user with AuthMethods=[]
7. NEW: Detects missing device auth_method, re-creates it, clears cache
8. Frontend: isDeviceLogin() = true -> correct UI

Notes

  • This pattern applies broadly to any system where:
    • Account deletion removes identity records but keeps usage records
    • Re-login can succeed via the usage records
    • UI/business logic depends on the identity records existing
  • The "don't delete device records" design is intentional for preventing abuse (e.g., users repeatedly deleting and re-creating accounts to get trial rewards)
  • Cache invalidation is the most commonly missed step - without it, the fix appears to not work because cached data is served until TTL expires
  • Consider whether Unscoped() (GORM) should be used to also query soft-deleted records, or whether re-creation is the better approach (usually re-creation is cleaner as it creates a fresh record with correct timestamps)
  • Cache key dependency chains: When ClearUserCache depends on AuthMethods to generate email cache keys, capture auth_methods BEFORE deletion, then explicitly clear derived cache keys after the transaction
  • Family ownership transfer: When an owner exits a shared resource group, transfer ownership to a remaining member instead of dissolving the group