[СДЕЛАЙ САМ] Авторизация и регистрация по SMS с помощью SendIt

Приветствую! Хочу рассказать вам как с помощью одного компонента и одной формы с минимумом усилий сделать регистрацию и авторизацию по SMS.

1. Устанавливаем SendIt и FormIt

2. Подготавливаем форму.
<form data-si-form="smsAuthForm" data-si-preset="sms_register">
    <label>
        <input type="tel" name="phone" data-si-event="change" data-si-preset="sendcode" placeholder="+7(">
        <p data-si-error="phone"></p>
    </label>
    <label data-si-hide="code">
        <input type="text" name="code" data-si-event="change" data-si-preset="checkcode" placeholder="0000">
        <p data-si-error="code"></p>
    </label>
    <label data-si-hide="fullname">
        <input type="text" name="fullname" placeholder="Полное имя">
        <p class="color_error px-15 fs-16-12" data-si-error="fullname"></p>
    </label>
    <button type="submit">Отправить</button>
</form>

3. Добавляем необходимые пресеты.
'sendcode' => [
        'hooks' => '',
        'snippet' => '@FILE snippets/smsauth/snippet.sendcode.php',
        'successMessage' => 'Код отправлен на номер {$phone}',
        'validate' => 'phone:required',
        'phone.vTextRequired' => 'Укажите телефон.'
    ],
    'checkcode' => [
        'hooks' => '',
        'successMessage' => '',
        'validate' => 'code:CheckCode',
        'validationErrorMessage' => 'Неверный код.',
    ],
    'sms_auth' => [
        'extends' => 'checkcode',
        'successMessage' => 'Вы успешно авторизованы.',
        'hooks' => 'SetUserFields,Identification',
        'method' => 'login',
        'redirectTo' => 5,
        'user_exist' => 1,
        'redirectTimeout' => 3000,
        'clearFieldsOnSuccess' => 1,
    ],
    'sms_register' => [
        'hooks' => 'SetUserFields,Identification',
        'method' => 'register',
        'successMessage' => 'Вы успешно зарегистрированы.',
        'activation' => 0,
        'autoLogin' => 1,
        'redirectTo' => 5,
        'redirectTimeout' => 3000,
        'usergroups' => 2,
        'validate' => 'fullname:required,phone:required,code:CheckCode',
        'fullname.vTextRequired' => 'Укажите ваше имя.',
        'clearFieldsOnSuccess' => 1,
    ],

4. Создаём сниппет генерации и отправки кода snippet.sendcode.php
require_once MODX_CORE_PATH . 'components/sendit/model/sendit/identification.class.php';

$phone = preg_replace('/[^0-9]/', '', $_POST['phone']);
$code = Identification::generateCode($modx, 'code', 4);
$userExist = $modx->getCount('modUser', ['username' => $phone]);
$site_url = $modx->getOption('site_url');
$msg = "Код подтверждения с сайта $site_url: $code";

$_SESSION['SMSAuth']['user_exist'] = $userExist;
$_SESSION['SMSAuth']['code'] = $code;
$_SESSION['SMSAuth']['phone'] = $phone;
$_SESSION['SMSAuth']['phone_orignal'] = $_POST['phone'];

// ОТПРАВКА КОДА В СЕРВИС РАССЫЛКИ СМС

//$modx->log(1, $code);
$successMessage = str_replace('{$phone}', $_POST['phone'], $successMessage);

return $SendIt->success($successMessage, ['user_exist' => $_SESSION['SMSAuth']['user_exist']]);

5. Создаём сниппет проверки введённого кода CheckCode
$inputCode = $_POST['code'];

if($inputCode !== $_SESSION['SMSAuth']['code']){
    $validator->addError($key, $validationErrorMessage);
}else{
    $code = $_SESSION['SMSAuth']['code'];
    $phone = $_SESSION['SMSAuth']['phone'];
    if($_SESSION['SMSAuth']['user_exist']){
        $user = $modx->getObject('modUser', ['username' => $phone]);
        $user->set('password', $code);
        $user->save();
    }
}
return true;

