Кто помнит пароли от всех своих аккаунтов? Учеток слишком много, ведь почти каждый сервис в наше время требует для полноценного доступа создать учётную запись и придумать новый пароль.
Столкнувшись с очередным таким требованием, особенно на новой платформе, человек просто закрывает вкладку в браузере со словами «не больно-то и хотелось». Чтобы упростить жизнь пользователям – существует аутентификация через третьи сервисы (чаще социальные сети).
Привет, Хабр, меня зовут Сергей Солдатов, я тимлид в подразделении eSports MTS Digital, разработчик на Go и немного архитектор. Занимаюсь сервисами аутентификации и авторизации на WASD.TV. Это многофункциональная стриминговая платформа для геймеров, собственный сервис МТС. Как мы настраивали аутентификацию на WASD.TV и что из этого вышло – читайте под катом.
Как я уже сказал, наиболее удобна аутентификация через социальные сети, ведь почти у каждого из нас есть аккаунт хотя бы в одной из них.
Наглядный пример – вы аутентифицируетесь в Google и потом, не вводя снова логина и пароля, можете зайти в аккаунт на Miro, YouTube, WASD.TV, Яндексе. Это нормальная ситуация, когда в одной экосистеме можно пройти аутентификацию с помощью другой. Могут быть целые матрёшки: на WASD.TV проходим аутентификацию через Яндекс, а уже там выбираем Facebook и заходим в свой профиль.
Интеграция становится всё теснее и пользователям предлагается не только аутентификация, но и авторизация. Так платформа, на которую вы пришли, может совершать от вашего имени действия на другой платформе, где вы прошли аутентификацию.
Такая авторизация/аутентификация стала возможна благодаря протоколам OAuth2.0 и OpenID Connect. Последнее не стоит путать с OpenID, это совершенно разные протоколы.
Протоколы OAuth2.0 и OpenID Connect
OAuth2.0 – это авторизация, OpenID Connect – аутентификация. OpenID Connect – это надстройка над OAuth2.0.
При авторизации через OAuth2.0 есть три основные сущности: владелец ресурса, сервис ресурсов и клиент. Последний – это внешнее приложение, которое хочет получить доступ к ресурсу. На схеме показана абстрактная схема взаимодействия.
Порядок такой:
- Приложение запрашивает у пользователя авторизацию на доступ к серверу ресурсов.
- Если пользователь авторизует запрос, приложение получает разрешение на авторизацию (authorization grant).
- Приложение запрашивает авторизационный токен у сервера авторизации (API) путём предоставления информации о самом себе и разрешении на авторизацию от пользователя.
- Если подлинность приложения подтверждена и разрешение на авторизацию действительно, сервер авторизации (API) создаёт токен доступа для приложения. Процесс авторизации завершён.
- Приложение запрашивает ресурс у сервера ресурсов (API), предоставляя при этом токен доступа для аутентификации.
Если токен действителен, сервер ресурсов (API) предоставляет запрашиваемый ресурс приложению.
По стандарту OAuth2.0 есть два вида токенов – accеss и refresh. Первый предназначен для доступа к API сервера ресурсов и живет он недолго, обычно несколько часов. Второй не даёт доступа, но позволяет обновить первый токен, его срок жизни — до нескольких месяцев. При обновлении в зависимости от реализации меняются либо оба токена (более безопасный вариант) либо только access-токен.
Стандарт OAuth2 никак не регламентирует и не ограничивает формат токенов, но обычно они состоят из двух частей:
- случайной последовательности символов заданной длины;
- подписи данной последовательности символов.
{
"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 оставляют большой простор для конкретной реализации. И подключение различных социальных сетей для аутентификации иногда напоминает поход по минному полю. На WASD.TV мы наладили поддержку 6 соцсетей. Вот, что мы узнали о каждой из них в ходе настройки.
VK
У VK нет поддержки OpenID Connect и refresh-токенов. Если требуется долгая сессия, пользователю будет выдан бессрочный access-токен.
Также при интеграции с VK следует учесть, здесь уже много лет основной способ идентификации пользователя – номер мобильного телефона.
В 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.
Есть поддержка OpenID Connect, полная discovery информация. Из специфики платформы, здесь при обновлении токенов refresh-токен не обновляется. Единственная проблема, с которой столкнулись в процессе разработки это несоответствие между документацией и реальными контрактами API. Но такое бывает у многих платформ независимо от размера и известности.
Яндекс
Всё просто, получаете access-токены и обращаете в API за профилем пользователя. OpenID Connect тут нет.
Что делать?
Наш опыт показывает, что все соцсети совершенно разные. Поэтому после принятия решения об интеграции с соцсетями нужно четко понять, что мы хотим получить. Для этого необходимо ответить на такие вопросы:
- Какие данные из соцсетей нужны вашей платформе кроме ID-пользователя/email – будет ли у вас интеграция или требуется только аутентификация?
- Что делать если не получен email из социальной сети – как будут связываться учётные записи, зарегистрированные через разные соцсети и прямую регистрацию на платформе и нужно ли это вообще? Что делать, если нет нужно идентификатора? Например, VK использует мобильный телефон как идентификатор, а почту – как дополнительный.
- Будет ли расширяться/меняться список социальных сетей – может быть ваша целевая аудитория сидит в одной соцсети и вам не нужно несколько интеграций?
- Будет ли аутентификация через социальные сети на мобильных клиентах – а есть ли вообще у вас аутентификация в мобильном приложении?
Последний пункт особенно важен, поскольку тот же Apple требует обязательно реализовывать авторизацию через них, если в вашем мобильном приложении в принципе есть авторизация. Многие соцсети имеют свои SDK для авторизации через них на мобильных платформах.
Мы на эти вопросы ответили вот так:
Выбор велосипеда
Для реализации интеграции с социальными сетями можно пойти двумя путями – выбрать готовое, коммерческое или Open Source решение, или написать своё. Далее рассмотрены плюсы и минусы разных подходов.
Готовый велосипед
Есть много готовых Open Source решений, среди широко известных — Ory/Kratos и Keycloak.
Плюсы очевидны:
- Готовое решение из коробки, развернули и забыли (ну почти).
- Поддержка множества способов аутентификации/авторизации.
Но также очевидны и минусы:
- Эти решения – комбайны, включающие, в том числе, и прямую аутентификацию, хранение и управление профилями пользователей и многое другое. Нельзя частично переехать на них, это создаст сложности для разработки и инфраструктуры. Если вы только начинаете и экспериментируете – переезд будет легким, но если у вас уже сотня тысяч и более пользователей, такой переход может окончиться провалом. Например, из-за отличий в формате хеша паролей в вашей самописной системе аутентификации и в таком комбайне.
- Поддержка не всех требуемых соцсетей. Пример с Apple наиболее наглядный, также нам не встретилось обновление токена для Twitch. Конечно, почти всегда есть API и можно сделать расширение. Но тут всё зависит от фантазии разработчиков соцсети. Возможно придётся сделать свой форк провайдера аутентификации и уже самостоятельно его поддерживать.
- Можно исследовать и другие Open Source решения, но в целом плюсы и минусы будут аналогичные.
Свой велосипед
У своего решения также очевидны плюсы и минусы:
- Можно подстроиться под имеющуюся систему аутентификации.
- Можно реализовать все нужные соцсети.
Минус – это ваш крест и вам его нести, обеспечивая поддержку и развитие.
Оценив все плюсы и минусы, мы решили написать свой сервис.
Сервис для аутентификации через соцсети
Микросервис для аутентификации мы назвали social-oauth2. Функционал сервиса охватывает все возможные флоу:
1. штатная аутентификация с frontend;
2. аутентификация с frontend при отсутствии ассоциации (связи) с учётной записью в социальной сети:
- и наличии email от социальной сети;
- и отсутствии email от социальной сети;
- наличии email от социальной сети конфликт с существующей учётной записью на платформе;
- и отсутствии email от социальной сети, наличии email от пользователя и конфликт с существующей учётной записью на платформе;
3. аутентификация пользователя из мобильного приложения по всем вышеперечисленным флоу.
Подробное описание всех флоу сделает статью очень объёмной, поэтому ограничусь упрощённым описанием штатной аутентификации.
Флоу штатной аутентификации
Пользователь выбирает соцсеть для аутентификации. Ему выдаётся 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.
За это время мы столкнулись только с одной проблемой – Facebook потребовал подтверждения политики доступа к ПД, а мы это не увидели, и наш аккаунт был заблокирован. Поэтому пользователи некоторое время не могли аутентифицироваться через Facebook.
Боли, с которыми столкнулись
К сожалению, проблемы тоже встречались. Живое тестирование с реальными соцсетями — это весьма трудоёмкий процесс. Так что моки – наше всё, но надо их актуализировать при изменении в API соцсетей. Если есть ресурсы – стоит написать автотесты для соцсети с реальным выполнение процесса аутентификации в браузере, у нас на проекте такие есть.
Ещё одна боль – это контроль учётных записей, как сервисных, так и тестовых. Их потребуется как минимум три: для получения кред прода, для получения кред тестовых стендов и учётная запись тестового пользователя в соцсести. Ряд соцсетей требует телефонный номер для регистрации или проверки 2FA, поэтому подумайте, как несколько разработчиков и тестировщиков получат к нему доступ. Мы для этого обзавелись виртуальным номером.
Что в итоге?
Аутентификация через соцсети очень упрощает жизнь пользователю и способна повысить трафик на вашей платформе. Больше людей решат остаться, а не уйти, увидев ненавистное окно с регистрацией.
Но эта аутентификация не должна реализовываться как MVP, ее нужно сразу продумывать. Цена ошибки здесь заметно выше, чем при прямой авторизации, нужно учитывать то, что соцсети – это сторонние сервисы со своими разработчиками и регулярными изменениями.
Готовые библиотеки для OAuth2.0 и OpenID Connect сэкономят время и упростят процесс интеграции, но совсем всё на себя не возьмут. Если вы только начинаете проект, то стоит посмотреть в сторону готовых провайдеров аутентификации. Однако надо быть готовым, что при развитии проекта потребуется какая-то доработка. И своё решение с учётом перспектив окажется предпочтительнее.
Если у вас есть вопросы или опыт создания и настройки сервисов для аутентификации – добро пожаловать в комментарии!
Комментарии (11)
irbis_al
13.01.2022 23:27Согласен что авторизация через сети головняк.
Вот попробовал oauth2 на go с примера
(см правилый ответ)
Завел на Facebook приложение получил client-id ,клиент секрет увидел ,-вставил...запустил ...в браузере,- одна ссылка на странице login facebook / давим / Первое что facebook не нравится надо добавить домены(я ну как бы со страрта ...имею только localhost...)Добавляю localhost в настройки приложения на сайте developer.facebook...Теперь ему(facebook) не нравится...что не https :-)
Дорабатываем типа https://github.com/denji/golang-tls
Запускаем
Теперь ему не нравится самоподписанный сертификат...(Да и calback куда оно сделает?(Не на localhost же)..но до calbback еще далеко)....
Т.е чтоб это оттестировать мне надо...реальный IP(или арендовать VPS) купить домен к нему ,организовать ключ и сертификат к этому домену...и лишь потом скормить это facebook,убедившись что авторизация через сеть работает?
Есть ли проще путь?
vassabi
14.01.2022 00:32+1ngrock не пробовали? (хинт - запускайте либо из пустой директории, либо из директории с файлами, которые не жалко выставить всем на чтение :) )
irbis_al
14.01.2022 10:51Благодарю...прикольный сервис...Попробовал с ним..и больше вопросов чем ответов...:-)
Он создал ссылку https://cd1c-194-187-149-96.ngrok.io
Поправил код...подправил личный квбинет facebook приложения...и тут совсем странно пишет что домен не внесен в приложение,..хотя он точно внесен..я вот даже видно набросал ниже по ссылке
Protos
15.01.2022 08:40Видимо в фейсбуке знаю об этом хитром способе и так его лочат
irbis_al
15.01.2022 08:45Не там надо с бубном потанцевать...ниже ответ написал (возможно в другой ветке)
Цитирую,что ответил
После поиска решения невхода, что домен не внесен(Текс ошибки не соответствует причине ) выяснилось что надо отключить "Использовать строгий режим для URI перенаправления " Я его отключить не мог почему-то...просто добавил uri .. Всё одно при входе (см видео) пишет красный треугольник предупреждения(как его убрать ? кто знает?)...а дальше возвращает токен в прогу
irbis_al
14.01.2022 12:00Ещё раз благодарю за ссылку ngrock...Без него сложно оттестировать вход в соц сети...жаль что после перезапуска сервиса ngrock ссылка новая.
После поиска решения невхода что домен не внесен(Текс ошибки не соответствует причине ) выяснилось что надо отключить "Использовать строгий режим для URI перенаправления " Я его отключить не мог почему-то...просто добавил uri .. Всё одно при входе (см видео) пишет красный треугольник предупреждения(как его убрать ? кто знает?)...а дальше возвращает токен в прогу
TerrorDroid
14.01.2022 14:52+3Можете называть меня ретроградом, но я не соглашусь с изначальным тезисом статьи о том, что сложно или плохо иметь отдельные учетки для отдельных сервисов или сайтов. Это намного лучше, чем иметь учетки, которые привязаны к другим учеткам, ибо шанс того, что этот карточный домик интеграций сломается по тем или иным техническим причинам слишком велик. Или все сломается просто потому, что вы потеряете доступ к изначальной учетке, на основе которой происходил вход в другой сервис.
Мы живём в мире, где любой крупный сервис не отвечает ни за что перед своими обычными пользователями. Если вы не VIP-пользователь, для которых существует отдельный мирок, то вашу учетную запись в любой момент может забанить поехавшая автоматика, которая усмотрит своими кривыми алгоритмами в вашем поведении «что-то не то». Вас забанит анонимный и неподотчетный модератор с синдром вахтёра, либо потому что у него было плохое настроение, либо потому, что на вас настрочили жалоб какие-то троли. Вашу учетку банально заблокирует «умная» эвристика по причине «он зашёл через VPN!!!!111 Полундра!!!11», несмотря на то, что вы пол года уже заходили в своё учетку через один тот же VPN. Как бонусные очки: заблокированную учетку якобы можно будет разблокировать путём сброса пароля через ответ на секретный вопрос, но система не будет принимать его, несмотря на то, что он у вас храниться в менеджере паролей. Реальная проблема, если что. К слову тот же самый ответ на секретный вопрос потом магическим образом сработает через пол года какого-то непрозрачного таймаута при блокировке.
И у вас не будет ни саппорта, ни эскалации проблемы, ни обжалования и возможности оспорить это. И вы хотите привязываться к этой помойке? Нет, спасибо.
ssa-company Автор
14.01.2022 18:09Я соглашусь с Вами, сам где-то использую авторизацию через соцсети, а где-то только прямую. Мы предоставляем выбор пользователю. И этот выбор, судя по анализу статистики, привлекает новых пользователей.
Насчёт вашего кейса с блокирокой - всё сильно зависит от реализации конкретного сервиса. На нашей платформе не запрещено совмещать несколько авторизаций завязанных на один аккаунт платформы - можно зайти напрямую, можно зайти через соцсети. Если везде на момент регистрации была использована одна почта, то вы всегда откроете один аккаунт. Т.е. если вашу учётную запись заблокировала соцсеть, вы можете восстановить пароль на почту и зайти через прямую авторизацию.
На некоторых сервисах, особенно, если аккаунт становиться корпоративным, авторизации разными способами приводят к попаданию в разные аккануты.
mgis
Немного не по теме, ребят подскажите пожалуйста, я в процессе написания своего Saas сервиса, так вот. Как лучше сделать авторизацию? Мне видится что проше всего для пользователя это авторизация по номеру телефона, через короткий пароль высылаемый по смс. Так сделано у Тинькофа и Озона, довольно удобно и процент отказа скорее всего очень низкий.
ssa-company Автор
Не все готовы оставлять свой номер телефона. Одно дело банк или крупный маркетплейс, другое дело, если некая новая платформа. Никто не хочет потом телефонного спама. К тому же, по номеру телефона гораздо проще отследить владельца. Ваша целевая аудитория согласится на это? В-третьих, смс не бесплатны, в отличии от пинкода отправленного на почту. Ну и стоит напомнить, в про уязвимость SS7 - Уязвимость в протоколе SS7 уже несколько лет используют для перехвата SMS и обхода двухфакторной аутентификации / Хабр (habr.com).
И чуть не забыл про кейс с переходом номера от одного владельца к другому. Зачастую, если человек не пользуется номером более 6 месяцев, номер считается свободным и может уйти к другому человеку. Сможет ли он зарегистрироваться на вашем сервисе? Ведь номер может оказаться занят.