Настройка серверной части для интеграции мобильной авторизации через виджет или SDK

Настройка серверной части для интеграции мобильной авторизации через виджет или SDK

Для работы вам нужно реализовать два эндпоинта на своём сервере:

Эндпоинт Кто вызывает Назначение
POST /api/token Виджет/SDK (браузер) Выдать init-токен для старта верификации
POST /api/siteverify Ваш фронтенд после получения события verified (браузер) Подтвердить результат верификации через MobileID API (server-to-server)

Имена эндпоинтов можно выбрать произвольно.

Пример эндпоинта для виджета

const widget = new MobileIDWidget({
  tokenUrl: '/api/token', /* URL вашего бэкенда для получения токена */
});

Пример эндпоинта для SDK

const mid = new MobileID({
  tokenUrl: '/api/token', /* URL вашего бэкенда для получения токена */
});

Учётные данные

Получите в личном кабинете в разделе мобильная авторизация:

Параметр Описание
CLIENT_ID Публичный идентификатор виджета
API_SECRET Секретный ключ для подписи запросов — никогда не передаётся на фронтенд

Эндпоинт 1 — получение init-токена

Виджет/SDK вызывает этот эндпоинт автоматически при каждом старте верификации. Ваш сервер подписывает запрос и проксирует его в MobileID API.

Что присылает виджет/SDK

POST /api/token
Content-Type: application/json

{ "fingerprint_hash": "a3f9c..." }

Если в виджете настроена капча, её токен приходит как query-параметр:

POST /api/token?captcha_token=<токен_капчи>

Что нужно сделать на сервере

  1. (Опционально) Проверить токен капчи через API провайдера капчи
  2. Сформировать подпись
  3. Отправить подписанный запрос в MobileID API
  4. Вернуть ответ виджету

Алгоритм подписи

timestamp = текущее время в секундах (Unix), как строка
message   = CLIENT_ID + fingerprint_hash + timestamp
signature = HMAC-SHA256(message, API_SECRET)

Запрос к MobileID API

POST https://midsdk.smsaero.ru/api/token
Content-Type: application/json

{
  "client_id":        "pub_abc123",
  "fingerprint_hash": "a3f9c...",
  "timestamp":        "1715000000",
  "signature":        "e3b0c4..."
}

Ответ виджету/SDK

Верните ответ MobileID API без изменений:

{
  "token": "<init-token>"
}

Эндпоинт 2 — проверка результата (server-to-server)

После успешной верификации вызывается колбек:

// Вариант для виджета
onVerified: (data) => {
  // data.verify_token — токен результата
  // data.session_id   — идентификатор сессии
  // data.phone        — номер телефона
}

// Вариант для SDK
verified: (data) => {
  // data.verify_token  — токен результата
  // mid.getSessionId() — идентификатор сессии
  // mid.getPhone()     — номер телефона
}

Доверять этим данным напрямую нельзя — они пришли из браузера. Чтобы убедиться, что верификация действительно прошла, отправьте verify_token и session_id на ваш сервер, а сервер проверит их в MobileID API. Это и есть server-to-server проверка.

Принцип проверки

Браузер                    Ваш сервер              MobileID API
   │                           │                        │
   │  onVerified / verified    │                        │
   │  ──────────────────────►  │                        │
   │                           │  POST /api/siteverify  │
   │                           │  {session_id,          │
   │                           │   verify_token, ...}   │
   │                           │ ─────────────────────► │
   │                           │   { success: true,     │
   │                           │     phone: "7..." }    │
   │                           │ ◄───────────────────── │
   │   { ok: true }            │                        │
   │  ◄──────────────────────  │                        │

Только после успешного ответа MobileID API считайте номер подтверждённым и выполняйте бизнес-логику (вход, привязка номера и т.д.).

Что отправляет ваш фронтенд

POST /api/siteverify
Content-Type: application/json

{
  "session_id":   "<из onVerified>",
  "verify_token": "<из onVerified>"
}

