Тернистый путь внедрения аутентификации через соцсети

Kate

Administrator
Команда форума
Кто помнит пароли от всех своих аккаунтов? Учеток слишком много, ведь почти каждый сервис в наше время требует для полноценного доступа создать учётную запись и придумать новый пароль.

Столкнувшись с очередным таким требованием, особенно на новой платформе, человек просто закрывает вкладку в браузере со словами «не больно-то и хотелось». Чтобы упростить жизнь пользователям – существует аутентификация через третьи сервисы (чаще социальные сети). Как ее правильно настроить – читайте под катом.

Привет, Хабр, меня зовут Сергей Солдатов, я тимлид в подразделении eSports MTS Digital, разработчик на Go и немного архитектор. Занимаюсь сервисами аутентификации и авторизации на WASD.TV. Это многофункциональная стриминговая платформа для геймеров, собственный сервис МТС.

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

Наглядный пример – вы аутентифицируетесь в Google и потом, не вводя снова логина и пароля, можете зайти в аккаунт на Miro, YouTube, WASD.TV, Яндексе. Это нормальная ситуация, когда в одной экосистеме можно пройти аутентификацию с помощью другой. Могут быть целые матрёшки: на WASD.TV проходим аутентификацию через Яндекс, а уже там выбираем Facebook и заходим в свой профиль.

Интеграция становится всё теснее и пользователям предлагается не только аутентификация, но и авторизация. Так платформа, на которую вы пришли, может совершать от вашего имени действия на другой платформе, где вы прошли аутентификацию.

Такая авторизация/аутентификация стала возможна благодаря протоколам OAuth2.0 и OpenID Connect. Последнее не стоит путать с OpenID, это совершенно разные протоколы.

Протоколы OAuth2.0 и OpenID Connect​


q0fnupl4vmekc7ewru5myvmnmpo.jpeg


OAuth2.0 – это авторизация, OpenID Connect – аутентификация. OpenID Connect – это надстройка над OAuth2.0.

При авторизации через OAuth2.0 есть три основные сущности: владелец ресурса, сервис ресурсов и клиент. Последний – это внешнее приложение, которое хочет получить доступ к ресурсу. На схеме показана абстрактная схема взаимодействия.

aheybmw0jhrmy3jrsivlnv5znew.jpeg


Порядок такой:

  1. Приложение запрашивает у пользователя авторизацию на доступ к серверу ресурсов.
  2. Если пользователь авторизует запрос, приложение получает разрешение на авторизацию (authorization grant).
  3. Приложение запрашивает авторизационный токен у сервера авторизации (API) путём предоставления информации о самом себе и разрешении на авторизацию от пользователя.
  4. Если подлинность приложения подтверждена и разрешение на авторизацию действительно, сервер авторизации (API) создаёт токен доступа для приложения. Процесс авторизации завершён.
  5. Приложение запрашивает ресурс у сервера ресурсов (API), предоставляя при этом токен доступа для аутентификации.

Если токен действителен, сервер ресурсов (API) предоставляет запрашиваемый ресурс приложению.

По стандарту OAuth2.0 есть два вида токенов – accеss и refresh. Первый предназначен для доступа к API сервера ресурсов и живет он недолго, обычно несколько часов. Второй не даёт доступа, но позволяет обновить первый токен, его срок жизни — до нескольких месяцев. При обновлении в зависимости от реализации меняются либо оба токена (более безопасный вариант) либо только access-токен.

vwmiyiacmrikjh0lzrdlldns8ji.png


Стандарт OAuth2 никак не регламентирует и не ограничивает формат токенов, но обычно они состоят из двух частей:

  • случайной последовательности символов заданной длины;
  • подписи данной последовательности символов.
При использовании OpenID Connect после аутентификации приложение получит ID-токен. Это JWT-токен, тело которого будет содержать информацию об аутентифицированном пользователе (email, логин, иные запрошенные сведения). Это позволяет за один запрос получить информацию об авторизовавшемся пользователе и токены для доступа к API социальной сети.

