#oauth-2.0 #openid-connect
#oauth-2.0 #OpenID-connect
Вопрос:
Учитывая ситуацию, когда общедоступный клиент Javascript хочет получить доступ к конечной точке API, используя тип предоставления учетных данных пароля владельца ресурса.
Типичный запрос будет выглядеть следующим образом:
username = "john.doe@mail.com"
password = "MyP@assw0rd!"
grant_type = "password"
scope = "openid offline_access"
Не client_id
передается, поскольку клиент не может сохранить client_secret
и в соответствии со спецификацией предоставления учетных данных пароля владельца ресурса, он может быть опущен.
Сервер авторизации ДОЛЖЕН:
- требовать проверки подлинности клиента для конфиденциальных клиентов или для любого клиента, которому были выданы учетные данные клиента (или с другими требованиями к аутентификации)
Проблема в том, что scope = "openid offline_access"
параметр не может быть проверен, если клиент неизвестен. В моем понимании области предназначены для описания разрешений приложений.
Сразу возникает вопрос.
Как проверить области клиента, когда client_id
это опущено (намеренно и по спецификации)?
Некоторые рассуждения:
-
Причина исключения
client_id
заключается в том, что любой может легко получить его из клиента JS и использовать его. -
Проверка CORS в этом случае добавляет почти нулевое значение, поскольку заголовок узла может быть создан вручную.
Ответ №1:
TL; DR Перейдите к разделу выводов ниже…
Это правда, что client_id
сохраненные в Javascript данные можно легко получить, но этот идентификатор не является секретом, в спецификации это явно указано.
Идентификатор клиента не является секретным; он предоставляется владельцу ресурса и НЕ ДОЛЖЕН использоваться отдельно для аутентификации клиента.
(источник: RFC 6749, раздел 2.2)
Это означает, что допустимо иметь client_id
доступ к вашему общедоступному клиенту, поэтому теперь давайте сосредоточимся на второй проблеме … аутентификации клиента.
В приведенном вами разделе говорится, что аутентификация клиента должна требоваться для конфиденциальных клиентов или любого клиента, которому были выданы учетные данные. Ваше приложение не подпадает ни под один из этих случаев, поэтому требование аутентификации клиента неприменимо.
Итак, ваш клиент не требует (и фактически не может выполнять) проверку подлинности клиента. Это вызывает проблему для вашего сценария, поскольку вы хотите проверить запрошенные области, поэтому давайте попробуем найти совместимое решение…
Прежде всего, нужно найти способ передать client_id
на сервер, потому что это будет необходимо. Если бы клиент был конфиденциальным, это было бы передано в Authorization
заголовке вместе с его секретом, но мы не в этом сценарии. Однако спецификация позволяет client_secret
опустить, поэтому позволяет по-прежнему использовать этот HTTP-заголовок для передачи идентификатора клиента.
client_secret: ТРЕБУЕТСЯ. Секрет клиента. Клиент МОЖЕТ опустить параметр, если секрет клиента является пустой строкой.
(источник: RFC 6749, раздел 2.3.1)
Теперь у нас есть client_id
на стороне сервера, но мы не можем доверять ему, не в соответствии со спецификацией, потому что, как мы уже упоминали, мы не можем использовать только этот идентификатор для аутентификации клиента, и даже если бы мы попробовали какой-то умный (иначе очень легко ошибиться, не зная об этом) механизм аутентификации, также:
Сервер авторизации МОЖЕТ установить метод аутентификации клиента для общедоступных клиентов. Однако сервер авторизации НЕ ДОЛЖЕН полагаться на аутентификацию общедоступного клиента с целью идентификации клиента.
(источник: RFC 6749, раздел 2.3)
Черт, это никуда не годится! Я не могу пройти аутентификацию, поэтому я не могу доверять идентификатору клиента, поэтому я не могу проверить области.
Я вас слышу, но все еще есть некоторая надежда. Теперь давайте сосредоточимся на другом разделе спецификации.
Когда аутентификация клиента невозможна, сервер авторизации ДОЛЖЕН использовать другие средства для проверки подлинности клиента — например, путем требования регистрации URI перенаправления клиента или привлечения владельца ресурса для подтверждения личности.
(источник: RFC 6749, раздел 10.1)
ДЖЕКПОТ!Вы включили владельца ресурса в процесс подтверждения личности клиента, черт возьми, он дал свое имя пользователя и пароль клиенту, так что все улажено.
Выводы
Похоже, у вас есть способ выполнить поставленную задачу и по-прежнему требовать соответствия спецификации (по крайней мере, со стороны OAuth 2.0). вещей). Единственное, что осталось сделать, это то, что это всего лишь мое мнение, поэтому я хотел бы также привести вам пример с реальной реализацией конечной точки владельца ресурса.
Если вы перейдете к API аутентификации Auth0 — конечная точка владельца ресурса, вы найдете конкретную реализацию. Всегда приятно видеть, как кто-то другой подходит к проблеме. Я уже могу отметить, что эта реализация решила разрешить client_id
само тело запроса и иметь несколько других пользовательских параметров в качестве опций, но это нормально, поскольку OAuth 2.0 не определяет строгий протокол, а просто определяет основы.
И последнее, на что я хотел обратить ваше внимание, это то, что offline_access
область используется в OpenID Connect для сигнализации о том, что вам нужен токен обновления, но вы не должны этого делать для общедоступных клиентов. Хранение токена обновления, который обычно является долговременными учетными данными, почти так же плохо, как хранение фактического пароля. Некоторые общие указания по этим типам токенов см. В разделе Обновление токенов.
Комментарии:
1. Прежде всего, большое вам спасибо за редактирование моего вопроса и предоставление подробного ответа! Просто чтобы дважды проверить решение. Таким
enlisting the resource owner
образом, это означает (технически) правильное предоставление его учетных данных? С сервера авторизации справедливо предположить, что если предоставлены действительные учетные данные, то пользователь использовал «существующий» на некотором уровне «доверенный» клиент. Тем не менее, даже если мы знаем, что клиент «МОЖЕТ» быть доверенным, нам нужно его идентифицировать. Как? По заголовку хоста запроса? (Redirect_URI в соответствии со спецификацией также может добавить дополнительную проверку, но не окончательную) : (2. Чтобы идентифицировать клиента, просто отправьте его
client_id
по запросу. Вы можете сделать это черезAuthorization
заголовок и отправить вместе с пустым секретом клиента, или вы можете просто включить его в само тело запроса, аналогично реализации Auth0. Вы можете доверять этому удостоверению, предполагая, что если учетные данные пользователя действительны, используемое приложение фактически является законным.3. Понятно! Отлично! Большое вам спасибо за вашу помощь и за замечания по
access_token
;). Я потрачу больше времени на чтение спецификаций
Ответ №2:
Предположение о том, что области могут проверяться для каждого клиента в таком потоке, неверно. Поскольку идентификатор не client_id
является секретным, он не добавляет значения. Любой может получить его из вашей реализации Javascript и выдать себя за клиента, подделав учетные данные пользователя. Следовательно, запрошенные области могут обрабатываться только общим, не зависящим от клиента способом, но, кроме того:
спецификация OAuth 2.0 не рекомендует использовать поток учетных данных пароля владельца ресурса в таких ситуациях, https://www.rfc-editor.org/rfc/rfc6749#section-1.3.3:
Учетные данные следует использовать только тогда, когда существует высокая степень доверия между владельцем ресурса и клиентом (например, клиент является частью операционной системы устройства или
приложения с высокими привилегиями), и когда другие типы предоставления авторизации
недоступны (например, код авторизации).
В случае с Javascript в браузере не может быть высокой степени доверия между владельцем ресурса и Клиентом, поскольку Клиент не может быть надежно идентифицирован.
Комментарии:
1. Я бы сказал, что это серая область, но не назвал бы ее неправильной. Любой может запрашивать учетные данные пользователей, не зная никакого идентификатора клиента. Области все равно должны быть проверены, учитывая, что это общедоступный клиент (запрашивающий автономный доступ, должно быть «нет»), но если у злоумышленника есть учетные данные пользователя, это своего рода игра в любом случае, независимо от потока. Согласен, что этого потока следует избегать, но вопрос не в этом.