diff --git a/clients/dashboard/src/pages/settings/profile.tsx b/clients/dashboard/src/pages/settings/profile.tsx index 02776556d8..326a94ed66 100644 --- a/clients/dashboard/src/pages/settings/profile.tsx +++ b/clients/dashboard/src/pages/settings/profile.tsx @@ -140,8 +140,8 @@ export function ProfileSettings() { mutationFn: (url: string | null) => setProfileImage(url), onSuccess: async () => { toast.success("Profile image updated"); - // Setting the image is a second write to the same row, so ASP.NET Identity rotates the - // concurrency stamp and the tag this form is holding is spent. Adopting the new version (which + // The avatar URL is one of the fields the profile version hashes, so setting the image moves + // the tag and the one this form is holding is spent. Adopting the new version (which // also refreshes the cache, so the topbar avatar still updates) keeps the next save from // answering 412 and telling the user someone else edited their profile. await adoptCurrentVersion(); diff --git a/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs b/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs index 66b5d971d3..2c2f21e1ce 100644 --- a/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs +++ b/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs @@ -27,7 +27,9 @@ internal static RouteHandlerBuilder MapGetMeEndpoint(this IEndpointRouteBuilder // The profile is a full-representation resource: PUT /profile rewrites every field, so // a caller editing a stale copy would blank whatever changed meanwhile. Publishing the // profile version as a strong ETag lets that caller echo it back in If-Match and have - // the server reject the stale write. + // the server reject the stale write. The tag covers only the fields PUT /profile writes + // (not email, status or the host-prefixed avatar URL in this body), so it is a write + // precondition only: never use it to answer a conditional GET with 304. response.Headers.ETag = new EntityTagHeaderValue($"\"{profile.ProfileVersion}\"", isWeak: false).ToString(); return TypedResults.Ok(profile);