In my website I have users registering with the email as the username
If I try to manually add a new user and I use a username (which is a mail) that already exists, WP complains and tells me that that username is already registered
If I do this programmatically through the code below, I handle this and prevent the registration
However, there's a user that I guess has clicked repeated times to the form submit button, has managed to get the exact same user duplicated in my WP user database
The question is, how is this possible?
And how can I prevent this?
From the comments, rephrasing the question in other words: If I want to replicate this, how can I do this?
So far the only explanation that makes sense is that I can only replicate it by recreating a race condition (frontend, postman, curl, whatever)
And I'm unsure about if there is a way to prevent it (it'd be like preventing a DOS attack I guess)
The code I'm using is this:
public function register_user($request = null)
{
$response = array();
$parameters = $request->get_json_params();
$email = sanitize_text_field($parameters['email']);
$password = sanitize_text_field($parameters['password']);
$domain = $_SERVER['HTTP_REFERER'];
$uri = $_SERVER['REQUEST_URI'];
$role = 'subscriber';
$error = new WP_Error();
if (empty($email)) {
$error->add(...);
return $error;
}
if (!is_email($email)) {
$error->add(...);
return $error;
}
if (empty($password)) {
$error->add(...);
return $error;
}
$user_id = username_exists($email);
if (!$user_id && email_exists($email) == false) {
$user_id = wp_create_user($email, $password, $email);
if (!is_wp_error($user_id)) {
$user = get_user_by('id', $user_id);
$user->set_role($role);
$response['code'] = 200;
$response['message'] = __("User '" . $user_id . "' ok ", "...");
} else {
return $user_id;
}
} else if ($user_id) {
$error->add(...);
return $error;
}
return new WP_REST_Response($response, 200);
}
I just cannot get how this has happened
EDIT from the comments, front-end code included
<button type="text" value="" onClick={register}>
<div>...</div>
</button>
const register = async e => {
e.stopPropagation()
if (registering === 'working') return
setRegistering('working')
let id = input_id.current.value.trim()
let pass = input_pass.current.value
let pass2 = input_pass2.current.value
const success = pass === pass2 ? await auth.signup(id, pass, feedback) : 'not_equal_passwords'
setRegistering(success)
if (success === 'yes') navigate('/welcome')
}
const signup = async (email, password) => {
const register = `${domain}${website}/wp-json/.../register`
const request = get_request('POST')
const expression = /^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/
let success = password ? (email ? '' : 'no_id') : 'no_pass'
success = expression.test(String(email).toLowerCase()) ? '' : 'no_mail'
if (!success) {
request.body = JSON.stringify({ email: email, password: password })
const response = await get_response(register, request, 5, 4000, 'json')
success = response.code === 'user_exist' ? 'user_exist' : 'yes'
}
return success
}
const get_response = async (url, request, times, time = 2000, json) => {
const rq = request || get_request('GET')
let response = await Promise.race([fetch(url, rq), wait(time)])
let counter = 0
for (const _ of [...Array(times)]) {
if (counter >= times || response !== 'timed') break
response = await Promise.race([fetch(url, request), wait(time)])
counter++
}
if (response === 'timed') return
return json ? await response.json() : response
}