Комментарии 2
Сколько я уже разрабатываю веб-сервисы, еще не разу не видел адектавно реализованого механизма аутентификации и авторизации. Возникает ощущение, что никто в компании не понимает, как использовать стандартные протоколы, включая кастомных Identity Provider-ов, например на базе того же IdentityServer4.
Я выделил следующие типы странностей при использовании OAuth2 и OIDC:
Отправка
id_token
в API; Вроде как этот токен часть флоу аутентификации, форма токена вполне стандартная и не подразумевает разнообразиеclaims
достаточных, для авторизации в API;Отсутсвие верификации
audience
иscope
при передачеaccess_token
. Т.е. фактически токен может быть получен для другого API, но при этом нормально восприниматься любым API в системе. Это как будто бы нарушает принцип наименьших привелегий и оставляет кучу дыр в безопасности;Пинг-понг токенами (
access_token
иid_token
) между сервисами. Это развивает идеи предыдущего пункта. Т.е. в любой API можно идти с любым токеном, который выдан корректным identity provider. Данный подход начинает играть еще более интересными красками, когда где-то в середине "партии" токен истекает.
У меня накопились следующие вопросы по использованию OAuth2 и OIDC:
Правильно ли я понимаю, что OAuth2 был придуман для реализации интеграций с другими сервисами? Т.е. реализуется сценарий делегирования, когда пользователь передает Сервису Б права доступа работы с данными в Сервисе А. Примеры пользовательских сценариев: пользователь логинится в почтовом аггрегаторе. Далее пользователь передает почтовому аггрегатору права доступа к почте пользователя используя OAuth2 +
offline_access
.Правильно ли я понимаю, что OIDC был придуман как замена SAML, и был реализован в качестве расширения к OAuth2?
Корректен ли следующий пользовательский сценарий: пользователь нажимает на кнопку "Войти через Google" в моем веб-сервисе. Далее пользователь проходит аутентификацию в Google и идет обратный редирект в мое приложение.
Далее если это классическое веб-приложение (ASP.NET Core, Ruby on Rails & etc), приложение опционально делает ассоциацию пользователя в БД с
subject
изid_token
полученного от Google и например далее записывает состояние аутентификации в куки?Далее если это SPA (React, Angular),
id_token
остается в приложении и... тут мое понимание зачем это надо теряется. Возвращаемся к кейсу отправкиid_token
к API, что противоречит рекоммендациям OAuth2.
Поясните, пожалуйста, как OAuth2 должен работать в экосистеме веб-сервиса? Предположим, если веб-сервис, в котором есть несколько приложений. Например, Client Portal (панель управления клиента) - Next.JS; Developer Portal (где клиенты могут регистрировать свои клиенты в Identity Provider и покупать лицензии на интеграции) - ASP.NET Core; и Partner Portal (где партнеры могут выставлять свои API в маркетплейс Developer Portal) - ReactJS SPA. Предположим что в системе есть свой Identity Provider, который реализует OAuth2 и OIDC.
Как должны работать внутренние приложения? Каждое приложение должно иметь статический клиент в identity Provider?
Нужно ли Next.JS и ASP.NET Core использовать OAuth2 и писать
access_token
иrefresh_token
в куки? Или достаточно использовать OIDC? Или комбинация?Как поступить с ReactJS SPA? Куда девать
id_token
после логина? Он вообще нужен в этом случае?Что насчет API? Каждое API должно иметь свой
resource_id
иscope-ы
? Так же каждый API должен иметь список требуемых емуclaims
для работы (например,user.role
илиorg.id
).Значит ли это, что для каждого статического клиента нужно задать список API к которым он обращается? Этот список заранее известен, поэтому, кажется, это не должно стать проблемой?
Необходимо ли проверять
audience
claim
на стороне API? Какие последствия, если проигнорировать эту проверку?Как реализовать коммуникацию между микросервисами за пределами API Gateway? Гонять токены пользователя? Использовать
client_credentials
флоу? Еслиclient_credentials
флоу, то нужно ли ограничивать пермишены клиентов? Типа Profile Microservice может ходить в Subscriptions Microservice на чтение, и не может ходить в остальные сервисы? Или OAuth2 здесь вообще не нужен?
В общем такие вот вопросы. В целом на всех проектах, на которых я работал, это сделано "как-то" и работает соответствующе. Т.е. используются OAuth2 и OIDC для приложений, которые являются часть экосистемы, но одна команда везде использует id_token
, другая access_token
. В целом получается кое-как используется одно ключевое свойтво этого механизма - оба токена содержат subject
для идентификации пользователия и механизм валидации издателя токена для аутентификации. Поэтому выглядит так, что все работает, включая SSO. А чтобы понять,какая комбинация параметров правильная и действительно безопасная для каждого конкретного пользовательского сценария - компетенций и знаний уже не хватает. А потом в вся система пропитывается таким миксом подходов, один костыль начинает подпирать другой и вот уже переделать это кажется невыполнимой задачей. Тем боеле, когда все еще не знаешь, а как правильно-то делать :) Это надо какую-то песочницу заводить и в ней ковыряться, воспроизводить ключевые системы, ключевые флоу, желательно в паре с экспертом в безопасности, в частности в OAuth2 и OIDC. Иначе получатся те же яйца, только в профиль.
Немного расширю своим виденьем, как это возможно надо делать, поправьте меня, если неправ.
OAuth2 только для внешних интеграций с нашим сервисом.
Пользователь делегирует доступ какому-то third-party сервису;
Пользователь делегирует доступ своему собственному сервису для партнерской интеграции; тут было бы неплохо как-то через клиент работать - т.е. создавать специальный партнерский API, регистрировать клиент в системе и присваивать напрмямую клиенту нужные для авторизации клеймы. И то скорее все не получится поместить в клеймы, скорее всего будет какая-то рантайм проверка.
Как альтернативу OAuth2 для обоих типов интеграций можно использовать API Keys (там свои нюансы с ротацией, но как вариант).
OIDC как Identity Provider для логина в другой сервис / приложение через наш сервис.
OIDC для предоставления SSO в нашей платформе.
Наши веб-приложения на ASP.NET Core / NextJS получают
id_token
, и сами идут в нужные микросервисы, чтобы собрать объектsession
и либо положить его в куки, либо положитьsession_id
в куки, а самsession
в хранилище сессий, чтобы поддерживать выход из определенных сессий. Далее все взаимодействия с внутренними микросервисами идут через это веб-приложение. Или другие варианты реализации, но суть в том, что будут использоваться secure cookie.Наши SPA на ReactJS / Angular работают c BFF. BFF реализует такой же механизм, как и в предыдущем сервисе через куки и служит единой точкой запросов SPA.
Мобильные приложения работают так же работают с BFF. Пользователь логинится в webview, далее одно двух -
session_id
отправляется в мобильное приложение и хранится в зашифрованном виде; либо реализуется механизмjwt
+refresh_token
, с хранилищем активныхrefresh_tokens
. Все необходимые запросы к нашему сервису приложение делает через BFF.Сервисы между собой аутентифицируются по комбинации следующих параметров:
Изоляция сети
iAM: что-то типа Open Policy Agent (OPA) + Gatekeeper
Сервисы интегрируются с внешними сервисами по OAuth2.
Т.е. OAuth2 вообще никак не фигурирует во внутренних сервисах, кроме интеграций с внешним миром.
Альетернативно OIDC, можно реализовать механизм "попроще", когда сервер возвращает подписанный
jwt
на заранее сконфигугированные разрешенныеcallback_url
, а дальше то же самое -session
,session_id
, secure cookie. По сути тот же OIDC (только замениid_token
из флоу выше наjwt
), клиенты точно так же заранее сконфигурированы и имеютcallback_url
. Но решение сjwt
не тянет за собой ритуалов навязанных базисом OAuth2. Но тут надо подумать.
Получается OAuth2 только для интеграций. OIDC возможно для SSO, но можно и без него через jwt
(опять же, чтобы не путать карты). А в сервисы вообще эта муть не тянется, там используются соответствующие инфраструктурные инструменты. И в итоге каждый решает ту проблему, для которой был создан. Но это все в теории, на практике, конечно, надо собирать сетап в песочнице.
Введение в OAuth и OpenID Connect