{
"sub": "248289761001",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"preferred_username": "j.doe",
"email": "janedoe@example.com"
}

Как и все JWT-токены, ID-токен состоит из трёх частей:

  • заголовок – обязательное поле здесь только алгоритм подписи;
  • тело – здесь есть служебные поля, но в целом набор произвольный;
  • подпись токена.

Для подписи ID-токена используют алгоритмы ассиметричного или симметричного шифрования.

В первом случае для проверки подписи можно обратиться к специальному endpoint сервиса выдавшего токен и узнать открытый ключ для соответствующего алгоритма. Во втором случае выдавший токен сервис предоставляет специальный API для проверки токенов и при валидности в ответ обычно сообщается тело токенов в JSON-формате.

Стоит ли всегда проверять ID-токен? Вообще, да. Но если вы получили токен непосредственно от сформировавшего сервиса, то с определённой долей риска можно не проверять. Если будет атака «человек посередине» – проверка уже не спасёт, поскольку он потенциально может подделать все ответы от сформировавшего токен сервиса. Но если вы получаете ID-токен от третьего сервиса, то проверка обязательна.

Для упрощения жизни разработчика в стандартах OAuth2 и OpenID Connect предусмотрены discovery URL для получения информации о настройках и endpoints для взаимодействия:

  • /.well-known/oauth-authorization-server;
  • /.well-known/openid-configuration.
Для OAuth2 на практике я таких не встречал, а для OpenID Connect они активно используются.

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

А в социальных сетях как?​


Несмотря на детальную спецификацию, стандарты OAuth2 и OpenID Connect оставляют большой простор для конкретной реализации. И подключение различных социальных сетей для аутентификации иногда напоминает поход по минному полю. На WASD.TV мы наладили поддержку 6 соцсетей. Вот, что мы узнали о каждой из них в ходе настройки.

VK

У VK нет поддержки OpenID Connect и refresh-токенов. Если требуется долгая сессия, пользователю будет выдан бессрочный access-токен.

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

Facebook

В Facebook также нет refresh-токенов, но и бессрочный access-токен отсутствует. При авторизации вы получаете access-токен со сроком жизни в несколько часов. Его можно поменять на долгоиграющий, который живет 90 дней.

OpenID Connect поддерживается, но discovery информация не полная, приходится лезть в документацию для получения всех необходимых URL.

Есть и ложка дёгтя, появилась она недавно. Если раньше можно было получить сразу access-токен и ID-токен, то теперь вариантов нет, токен должен быть один. Если вам требуется взаимодействие с API Facebook, аутентифицировать пользователя за один запрос не получится, придется делать запрос профиля отдельно.

Apple

Apple воспользовалась пробелом в спецификации и при авторизации по Authorization Code Flow для передачи авторизационного кода использует POST-запрос, в то время как все остальные сети (по крайней мере те, с которыми мы работаем) используют GET-запрос. Когда выпустили iOS 13 выяснилось, что для неё надо заводить отдельные креды на стороне Apple для OAuth2.0 авторизации.

Также у них очень нестандартный способ получения секретов для клиентов. Если другие сервисы просто выдают идентификатор и секрет, то Apple отправляет идентификатор и приватный ключ, с помощью которого пользователь подписывает специально сгенерированный JWT-токен. Такой секрет живёт не более 6 месяцев.

Twitch

Если пользователи аутентифицировались через Twitch и получили access-токены, то потребуется периодическая валидация для ответа на вопрос – а не решил ли пользователь разорвать интеграцию между вашим сервисом и Twitch?.. Под интеграцией здесь подразумевается доступ вашего сервиса к API Twitch от имени пользователя. Если пользователь разорвал интеграцию, то сессия между пользователем и вашим сервисом, в рамках которой был получен access-токен тоже должна быть разорвана. Если такой проверки не будет, то во после аудита со стороны Twitch ваша платформа может оказаться заблокирована для использования API Twitch.

