Я обнаружил регресс в мастере при использовании с request / requests-oauthlib, поскольку https://github.com/oauthlib/oauthlib/issues/495 был объединен. Это относится только к разрешению / веб-приложению.
Основное использование requests-oauthlib:
sess = OAuth2Session(client_id)
token = sess.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url)
Однако после внесения изменений client_id
сеанса игнорируется. Я думаю, что https://github.com/oauthlib/oauthlib/pull/505 исправил один вариант использования, но сломал другой. Мы должны найти беспроигрышное решение.
Вызов кода requests-oauthlib по адресу https://github.com/requests/requests-oauthlib/blob/master/requests_oauthlib/oauth2_session.py#L196 -L198 и проблема oauthlib здесь
https://github.com/oauthlib/oauthlib/blame/master/oauthlib/oauth2/rfc6749/clients/web_application.py#L128.
Моя первая мысль - отменить изменения в prepare_request_body
чтобы по умолчанию использовать self.client_id
установленный в конструкторе WebApplicationClient
.
Затем следует изменить встроенные документы, чтобы добавить &client_id=xx
к выходу prepare_request_body
.
Наконец, чтобы заменить исходное исправление, я предлагаю удалить client_id
из аргументов и добавить новый аргумент в prepare_request_body
например include_client=True/False
чтобы добавить client_id
и client_secret
в теле, или не включать их оба.
Мысли?
тыкай @Diaoul @skion @thedrow
Что о:
Я предлагаю удалить client_id из аргументов и добавить новый аргумент в prepare_request_body, например include_client = True / False, чтобы добавить client_id и client_secret в тело или не включать их оба.
Спасибо
Я действительно вызвал эту же проблему в одном из своих тестов, но подал ее против запросов / oauthlib здесь: https://github.com/requests/requests-oauthlib/issues/330
Я считаю, что проблема на самом деле связана с request_oauthlib. Их документы - фактически первый пример во всей их документации при загрузке страницы - поддерживают указание client_id
в конструкторе OAuth2Session
. Логика в строке 200 извлекает client_id
из kwargs, но не имеет запасного варианта для извлечения его из уже WebApplicationClient
экземпляра
@jvanasco , текущая проблема # 585 может быть исправлена только с помощью request-oauthlib или путем восстановления PR # 505 oauthlib. Однако ни одно из решений не исправит поведение, упомянутое @skion в его комментарии на https://github.com/oauthlib/oauthlib/pull/505#issuecomment -351221107.
Согласно спецификации, параметр client_id должен быть отправлен для неаутентифицированных клиентов, но предпочтительно НЕ отправляется в теле запроса токена для конфиденциальных клиентов, поскольку в этом случае предпочтительный механизм аутентификации клиента - через HTTP Basic auth. Однако класс WebApplication всегда включает его (что ломает некоторые серверы) и не предлагает механизма для его удаления.
Oauthlib должен предоставлять элегантный и простой способ для request-oauthlib решить проблему. Если мы сможем найти решение в этом обсуждении, это будет здорово; потому что это огромный блокиратор.
Будет ли разрешено client_id=False
в prepare_request_body()
help указывать, что client_id не нужно отправлять? Хотя даже если это так, это приведет к этому уродству около строки 126 :
client_id = None if client_id=False else self.client_id
Ах я вижу.
Есть ли существующий модульный тест, когда нужно отправлять client_id, а не нет? Если нет, есть ли у кого-нибудь список, который можно использовать для его создания? Я был бы счастлив попытаться исправить это и запросить-oauthlib, потому что он блокирует часть моей работы прямо сейчас.
@JonathanHuot
Я предлагаю удалить client_id из аргументов и добавить новый аргумент в prepare_request_body, например include_client = True / False, чтобы добавить client_id и client_secret в тело или не включать их оба.
Читая это в ответ, мне действительно нравится ваше предложение. Мы, вероятно, сможем избежать этого, сделав это безотказно, поскольку функция уже принимает **kwargs
.
Одна нота:
чтобы добавить client_id и client_secret в тело
Поскольку речь идет о IIUC публичных клиентов, я бы не подумал, что здесь задействован client_secret
; это просто client_id
добавляется в тело? В этом случае я бы также подумал о переименовании нового параметра в include_client_id=True/False
.
В этом случае я бы также подумал о переименовании нового параметра в include_client_id = True / False.
Действительно ! client_secret
не участвует, так как его нет в WebApplicationClient
.
@jvanasco , если вы хотите сделать пиар, я думаю, что изменения должны быть такими:
1) Вернуть https://github.com/oauthlib/oauthlib/pull/505
2) Измените подпись prepare_request_body()
чтобы удалить client_id
и добавить include_client_id=True/False
( True
(по умолчанию): добавляет self.client_id
)
Затем в https://github.com/requests/requests-oauthlib/blob/master/requests_oauthlib/oauth2_session.py#L196-L211 будет выбор:
A) Включите client_id
только в тело
self._client.prepare_request_body(..)
Б) Включите client_id
и client_secret
в auth
и не включайте их в основной текст (предпочтительное решение RFC)
self._client.prepare_request_body(include_client_id=False, ..)
auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
C) Включите client_id
и client_secret
в тело (альтернативное решение RFC)
self._client.prepare_request_body(client_secret=client_secret, ..)
Сегодня я создам PR для обоих проектов.
У меня в значительной степени есть PR и тесты для OAuthlib. Но у меня есть вопрос ...
Следует ли еще разрешить client_id
в качестве kwarg? Это частично для обратной совместимости, но также и для крайних случаев. Поскольку этот метод был несколько сломан, я думаю, что может быть стоит либо заставить его работать по назначению (например, разрешить ему в prepare_request_body
переопределить self.client_id), либо поднять исключение при неправильном использовании (как при повышении ошибка, если client_id
указан, но не соответствует self.client_id
).
Вы бы не увидели, как вы когда-нибудь захотите переопределить client_id
с другим значением, поэтому проголосовали бы за создание исключения, если они отличаются.
Должны ли мы дополнительно регистрировать DeprecationWarning
если client_id
вообще был предоставлен как kwarg?
PR № 593 отправлен. Он вызывает DeprecationWarning при отправке client_id
и ValueError, если он отличается от self.client_id
. Также есть новый тест, который обеспечивает соответствие трём сценариям, подробно описанным @JonathanHuot .
столкнулся с моей первой проблемой с кандидатом на PR request-oauthlib, когда я писал несколько тестов
Он ВСЕГДА вызывает prepare_request_body
с username=username, password=password
. Это кажется неправильным. Я надеюсь, что кто-то здесь более знаком с RFC и знает ответы на следующие вопросы:
username
+ password
вообще когда-либо быть параметром в request.body?username
+ password
появляется в заголовке HTTP Basic Auth, должны ли они дублироваться в теле запроса?username
+ password
и заголовок HTTPBasicAuth с данными клиента?Спасибо, что столкнулись с этим.
username
и password
всегда должны присутствовать в теле запроса. Они должны использоваться только для предоставления пароля, известного как устаревший. Их нельзя использовать для других грантов (неявных, кодовых, учетных данных клиента).client_secret
). Учетные данные пользователя никогда не должны быть в HTTP Basic Auth.Спасибо. Просто чтобы прояснить две вещи, и, пожалуйста, не стесняйтесь говорить со мной, как будто мне пять. Я хочу убедиться, что правильно понял это и тесты:
Если они явно не указаны, их не должно быть, верно?
Пожалуйста, простите меня за многословность и зацикленность на этих мелких деталях. Я просто хочу убедиться, что у j правильное поведение, и могу написать тесты, которые гарантируют, что мы не получим еще одну регрессию.
@jvanasco , я могу рассказать о RFC OAuth2, однако я не уверен, как сочетаются requests-oauthlib
и flask-oauthlib
.
Верный.
Это я понимаю, однако было бы хорошо сопоставить с реальностью поля; то есть запросы-oauthlib и различные публичные провайдеры. Множественные обсуждения запросов-oauthlib https://github.com/requests/requests-oauthlib/issues/218 , https://github.com/requests/requests-oauthlib/issues/211 , https://github.com/requests / requests-oauthlib / issues / 264 , уже произошло.
Я считаю, что у них была путаница между client password
и client secret
которые на самом деле являются двумя формулировками для одного и того же.
Если мы будем следовать логике https://github.com/requests/requests-oauthlib/pull/206 , содержание PR никогда не должно было быть похоже на добавление HTTPAuth(username, password)
а должно было быть HTTPAuth(client_id, client_secret
(пароль клиента).
Poke @Lukasa , @chaosct , @ibuchanan, которые участвовали в обсуждениях request-oauthlib.
Большой! Спасибо. Я думал, что это то, что происходит, но хотел подтвердить.
Думаю, теперь я знаю, как я хочу структурировать запросы. У меня есть несколько коммитов в основном проекте запросов, поэтому я знаю, что сопровождающие хотят видеть в PR и функциональности.
- Имя пользователя и пароль используются только в определенных типах грантов, для которых они необходимы. Если они используются, они могут присутствовать только в теле запроса.
Да, в отношении первой части: они требуются только для определенного вида гранта. Но во второй части об отправке их в теле запроса в спецификации говорится:
Включение учетных данных клиента в тело запроса
использование двух параметров НЕ РЕКОМЕНДУЕТСЯ
и ДОЛЖЕН быть ограничен клиентами, которые не могут напрямую использовать
схема аутентификации HTTP Basic ...
Но для серверов это гласит:
Сервер авторизации МОЖЕТ поддерживать, в том числе
учетные данные клиента в теле запроса ...
Соответствующий клиент не будет отправлять учетные данные в теле запроса.
Но для некоторых частично реализованных серверов они будут принимать их только в теле запроса.
Если я правильно помню, мой PR решил эту путаницу, добавив заголовок auth,
поэтому клиент отправляет оба.
Я считаю, что @JonathanHuot прав насчет второго пункта.
Привет, @ibuchanan , в цитатах, на которые вы ссылаетесь, используется термин client credentials
. Мы должны быть очень осторожны, чтобы не путать клиента с владельцем ресурса (фактическим пользователем).
Роли четко объяснены здесь rfc6749 # 1.1 .
Этот client credentials
относится к client_id
и client_secret
а владелец ресурса ссылается на username
и password
. Они не взаимозаменяемы.
Итак, чтение RFC с этими ролями означает, что совместимый клиент ДОЛЖЕН отправлять учетные данные клиента ( client_id
, client_secret
) в HTTP Basic и ДОЛЖЕН отправлять учетные данные пользователя ( username
, password
) в теле запроса (никогда не читайте здесь альтернативу); см. rfc6749 # 4.3.2 .
Некоторые серверы отклоняют запрос (400), если client_id находится в запросе
тело. Я думаю, что по умолчанию должно быть то, что рекомендовано спецификацией.
Ok. Я думаю, что текущий PR для oauthlib
удовлетворяет вышеуказанные проблемы - флаг include_client_id
явно разрешает отправку client_id
или нет.
с точки зрения requests_oauthlib
, вот что я думаю:
username
и password
появятся только в теле (не как HTTP Basic). Если такое поведение необходимо для несовместимой серверной интеграции, разработчик может отправить аргумент auth
или headers
в fetch_token()
.
предоставление client_id
в правильном месте немного раздражает, но я думаю, что у меня есть логика и варианты использования. это обязательно потребует некоторого обзора.
У меня вопрос к @JonathanHuot и @ibuchanan :
oauthlib
OAuth2 Client
и requests_oauthlib
OAuth2Session
не сохраняют client_secret
и должны вызывать его повторно. В случае с OAuth1 этого не было, и я думаю, что это была реальная проблема, стоящая за № 370. RFC не упоминал о необходимости этого, и я не смог найти никакой истории.
Для меня имеет смысл расширить клиент, сохранив client_secret
, и начать отказываться от использования OAuth2Session при передаче client_secret
на fetch_token
и request
в пользу использования его сейчас в самом клиенте. (это также устранит некоторые другие несоответствия, указанные в https://github.com/requests/requests-oauthlib/issues/264)
Я немного изменил PR для oauthlib (# 593), чтобы стандартизировать тестовые переменные для имени пользователя / пароля и client_id / client_secret. Я думаю, это должно предотвратить путаницу между ними в будущем.
Предлагаемое изменение для requests-oauthlib: https://github.com/requests/requests-oauthlib/compare/master...jvanasco : fix-oauthlib_client_id
Это немного более радикально, потому что, глядя на код и тесты, кажется, что библиотека просто пыталась заставить работать все виды вещей, которые не должны работать.
Исправление делает несколько вещей:
Проверка на username
и password
происходит только для экземпляров LegacyApplicationClient
- так как это должно быть единственно необходимым (правильно?). Под проверкой есть раздел, который объединяет имя пользователя / пароль в kwargs dict. В настоящее время он не используется, потому что тесты показывают, что другие клиенты могут захотеть передать эту информацию.
Логика обработки заголовков client_id / auth была переписана, чтобы обеспечить правильные типы аутентификации по умолчанию в нужном месте. Если пользователь хочет принудительно ввести учетные данные другим способом, это все еще возможно.
Вопрос: Есть ли ситуация, когда client_secret
не будет передан? Я не могу вспомнить ни одного, но существует множество потоков oAuth.
Итак, в версии requests-oauthlib:
| include_client_id
| auth
| поведение |
| ------------------- | --------------- | -------- |
| Нет (по умолчанию) | Нет (по умолчанию) | создайте объект Auth с помощью client_id
. Не отправляйте client_id в теле. Это поведение по умолчанию, потому что это рекомендуется RFC. |
| Нет (по умолчанию) | настоящее | используйте объект Auth. вызвать prepare_request_body
oauthlib с помощью include_client_id=False
|
| Ложь | настоящее | используйте объект Auth. вызвать prepare_request_body
oauthlib с помощью include_client_id=False
|
| Правда | настоящее | используйте объект Auth. вызвать prepare_request_body
oauthlib с помощью include_client_id=True
|
| Правда | Нет (по умолчанию) | создайте объект Auth с помощью client_id
. вызвать prepare_request_body
oauthlib с помощью include_client_id=True
|
| Ложь | Нет (по умолчанию) | создайте объект Auth с client_id
. вызвать prepare_request_body
oauthlib с помощью include_client_id=False
|
или указано иное:
@jvanasco : очень хорошо выглядит на стороне oauthlib и requests-oauthlib.
По поводу вашего 3. вопроса:
У вас могут быть общедоступные клиенты без client_secret (или пустые, что близко с точки зрения RFC); поэтому API-интерфейс python должен поддерживать «не секрет».
Реальный вариант использования часто используется для собственных приложений, в которых вы предпочитаете использовать код авторизации, но вы не можете гарантировать безопасность, сохраняя секрет в безопасности, поэтому вы либо принимаете client_id
без client_secret
(или пустой client_secret
, идентичный RFC); или у вас есть еще один доступный RFC PKCE (см. https://oauth.net/2/pkce/). Но последнее пока не реализовано на стороне oauthlib
.
Благодарю. Я добавлю несколько тестовых примеров, чтобы убедиться, что мы можем отправить client_id
без client_secret
. Извините за то, что задаю так много вопросов, существует так много возможных правильных реализаций этой спецификации (и даже больше сломанных реализаций, которые должны работать).
@JonathanHuot существующая реализация не поддерживает отправку пустой строки для client_secret
. Он удален в этой логике https://github.com/oauthlib/oauthlib/blob/master/oauthlib/oauth2/rfc6749/parameters.py#L90 -L125 - в частности, строка 122
В поддержку этого можно добавить что-то вроде этого сразу после этой процедуры:
if ('client_secret' in kwargs) and ('client_secret' not in params):
if kwargs['client_secret'] == '':
params.append((unicode_type('client_secret'), kwargs['client_secret']))
Это отправит пустую строку для client_secret
когда секрет является пустой строкой, но не отправит client_secret
если значение равно None
.
Я думаю, что стоит поддержать это, потому что, если RFC поддерживает любой из двух вариантов ... вероятно, будет много неработающих реализаций, которые поддерживают только один вариант.
Эта исходная проблема исправлена в oauthlib. Однако поведение сохраняется до тех пор, пока https://github.com/requests/requests-oauthlib/pull/331 не будет исправлен.
Самый полезный комментарий
Вы бы не увидели, как вы когда-нибудь захотите переопределить
client_id
с другим значением, поэтому проголосовали бы за создание исключения, если они отличаются.Должны ли мы дополнительно регистрировать
DeprecationWarning
еслиclient_id
вообще был предоставлен как kwarg?