Provide support conditional writes in Auth0 APIs

The Auth0 APIs do not provide any means of performing writes with 100% reliability - i.e., with no possible race conditions. Concurrency controls on the client application side can reduce the changes of race conditions on writes to Auth0, but they cannot completely eliminate them. The only way to eliminate them would be for Auth0 APIs to support conditional writes - e.g., ETag/If-Match headers, or update only if higher version number or later timestamp. ETag/If-Match headers requires a GET before update, but conditional update based on version number of timestamp allows updates to be performed without first performing a GET.

Consider an update to a user’s email address. We need to handle the email verification ourselves in the client application before updating Auth0 to ensure the email is verified before applied to the user’s account. Once the user verifies their email, we queue a request to update Auth0 (in the same database transaction, using the “transactional outbox” pattern). The outbox handler dequeues the message, updates Auth0 and deletes the message.

However, a user can queue two email changes quickly, which creates a race - the latest email change might be beaten by the previous email change. We can put a UUID on the database record that identifies the change and only process the outbox message if the change ID in the message matches that of the database record. That considerably reduces the risk of a race condition, but it doesn’t eliminate it. It can’t. It’s mathematically impossible to eliminate the race condition unless the Auth0 API supports some kind of conditional write.

For example:

  1. User changes email from A to B.
  2. User verifies B. The system enqueues a request to update Auth0 to B.
  3. The request handler checks the change ID on the request matches the database record. It does. It goes to make the change, but then the thread is suspended for some reason (e.g., very long GC pause).
  4. The request handler loses its database connection and releases the request message.
  5. Another server/pod picks up the request, checks the change ID is the same (it is), sends the request to Auth0 to change the email to B, clears the change ID field and deletes the request.
  6. The user changes their email from B to C.
  7. The user verifies C. The system enqueues a request to update Auth0 to C.
  8. The request handler picks up the request, checks the change ID is the same (it is), sends the request to Auth0 to change the email to C, clears the change ID field and deletes the request.
  9. The suspended thread on the original server/pod resumes and sends the request to Auth0 to update the email to B.

So there is currently no 100% reliable way to use the Auth0 API to perform writes.

I would hope that supporting conditional writes would be a relatively minor change that would be very impactful in terms of system reliability.

Hi @bill.poole

Welcome to the Auth0 Community!

You are asking whether the Auth0 Management API supports conditional writes, such as ETag/If-Match headers or version-based updates, so that concurrent updates to a user’s email cannot overwrite a newer change.

Summary: The Management API does not expose a documented conditional-write mechanism in the materials reviewed, so the practical protection is to use the API’s built-in uniqueness check and to avoid triggering a second write that resets verification state.

Root Cause: A write to PATCH /api/v2/users/{id} applies the last request it receives, and the API does not compare a version or timestamp before applying it. A delayed request from an earlier change can therefore arrive after a newer one, which matches the sequence you described.

Solution: Use the following controls to reduce the race window, then submit the conditional-write request through official channels.

  1. Rely on the duplicate-email check built into the API. If the new email already belongs to another user, PATCH /api/v2/users/{id} returns 400 Bad Request with errorCode: auth0_idp_error and leaves the original profile unchanged. You do not need a separate read-then-write lookup for that case.
  2. Expect the same-email case to return 200 OK without changing email_verified, so a retried request with an unchanged value is safe.
  3. Avoid a second write that flips verification state. The Auth0 Community reported a case where another function updated the same user after the Management API call and reset email_verified to true. Route all email writes for a user through one service so no other process updates the profile in between.
  4. Serialize writes per user at the application layer, for example with a per-user lock or a single queue consumer, so only one outbox message for a given user is in flight at a time. This narrows the window but, as you noted, does not remove it without server-side support.
  5. Submit the conditional-write request to Auth0 through the Auth0 Community feature requests area or your account team, and include your transactional outbox scenario as the use case.

Kind Regards,
Nik

Thanks Nik! I have implemented all the application-side safeguards that are possible to reduce the risk of a race condition. But the risk cannot be eliminated without support for conditional writes on the Auth0 side. I thought this issue was submitted through the Auth0 Community feature requests area. Is that not the case?

I think the distinction between serializing requests and actually fencing out stale writers is the important part here.

Even with a single consumer per user, a worker can lose its lease, pause, and then resume after another worker has already applied a newer update. So the queue guarantees alone don’t solve the problem in your example.

A monotonic version number checked atomically by Auth0 would be useful here. The client could send the expected version with the update, and Auth0 would reject it if a newer version had already been committed.

I’d probably prefer that over a timestamp comparison, since timestamps introduce questions about clock differences and ordering.

It would also be useful to know whether Auth0 is considering this as a general Management API concurrency feature rather than something specific to email updates. The same issue could affect other mutable user attributes.