Что нужно сделать на сервере

  1. Принять session_id и verify_token
  2. Сформировать подпись
  3. Отправить запрос в MobileID API
  4. Вернуть результат фронтенду

Алгоритм подписи

timestamp = текущее время в секундах (Unix), как строка
message   = CLIENT_ID + session_id + timestamp
signature = HMAC-SHA256(message, API_SECRET)

Запрос к MobileID API

POST https://midsdk.smsaero.ru/api/siteverify
Content-Type: application/json

{
  "client_id":    "pub_abc123",
  "session_id":   "<session_id>",
  "verify_token": "<verify_token>",
  "timestamp":    "1715000000",
  "signature":    "e3b0c4..."
}

Ответ MobileID API

{
  "success": true,
  "phone": "79161234567",
  "status": "verified" // любой статус отличный от verified считается как неудачная верификация
}

Пример реализации на PHP

Требования: PHP 5.4+, расширения curl и hash (входят в стандартную поставку).

Класс MobileIDBackend

Скопируйте класс в свой проект:

<?php

class MobileIDBackend
{
    private $clientId;
    private $apiSecret;
    private $sdkBackendUrl;
    private $timeout;

    /**
     * @param array $config {
     *   @type string $clientId       Публичный идентификатор клиента (обязательный)
     *   @type string $apiSecret      Секретный ключ для подписи запросов (обязательный)
     *   @type string $sdkBackendUrl  Базовый URL MobileID API без слеша на конце
     *                                (обязательный, используйте https://midsdk.smsaero.ru)
     *   @type int    $timeout        Таймаут cURL в секундах (по умолчанию 10)
     * }
     */
    public function __construct(array $config)
    {
        $this->clientId      = $config['clientId'];
        $this->apiSecret     = $config['apiSecret'];
        $this->sdkBackendUrl = rtrim($config['sdkBackendUrl'], '/');
        $this->timeout       = isset($config['timeout']) ? (int)$config['timeout'] : 10;
    }

    /**
     * Получить init-токен для виджета.
     * Вызывается из эндпоинта /api/token.
     *
     * @param  string $fingerprintHash  Хэш из тела запроса виджета
     * @return array                    Массив из двух элементов: [HTTP-статус, тело ответа]
     */
    public function getToken($fingerprintHash)
    {
        if ($fingerprintHash === '') {
            return [400, ['error' => 'missing fingerprint_hash']];
        }

        $timestamp = (string)time();
        $signature = hash_hmac(
            'sha256',
            $this->clientId . $fingerprintHash . $timestamp,
            $this->apiSecret
        );

        return $this->request('/api/token', [
            'client_id'        => $this->clientId,
            'fingerprint_hash' => $fingerprintHash,
            'timestamp'        => $timestamp,
            'signature'        => $signature,
        ]);
    }

    /**
     * Проверить результат верификации (server-to-server).
     * Вызывается из эндпоинта /api/siteverify.
     *
     * @param  string $sessionId    ID сессии из события onVerified
     * @param  string $verifyToken  Токен результата из события onVerified
     * @return array                Массив из двух элементов: [HTTP-статус, тело ответа]
     */
    public function siteVerify($sessionId, $verifyToken)
    {
        if ($sessionId === '' || $verifyToken === '') {
            return [400, ['error' => 'missing session_id or verify_token']];
        }

        $timestamp = (string)time();
        $signature = hash_hmac(
            'sha256',
            $this->clientId . $sessionId . $timestamp,
            $this->apiSecret
        );

        return $this->request('/api/siteverify', [
            'client_id'    => $this->clientId,
            'session_id'   => $sessionId,
            'verify_token' => $verifyToken,
            'timestamp'    => $timestamp,
            'signature'    => $signature,
        ]);
    }