6. Создаём сниппет установки полей пользователя SetUserFields
$hook->setValue('username', $_SESSION['SMSAuth']['phone']);
$hook->setValue('password', $_SESSION['SMSAuth']['code']);
$hook->setValue('phone', $_SESSION['SMSAuth']['phone_orignal'] );

return true;

7. Добавляем JavaScript для обработки ответов вервера
const hiddenBlocks = document.querySelectorAll('[data-si-hide]');
const authFormBtn = document.querySelector('[data-si-form="smsAuthForm"] [type="submit"]');
authFormBtn && (authFormBtn.disabled = true); // блокируем кнопку
hiddenBlocks.length && hiddenBlocks.forEach(block => block.classList.add('v_hidden')); // скрываем поля с кодом и именем
document.addEventListener('si:send:after', (e) => {
    const {target, result} = e.detail; // получаем данные из события
    if(result.success){      

        if(target.dataset.siPreset === 'sendcode'){ // после отправки кода
            const codeInput =  document.querySelector('[data-si-form="smsAuthForm"] [name="code"]');
            const phoneInput = document.querySelector('[data-si-form="smsAuthForm"] [name="phone"]');
            const phoneError = document.querySelector('[data-si-form="smsAuthForm"] [data-si-error="phone"]');
            const wrapCode = document.querySelector('[data-si-hide="code"]');
            wrapCode.classList.remove('v_hidden'); // показываем поле ввода кода

            if(result.data.user_exist){ // если пользователь существует
                codeInput.dataset.siPreset = 'sms_auth'; // авторизуем
            }else{
                codeInput.dataset.siPreset = 'checkcode'; // проверяем код
            }
            phoneInput.disabled = true;
            phoneError.textContent = 'Повторная отправка возможна через 30 сек.';
            setTimeout(() => {
                phoneInput.disabled = false;
                phoneError.textContent = '';
            }, 30000)
        }

        if(target.dataset.siPreset === 'checkcode'){ // поле проверки кода
            const wrapName = document.querySelector('[data-si-form="smsAuthForm"] [data-si-hide="fullname"]');
            wrapName.classList.remove('v_hidden'); // показываем поле ввода имени
            authFormBtn.disabled = false; // разблокируем кнопку
        }
    }
})
Порядок работы
Вся форма (data-si-form=«smsAuthForm») будет регистрировать нового пользователя, но сразу после загрузки страницы мы блокируем кнопку и скрываем поля кода и имени.

После вводе номера телефона он будет отправлен на сервер, где выполнится код из сниппета snippet.sendcode.php. В нём подключаем класс Identification, чтобы сгенерировать код, проверяем есть ли пользователь с таким телефоном, формируем сообщение для отправки пользователю, записываем данные в сессию для использования на следующих этапах. Код отправки сообщения вы пишите сами, так как я не хочу ограничивать вас в выборе сервиса СМС рассылок..

После отправки кода, если пользователь существует, с помощью JS меняем пресет с checkcode на sms_auth. Блокируем ввод номера на 30 секунд.Если пользователь не существует проверяем код, если он верный, устанавливаем его в качестве пароля, на клиенте показываем поле ввода имени и разблокируем кнопку. Если существует, также устанавливаем код в качестве пароля и переадресовываем в ЛК.

Если поле с вводом имени показано, пользователь должен его обязательно указать и только потом сможет отправить форму регистрации.

Можно ещё много всякого накрутить по юзабилити, но в целом это всё.
Артур Шевченко
18 октября 2023, 18:00
modx.pro
4
863
+12
Поблагодарить автора Отправить деньги

Комментарии: 3

Александр Сенькин
29 октября 2023, 07:29
0
Получается весь механизм работы не подразумевает подключать в процесс сторонние сервисы по SMS рассылкам? Все делает php?
    Николай Савин
    29 октября 2023, 08:40
    0
    Нет. Автор немного схитрил и не указал в коде саму отправку SMS. Он просто оставил комментарий // ОТПРАВКА КОДА В СЕРВИС РАССЫЛКИ СМС

    По факту там будет подключение к оператору и отправка SMS
      Артур Шевченко
      29 октября 2023, 13:10
      0
      Сервис рассылки вы выбираете самостоятельно и отправку данных в него пишите самостоятельно.
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      3