UsersController.createUser checks for duplicate emails against current and soft-deleted users:
count({ where: { email, deleted: true } }) triggers "This email belongs to a deleted user, you must restore the user instead of creating a new one"
count({ where: { email } }) triggers "Email already exists"
There's no history table or history check. If an admin changes User A's email from foo@x.com to bar@x.com via UsersController.updateUser or ProfilesController.updateProfileById, foo@x.com becomes fully free to reassign to a new User B via create-user or invite. Delete already handles this correctly via soft-delete, but change does not — deleted users keep their emailHash reserved, changed users release it.
Consequence: if User A still has access to that mailbox they'll receive emails intended for the new User B (registration link, reset link, notifications). Password-reset links for the old address become invalid, but any pending or previously-received emails are still readable. Social engineering and info-leak vector.
Auth0 and similar providers keep an email-history table specifically to prevent this. Modern guidance is split on whether to block reuse outright or allow it after a cooldown (e.g. 90 days), so filing as a question — worth a team decision on whether we want history enforcement in the health context, or whether accepting the current behaviour as a known trade-off is fine.
Related to #929 which is about notification on email change, and #928 on reset completion.
Related files: UsersController.createUser (lines ~187-197), UsersController.updateUser, ProfilesController.updateProfileById.
UsersController.createUserchecks for duplicate emails against current and soft-deleted users:count({ where: { email, deleted: true } })triggers "This email belongs to a deleted user, you must restore the user instead of creating a new one"count({ where: { email } })triggers "Email already exists"There's no history table or history check. If an admin changes User A's email from
foo@x.comtobar@x.comviaUsersController.updateUserorProfilesController.updateProfileById,foo@x.combecomes fully free to reassign to a new User B via create-user or invite. Delete already handles this correctly via soft-delete, but change does not — deleted users keep their emailHash reserved, changed users release it.Consequence: if User A still has access to that mailbox they'll receive emails intended for the new User B (registration link, reset link, notifications). Password-reset links for the old address become invalid, but any pending or previously-received emails are still readable. Social engineering and info-leak vector.
Auth0 and similar providers keep an email-history table specifically to prevent this. Modern guidance is split on whether to block reuse outright or allow it after a cooldown (e.g. 90 days), so filing as a question — worth a team decision on whether we want history enforcement in the health context, or whether accepting the current behaviour as a known trade-off is fine.
Related to #929 which is about notification on email change, and #928 on reset completion.
Related files:
UsersController.createUser(lines ~187-197),UsersController.updateUser,ProfilesController.updateProfileById.