You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #487 (merged, closed #474) narrowed profile update, password change and reset request to compare-and-swap column writes. Other writers still read a user row, mutate it and save the whole row, so a stale read can overwrite concurrent MFA fields (or restore an old password hash).
Remaining whole-row writers on main:
ConfirmPasswordReset: internal/auth/service_password.go:398, 406, 418. Worst case: a reset-token holder whose read precedes a concurrent MFA enrollment wipes MFA and sets a password they know.
Recovery-code use at login: internal/auth/service.go:255. Can also restore an old password hash over a concurrent password change.
Also: #487's race tests call the service directly, not the HTTP API.
Suggested direction: reuse the narrow CAS write pattern from UpdateUserCredentials in internal/auth/store_postgres_credentials.go, with a column-scoped write per caller.
Acceptance: none of these paths writes MFA fields or the password hash from a stale read; race tests cover each path, at least one through the HTTP API.
PR #487 (merged, closed #474) narrowed profile update, password change and reset request to compare-and-swap column writes. Other writers still read a user row, mutate it and save the whole row, so a stale read can overwrite concurrent MFA fields (or restore an old password hash).
Remaining whole-row writers on main:
ConfirmPasswordReset: internal/auth/service_password.go:398, 406, 418. Worst case: a reset-token holder whose read precedes a concurrent MFA enrollment wipes MFA and sets a password they know.UpdateUser: internal/auth/service_user.go:359.Also: #487's race tests call the service directly, not the HTTP API.
Suggested direction: reuse the narrow CAS write pattern from
UpdateUserCredentialsin internal/auth/store_postgres_credentials.go, with a column-scoped write per caller.Acceptance: none of these paths writes MFA fields or the password hash from a stale read; race tests cover each path, at least one through the HTTP API.