Подключение Yandex SmartCaptcha к виджету мобильной авторизации
Эта инструкция поможет подключить Yandex SmartCaptcha для защиты формы, в которой используется виджет мобильной авторизации SMS Aero.
Капча нужна, чтобы защитить публичную форму от автоматических запросов. Проверка должна выполняться не только в браузере, но и на вашем сервере. Если проверить капчу только на странице, защиту можно будет обойти.
Как это работает
- Пользователь вводит номер телефона в виджете.
- Перед отправкой номера виджет вызывает вашу функцию
getCaptchaToken. - Функция запускает Yandex SmartCaptcha и возвращает одноразовый токен.
- Виджет отправляет запрос на ваш
tokenUrlи добавляет токен капчи в query-параметрcaptcha_token. - Ваш backend проверяет
captcha_tokenчерез API Yandex SmartCaptcha. - Если проверка успешна, backend продолжает обычную логику и получает token мобильной авторизации.
- Если проверка не пройдена, backend возвращает ошибку и мобильная авторизация не запускается.
Важно: captcha_token ещё не подтверждает прохождение проверки. Его обязательно нужно проверить на backend.
Что понадобится
- Подключенный виджет мобильной авторизации SMS Aero.
- Client key Yandex SmartCaptcha для frontend.
- Server key Yandex SmartCaptcha для backend.
- Backend-метод
tokenUrl, через который виджет получает token мобильной авторизации.
Ключи SmartCaptcha можно получить в Yandex Cloud: https://yandex.cloud/ru/docs/smartcaptcha/quickstart
Как это связано с кодом из личного кабинета SMS Aero
В личном кабинете SMS Aero в разделе настройки виджета есть блок "Код для вставки". В базовом варианте он выглядит так:
<div id="mobileid-widget-container"></div>
<script src="https://cdn.smsaero.ru/mid-widget/1/mobileid-widget.min.js"></script>
<script>
var widget = new MobileIDWidget({
tokenUrl: '/api/token',
onVerified: function(data) { console.log('verified', data); },
onRejected: function(data) { console.warn('rejected', data); },
onError: function(err) { console.error('error', err); },
});
widget.mount('#mobileid-widget-container');
</script>Этот виджет уже поддерживает подключение капчи через параметр getCaptchaToken. В базовом коде из личного кабинета этого параметра нет, поэтому его нужно добавить вручную.
Итоговая доработка состоит из трех частей:
- Добавить контейнер для Yandex SmartCaptcha.
- Подключить скрипт Yandex SmartCaptcha.
- Добавить
getCaptchaTokenв настройкиnew MobileIDWidget(...).
Добавьте контейнеры на страницу
На странице должны быть два контейнера:
- контейнер для виджета мобильной авторизации;
- контейнер для Yandex SmartCaptcha.
<div id="mobileid-widget-container"></div>
<div id="smartcaptcha-container"></div>id можно изменить, но тогда этот же id нужно указать в коде инициализации.
Подключите скрипт Yandex SmartCaptcha
Добавьте скрипт SmartCaptcha на страницу:
<script
src="https://smartcaptcha.cloud.yandex.ru/captcha.js?render=onload&onload=onSmartCaptchaReady"
defer
></script>Скрипт виджета мобильной авторизации подключите так же, как указано в основной инструкции по интеграции виджета.
Настройте получение captcha token
Пример для невидимой Yandex SmartCaptcha:
<script>
const SMARTCAPTCHA_CLIENT_KEY = 'client_key_из_yandex_cloud';
let smartCaptchaWidgetId = null;
let smartCaptchaResolve = null;
let smartCaptchaReject = null;
let smartCaptchaTimer = null;
function finishCaptcha(error, token) {
if (smartCaptchaTimer) {
clearTimeout(smartCaptchaTimer);
smartCaptchaTimer = null;
}
if (error && smartCaptchaReject) {
smartCaptchaReject(error);
} else if (smartCaptchaResolve) {
smartCaptchaResolve(token);
}
smartCaptchaResolve = null;
smartCaptchaReject = null;
}
window.onSmartCaptchaReady = function () {
smartCaptchaWidgetId = window.smartCaptcha.render('smartcaptcha-container', {
sitekey: SMARTCAPTCHA_CLIENT_KEY,
invisible: true,
callback: function (token) {
if (!token) {
finishCaptcha(new Error('captcha_empty'));
return;
}
finishCaptcha(null, token);
},
error: function () {
finishCaptcha(new Error('captcha_error'));
},
});
};
function getCaptchaToken() {
return new Promise(function (resolve, reject) {
if (!window.smartCaptcha || smartCaptchaWidgetId === null) {
reject(new Error('captcha_not_ready'));
return;
}
smartCaptchaResolve = resolve;
smartCaptchaReject = reject;
smartCaptchaTimer = setTimeout(function () {
finishCaptcha(new Error('captcha_timeout'));
}, 120000);
try {
window.smartCaptcha.reset(smartCaptchaWidgetId);
window.smartCaptcha.execute(smartCaptchaWidgetId);
} catch (error) {
finishCaptcha(error);
}
});
}
</script>Токен SmartCaptcha одноразовый и живет ограниченное время, поэтому перед каждой новой попыткой авторизации нужно получать новый token.
Передайте функцию в виджет
В коде из личного кабинета найдите блок new MobileIDWidget(...) и добавьте в него параметр getCaptchaToken:
<script>
var widget = new MobileIDWidget({
tokenUrl: 'https://your-site.ru/api/mobile-id/token',
getCaptchaToken: getCaptchaToken,
onError: function (error) {
console.error('Mobile ID error:', error);
},
});
widget.mount('#mobileid-widget-container');
</script>После этого виджет будет вызывать getCaptchaToken перед отправкой номера. Полученный token будет передан на ваш backend так:
POST https://your-site.ru/api/mobile-id/token?captcha_token=<captcha_token>Если getCaptchaToken вернет ошибку, виджет не начнет мобильную авторизацию и разблокирует кнопку отправки.
Проверьте captcha token на backend
Server key Yandex SmartCaptcha должен храниться только на backend. Нельзя передавать его в браузер или размещать в JavaScript-коде страницы.
Пример проверки на PHP:
<?php
const SMARTCAPTCHA_SERVER_KEY = 'server_key_из_yandex_cloud';
function verifySmartCaptcha(string $token, string $ip): bool
{
if ($token === '') {
return false;
}
$ch = curl_init('https://smartcaptcha.cloud.yandex.ru/validate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'secret' => SMARTCAPTCHA_SERVER_KEY,
'token' => $token,
'ip' => $ip,
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
]);
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response === false || $httpCode !== 200) {
return false;
}
$data = json_decode($response, true);
return ($data['status'] ?? '') === 'ok';
}
$captchaToken = $_GET['captcha_token'] ?? '';
$userIp = $_SERVER['REMOTE_ADDR'] ?? '';
if (!verifySmartCaptcha($captchaToken, $userIp)) {
http_response_code(403);
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'success' => false,
'message' => 'captcha_failed',
]);
exit;
}
// Далее выполняется штатная логика вашего /api/mobile-id/token:
// 1. сформировать подписанный запрос к backend SMS Aero Mobile ID;
// 2. получить token мобильной авторизации;
// 3. вернуть token виджету.Документация Yandex по проверке token: https://yandex.cloud/ru/docs/smartcaptcha/operations/validate-captcha
Если используется Vue
Если виджет подключается внутри Vue-компонента, принцип остается тем же: компонент капчи должен вернуть token, а виджет должен получить функцию через getCaptchaToken.
<template>
<div>
<YandexCaptcha
ref="captcha"
:sitekey="captchaSiteKey"
form-id="mobileid"
/>
<mobile-id-widget
:token-url="tokenUrl"
:get-captcha-token="getCaptchaToken"
/>
</div>
</template>
<script>
export default {
data() {
return {
captchaSiteKey: 'client_key_из_yandex_cloud',
tokenUrl: 'https://your-site.ru/api/mobile-id/token',
};
},
methods: {
async getCaptchaToken() {
const token = await this.$refs.captcha.check({ force: true });
if (!token) {
throw new Error('captcha_failed');
}
return token;
},
},
};
</script>Как проверить интеграцию
- Откройте страницу с виджетом.
- Введите номер телефона и нажмите кнопку отправки.
- В DevTools браузера проверьте запрос на ваш
tokenUrl. - В URL запроса должен быть параметр
captcha_token. - На backend проверьте, что перед выдачей Mobile ID token выполняется запрос к Yandex SmartCaptcha
/validate. - При успешной проверке капчи мобильная авторизация должна продолжиться.
- При ошибке капчи backend должен вернуть ошибку, а мобильная авторизация не должна запускаться.
Частые ошибки
Капча добавлена только на frontend
Так делать нельзя. Frontend получает token, но окончательное решение должен принимать backend после проверки token через Yandex SmartCaptcha.
Server key попал в JavaScript
Server key должен храниться только на backend. В браузере используется только client key.
Backend ищет token в теле запроса
Виджет передает token в query-параметре captcha_token:
?captcha_token=<captcha_token>Если backend читает только JSON body, он не увидит token.
Повторно используется старый token
Token SmartCaptcha одноразовый. Перед каждой попыткой отправки номера нужно получать новый token.
Не настроен домен в Yandex SmartCaptcha
В настройках SmartCaptcha должен быть указан домен сайта, на котором размещен виджет. Иначе проверка может завершаться ошибкой.
Не обработана ошибка капчи
Если getCaptchaToken завершился ошибкой, виджет не отправит номер. Покажите пользователю понятное сообщение, например: "Не удалось пройти проверку. Попробуйте еще раз".
Итоговая схема
Страница сайта
-> Yandex SmartCaptcha
-> Mobile ID widget
-> ваш backend tokenUrl?captcha_token=...
-> проверка token в Yandex SmartCaptcha
-> получение Mobile ID token в SMS Aero
-> запуск мобильной авторизацииГлавное правило: виджет только получает captcha_token и передает его на ваш backend. Проверка token и решение, можно ли запускать мобильную авторизацию, всегда должны выполняться на вашем backend.
Вы соглашаетесь с условиями обработки, используя сайт. Подробнее