File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff 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 }
You can’t perform that action at this time.
0 commit comments