Google

Есть поддержка OpenID Connect, полная discovery информация. Из специфики платформы, здесь при обновлении токенов refresh-токен не обновляется. Единственная проблема, с которой столкнулись в процессе разработки это несоответствие между документацией и реальными контрактами API. Но такое бывает у многих платформ независимо от размера и известности.

Яндекс

Всё просто, получаете access-токены и обращаете в API за профилем пользователя. OpenID Connect тут нет.

Что делать?​


Наш опыт показывает, что все соцсети совершенно разные. Поэтому после принятия решения об интеграции с соцсетями нужно четко понять, что мы хотим получить. Для этого необходимо ответить на такие вопросы:

  1. Какие данные из соцсетей нужны вашей платформе кроме ID-пользователя/email – будет ли у вас интеграция или требуется только аутентификация?
  2. Что делать если не получен email из социальной сети – как будут связываться учётные записи, зарегистрированные через разные соцсети и прямую регистрацию на платформе и нужно ли это вообще? Что делать, если нет нужно идентификатора? Например, VK использует мобильный телефон как идентификатор, а почту – как дополнительный.
  3. Будет ли расширяться/меняться список социальных сетей – может быть ваша целевая аудитория сидит в одной соцсети и вам не нужно несколько интеграций?
  4. Будет ли аутентификация через социальные сети на мобильных клиентах – а есть ли вообще у вас аутентификация в мобильном приложении?

Последний пункт особенно важен, поскольку тот же Apple требует обязательно реализовывать авторизацию через них, если в вашем мобильном приложении в принципе есть авторизация. Многие соцсети имеют свои SDK для авторизации через них на мобильных платформах.

Мы на эти вопросы ответили вот так:

paijz1yiztxfjh-tdzddv4mewdk.png


Выбор велосипеда​


Для реализации интеграции с социальными сетями можно пойти двумя путями – выбрать готовое, коммерческое или Open Source решение, или написать своё. Далее рассмотрены плюсы и минусы разных подходов.

lives3-clxfvysbeyb-jqpjnzki.png


Готовый велосипед

Есть много готовых Open Source решений, среди широко известных — Ory/Kratos и Keycloak.

Плюсы очевидны:

  • Готовое решение из коробки, развернули и забыли (ну почти).
  • Поддержка множества способов аутентификации/авторизации.

Но также очевидны и минусы:

  • Эти решения – комбайны, включающие, в том числе, и прямую аутентификацию, хранение и управление профилями пользователей и многое другое. Нельзя частично переехать на них, это создаст сложности для разработки и инфраструктуры. Если вы только начинаете и экспериментируете – переезд будет легким, но если у вас уже сотня тысяч и более пользователей, такой переход может окончиться провалом. Например, из-за отличий в формате хеша паролей в вашей самописной системе аутентификации и в таком комбайне.
  • Поддержка не всех требуемых соцсетей. Пример с Apple наиболее наглядный, также нам не встретилось обновление токена для Twitch. Конечно, почти всегда есть API и можно сделать расширение. Но тут всё зависит от фантазии разработчиков соцсети. Возможно придётся сделать свой форк провайдера аутентификации и уже самостоятельно его поддерживать.
  • Можно исследовать и другие Open Source решения, но в целом плюсы и минусы будут аналогичные.

Свой велосипед

У своего решения также очевидны плюсы и минусы:

  • Можно подстроиться под имеющуюся систему аутентификации.
  • Можно реализовать все нужные соцсети.

Минус – это ваш крест и вам его нести, обеспечивая поддержку и развитие.

Оценив все плюсы и минусы, мы решили написать свой сервис.

Сервис для аутентификации через соцсети​


Микросервис для аутентификации мы назвали social-oauth2. Функционал сервиса охватывает все возможные флоу:

1. штатная аутентификация с frontend;

