- PVSM.RU - https://www.pvsm.ru -

Организация аутентификации по СМС по примеру Telegram-Viber-WhatsApp

Представим, что перед вами стоит задача организовать аутентификацию [1] пользователя (в мобильном приложении, в первую очередь) так, как это сделано в Telegram/Viber/WhatsApp. А именно реализовать в API возможность осуществить следующие шаги:

  • Пользователь вводит свой номер телефона и ему на телефон приходит СМС с кодом.
  • Пользователь вводит код из СМС и приложение его аутентифицирует и авторизует.
  • Пользователь открывает приложение повторно, и он уже аутентифицирован и авторизован.

Я постараюсь кратко изложить выработанный подход к этому вопросу. Подразумевается, что у вас API, HTTPS и, вероятно, REST [2]. Какой у вас там набор остальных технологий неважно. Если интересно — добро пожаловать под кат.

Изменения в API

В сущности требуется добавить два добавить три метода в ваше API:

  • Запросить СМС с кодом на номер, в ответ — токен для последующих действий.

Действие соответствует CREATE в CRUD [3].

    POST /api/sms_authentications/
    Параметры на вход:
        phone
    Параметры на выход:
        token

Если всё прошло, как ожидается, возвращаем код состояния [4] 200.

Если же нет, то есть одно разумное исключение (помимо стандартной 500 ошибки при проблемах на сервере и т.п. — некорректно указан телефон. В этом случае:

HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: PHONE_NUMBER_INVALID.

  • Подтвердить токен с помощью кода из СМС.

Действие соответствует UPDATE в CRUD [3].

    PUT /api/sms_authentications/<token>/
    Параметры на вход:
        sms_code

Аналогично. Если всё ок — код 200.

Если же нет, то варианты исключений:

  1. Некорректный токен: HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: TOKEN_INVALID.
  2. Некорректный код: HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: SMS_CODE_INVALID.

  • Форсированная отправка кода повторно.

    PUT /api/sms_authentications/<token>/resend

Аналогично. Если всё ок — код 200.

Если же нет, то варианты исключений:

  1. Некорректный токен: HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: TOKEN_INVALID.
  2. Слишком частая отправка (скажем, прошлая отправка была не позднее чем 60 секунд назад): HTTP код состояния: 400 (BAD_REQUEST), в теле ответа: TOO_OFTEN.

Помимо этого, каждый метод API, который требует аутентифицированного пользователя должен получать на вход дополнительный параметр token, который связан с пользователем.

Литература:

  1. Образец для подражания — API Telegram: https://core.telegram.org/methods [5]
  2. Дискуссия на SOF: http://stackoverflow.com/questions/12401255/sms-registration-like-in-the-mobile-app-whatsapp [6]

Изменения в коде сервера

Вам потребуется хранить специальный ключ для проверки СМС-кодов. Существует алгоритм TOTP [7], который позволяет, цитирую Википедию:

OATH-алгоритм создания одноразовых паролей для защищенной аутентификации, являющийся улучшением HOTP (HMAC-Based One-Time Password Algorithm). Является алгоритмом односторонней аутентификации — сервер удостоверяется в подлинности клиента. Главное отличие TOTP от HOTP это генерация пароля на основе времени, то есть время является параметром[1]. При этом обычно используется не точное указание времени, а текущий интервал с установленными заранее границами (например, 30 секунд).

Грубо говоря, алгоритм позволяет создать одноразовый пароль, отправить его в СМС, и проверить, что присланный пароль верен. Причём сгенерированный пароль будет работать заданное количество времени. При всём при этом не надо хранить эти бесконечные одноразовые пароли и время, когда они будут просрочены, всё это уже заложено в алгоритм и вы храните только ключ.

Пример кода на руби, чтобы было понятно о чём речь:

totp = ROTP::TOTP.new("base32secret3232")
totp.now # => "492039"

# OTP verified for current time
totp.verify("492039") # => true
sleep 30
totp.verify("492039") # => false

Существует масса реализацией этого алгоритма для многих языков: для Ruby [8] и Rails [9], для Python [10], для PHP [11] и т.д. [12].

Итого, в модели (или в таблице БД, если угодно) надо хранить:

  1. Телефон: phone (советую использовать библиотеки для унификации телефонного номера, вроде этой для Rails [13]),
  2. Ключ для TOTP: otp_secret_key (читаете подробное README для выбранной библиотеки TOTP),
  3. Токен: token (создаете при первом запросе к API чем-нибудь типа SecureRandom [14]),
  4. Ссылку на пользователя: user_id (если у вас есть отдельная таблица/модель, где хранятся данные пользователя).

Особенности реализации мобильного приложения

В случае Android полученный токен можно хранить в SharedPreferences [15], а для iOS в KeyChain [16]. См. обсуждение на SoF [17].

Заключение

Вышеописанный подход позволит вам в рамках вашего стека технологий реализовать указанную задачу. Если вас есть соображения по этому подходу или альтернативные подходы, то прошу поделиться в комментариях. Аналогичная просьба, если у вас есть примеры документации к безопасным системам хранения в других мобильных ОС.

Автор: mpetrunin

Источник [18]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ruby/156363

Ссылки в тексте:

[1] аутентификацию: https://ru.wikipedia.org/wiki/%D0%90%D1%83%D1%82%D0%B5%D0%BD%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F

[2] REST: https://ru.wikipedia.org/wiki/REST

[3] CRUD: https://ru.wikipedia.org/wiki/CRUD

[4] код состояния: https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%BA%D0%BE%D0%B4%D0%BE%D0%B2_%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D1%8F_HTTP

[5] https://core.telegram.org/methods: https://core.telegram.org/methods

[6] http://stackoverflow.com/questions/12401255/sms-registration-like-in-the-mobile-app-whatsapp: http://stackoverflow.com/questions/12401255/sms-registration-like-in-the-mobile-app-whatsapp

[7] TOTP: https://ru.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm

[8] Ruby: https://github.com/mdp/rotp

[9] Rails: https://github.com/heapsource/active_model_otp

[10] Python: https://github.com/pyotp/pyotp

[11] PHP: https://github.com/Spomky-Labs/otphp

[12] т.д.: https://github.com/search?utf8=%E2%9C%93&q=totp

[13] этой для Rails: https://github.com/joost/phony_rails

[14] SecureRandom: http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html

[15] SharedPreferences: http://developer.android.com/reference/android/content/SharedPreferences.html

[16] KeyChain: https://developer.apple.com/library/mac/documentation/Security/Conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html

[17] SoF: http://stackoverflow.com/questions/10146996/how-do-i-set-up-persistent-authentication-in-a-mobile-app

[18] Источник: https://habrahabr.ru/post/305694/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best