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
{{ message }}
Repository navigation
Profile ETag changes on a failed sign-in, so an open profile form gets a false 412 #1398
Since #1387, GET /identity/profile publishes the user's ConcurrencyStamp as the ETag, and PUT /identity/profile answers 412 when If-Match does not match it. The stamp is not a profile version, though: ASP.NET Identity rotates it on every write to the AspNetUsers row, and some of those writes have nothing to do with the profile.
What rotates it
Measured against the Docker Compose stack built from main plus the open i18n PRs (which don't touch sign-in or the stamp), reading the ETag between steps:
Step
ETag changed
Two reads, nothing in between
no
One sign-in with a wrong password
yes
Token refresh
no
A clean successful sign-in
no
A successful sign-in right after a failed one
yes (it resets AccessFailedCount)
A failed sign-in goes through UserManager.AccessFailedAsync, which increments AccessFailedCount and saves the user, and the save rotates the stamp.
Effect
A user has Settings > Profile open. Anyone types a wrong password for that account: a bot, a colleague, or the user themselves on another device. The user presses Save, gets 412, and sees "Profile changed elsewhere", but nothing in the profile changed. The dashboard handles 412 correctly by re-reading and asking for a deliberate re-save, so no data is lost. The false conflict is still wrong, and anyone who can guess the email can cause it on demand.
Options
Derive the ETag from the profile fields the PUT actually writes (first name, last name, phone, image, and locale once i18n lands), e.g. a hash, instead of from ConcurrencyStamp. Writes that don't touch the profile then leave the tag alone. The precondition check would compare that hash, and the store's own ConcurrencyStamp check still catches a lost race at save time.
Keep ConcurrencyStamp and document that security-counter writes cause a spurious 412. This is the smallest change, but the behaviour stays surprising.
I'd go with option 1. Happy to send the PR if you agree.
Agreed, option 1. Go ahead with the PR. Two notes: (1) the save-time ConcurrencyFailure → 412 mapping still fires if a failed sign-in lands between load and save — fine given the tiny window, but worth a comment; (2) include locale in the hash, and sequence it with the i18n stack however is easiest for you.
Activity
iammukeshm commentedon Sep 26, 2026
Agreed, option 1. Go ahead with the PR. Two notes: (1) the save-time
ConcurrencyFailure→ 412 mapping still fires if a failed sign-in lands between load and save — fine given the tiny window, but worth a comment; (2) include locale in the hash, and sequence it with the i18n stack however is easiest for you.fix(identity): keep the profile ETag stable across a failed sign-in (#…