    /**
     * @param  string $path     Путь, например '/api/token'
     * @param  array  $payload  Данные для отправки
     * @return array            [HTTP-статус, декодированное тело]
     */
    private function request($path, array $payload)
    {
        $url  = $this->sdkBackendUrl . $path;
        $json = json_encode($payload);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
            CURLOPT_POSTFIELDS     => $json,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => $this->timeout,
        ]);

        $raw      = curl_exec($ch);
        $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr  = curl_errno($ch) ? curl_error($ch) : null;
        curl_close($ch);

        if ($curlErr !== null) {
            return [502, ['error' => 'sdk backend unavailable', 'details' => $curlErr]];
        }

        $body = json_decode($raw, true);

        if ($body === null) {
            return [502, ['error' => 'invalid response from sdk backend']];
        }

        return [$httpCode, $body];
    }
}

Использование — эндпоинт /api/token

<?php
require_once 'MobileIDBackend.php';

header('Content-Type: application/json');

$backend = new MobileIDBackend([
    'clientId'      => 'your_client_id',
    'apiSecret'     => 'your_api_secret',
    'sdkBackendUrl' => 'https://midsdk.smsaero.ru',
]);

$input = json_decode(file_get_contents('php://input'), true);
$input = $input ? $input : [];

// Проверка капчи (если в виджете задан getCaptchaToken)
// $captchaToken = isset($_GET['captcha_token']) ? $_GET['captcha_token'] : '';
// ... верификация токена через API провайдера капчи ...

$fingerprintHash = isset($input['fingerprint_hash']) ? $input['fingerprint_hash'] : '';

list($status, $body) = $backend->getToken($fingerprintHash);
http_response_code($status);
echo json_encode($body);

Использование — эндпоинт /api/siteverify

<?php
require_once 'MobileIDBackend.php';

header('Content-Type: application/json');

$backend = new MobileIDBackend([
    'clientId'      => 'your_client_id',
    'apiSecret'     => 'your_api_secret',
    'sdkBackendUrl' => 'https://midsdk.smsaero.ru',
]);

$input = json_decode(file_get_contents('php://input'), true);
$input = $input ? $input : [];

$sessionId   = isset($input['session_id'])   ? $input['session_id']   : '';
$verifyToken = isset($input['verify_token']) ? $input['verify_token'] : '';

list($status, $body) = $backend->siteVerify($sessionId, $verifyToken);

if ($status === 200 && !empty($body['success'])) {
    // Верификация подтверждена — выполняем бизнес-логику
    $phone = $body['phone']; // номер в формате 79161234567
}

http_response_code($status);
echo json_encode($body);

Настройка виджета/SDK на фронтенд

После реализации эндпоинтов передайте адрес первого в опцию tokenUrl:

// Вариант для виджета
const widget = new MobileIDWidget({
  tokenUrl: 'https://your-site.ru/api/token',  // ваш эндпоинт

  onVerified: async (data) => {
    // Отправляем на свой сервер для server-to-server проверки
    const res = await fetch('/api/siteverify', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({
        session_id: data.session_id,
        verify_token: data.verify_token,
      }),
    });
    const result = await res.json();

    if (result.success) {
      // Номер подтверждён
    }
  },
});

// Вариант для SDK
const mid = new MobileID({
  tokenUrl: 'https://your-site.ru/api/token',  // ваш эндпоинт
});

mid.on('verified', async (data) => {
  const res = await fetch('/api/siteverify', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
      session_id: mid.getSessionId(),
      verify_token: data.verify_token,
    }),
  });
  const result = await res.json();

  if (result.success) {
    // Номер подтверждён
  }
});

Подробную информацию по виджету или SDK можно посмотреть в разделе мобильная авторизация

Безопасность

  • API_SECRET должен храниться только на сервере — не передавайте его в браузер и не включайте в фронтенд-сборки
  • server-to-server проверка через /api/siteverify обязательна — verify_token без неё не является доказательством верификации
  • verify_token действителен только для одной проверки — после успешного ответа MobileID API он становится недействительным