2. аутентификация с frontend при отсутствии ассоциации (связи) с учётной записью в социальной сети:

  • и наличии email от социальной сети;
  • и отсутствии email от социальной сети;
  • наличии email от социальной сети конфликт с существующей учётной записью на платформе;
  • и отсутствии email от социальной сети, наличии email от пользователя и конфликт с существующей учётной записью на платформе;

3. аутентификация пользователя из мобильного приложения по всем вышеперечисленным флоу.

Подробное описание всех флоу сделает статью очень объёмной, поэтому ограничусь упрощённым описанием штатной аутентификации.

Флоу штатной аутентификации

nliafk--ufe7rkqj8anck6zjhtw.png


Пользователь выбирает соцсеть для аутентификации. Ему выдаётся URL (содержит идентификатор сессии) для перехода в соцсеть. После он вводит свои креды и определяет, какие доступы разрешает для нашей платформы. Затем соцсеть выполняет редирект на нашу платформу с кодом авторизации и идентификатором сессии. Код авторизации передаётся в соцсеть вместе с необходимыми сервисным кредами. В ответ получаем access-токен, с ним обращаемся в API соцсети за профилем пользователя, откуда получаем ID пользователя. По ID в соцсети ищем пользователя на нашей платформе и в случае успеха выдаём токены клиенту.
Токены генерирует наш форк сервиса Hydra.

Что под капотом?

Сервис написан на go, база храниться в PostgreSQL, для реализации локов на строки в БД при обновлении токенов используется Redis. Запускается в docker-контейнере, настройки получает при запуске из переменных среды.

Для OAuth2 и OpenID Connect использованы библиотеки golang.org/x/oauth2 и github.com/coreos/go-oidc/v3/oidc.

Результаты эксплуатации

Созданный нами сервис успешно работает уже более двух лет и за это время каждый второй пользователь попал на WASD.TV именно через социальные сети, вход через аккаунты Yandex, Apple и Google тоже считается. В таблице показано, из каких соцсетей приходили пользователи на WASD.TV.

w-jchi0zdbovzava2ksucxoxtj4.png


За это время мы столкнулись только с одной проблемой – Facebook потребовал подтверждения политики доступа к ПД, а мы это не увидели, и наш аккаунт был заблокирован. Поэтому пользователи некоторое время не могли аутентифицироваться через Facebook.

Боли, с которыми столкнулись​


К сожалению, проблемы тоже встречались. Живое тестирование с реальными соцсетями — это весьма трудоёмкий процесс. Так что моки – наше всё, но надо их актуализировать при изменении в API соцсетей. Если есть ресурсы – стоит написать автотесты для соцсети с реальным выполнение процесса аутентификации в браузере, у нас на проекте такие есть.

Ещё одна боль – это контроль учётных записей, как сервисных, так и тестовых. Их потребуется как минимум три: для получения кред прода, для получения кред тестовых стендов и учётная запись тестового пользователя в соцсести. Ряд соцсетей требует телефонный номер для регистрации или проверки 2FA, поэтому подумайте, как несколько разработчиков и тестировщиков получат к нему доступ. Мы для этого обзавелись виртуальным номером.

Что в итоге?​


Аутентификация через соцсети очень упрощает жизнь пользователю и способна повысить трафик на вашей платформе. Больше людей решат остаться, а не уйти, увидев ненавистное окно с регистрацией.

Но эта аутентификация не должна реализовываться как MVP, ее нужно сразу продумывать. Цена ошибки здесь заметно выше, чем при прямой авторизации, нужно учитывать то, что соцсети – это сторонние сервисы со своими разработчиками и регулярными изменениями.

Готовые библиотеки для OAuth2.0 и OpenID Connect сэкономят время и упростят процесс интеграции, но совсем всё на себя не возьмут. Если вы только начинаете проект, то стоит посмотреть в сторону готовых провайдеров аутентификации. Однако надо быть готовым, что при развитии проекта потребуется какая-то доработка. И своё решение с учётом перспектив окажется предпочтительнее.

 
Сверху