Skip to content

Commit 7d91f3d

Browse files
committed
fix: narrow optimistic locking to array fields only
1 parent 4c42259 commit 7d91f3d

1 file changed

Lines changed: 10 additions & 3 deletions

File tree

src/Routers/UsersRouter.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,22 @@ export class UsersRouter extends ClassesRouter {
287287
// If we have some new validated authData update directly
288288
if (validatedAuthData && Object.keys(validatedAuthData).length) {
289289
const query = { objectId: user.objectId };
290-
// Optimistic locking: include the original authData state in the WHERE clause
290+
// Optimistic locking: include the original array fields in the WHERE clause
291291
// for providers whose data is being updated. This prevents concurrent requests
292292
// from both succeeding when consuming single-use tokens (e.g. MFA recovery codes).
293+
// Only array fields need locking — element removal is vulnerable to TOCTOU;
294+
// scalar fields are simply overwritten and don't have concurrency issues.
293295
if (user.authData) {
294296
for (const provider of Object.keys(validatedAuthData)) {
295297
const original = user.authData[provider];
296-
if (original && JSON.stringify(original) !== JSON.stringify(validatedAuthData[provider])) {
298+
if (original && typeof original === 'object') {
297299
for (const [field, value] of Object.entries(original)) {
298-
query[`authData.${provider}.${field}`] = value;
300+
if (
301+
Array.isArray(value) &&
302+
JSON.stringify(value) !== JSON.stringify(validatedAuthData[provider]?.[field])
303+
) {
304+
query[`authData.${provider}.${field}`] = value;
305+
}
299306
}
300307
}
301308
}

0 commit comments

Comments
 (0)