Ich habe eine Regression in master gefunden, wenn es mit Requests/requests-oauthlib verwendet wurde, da https://github.com/oauthlib/oauthlib/issues/495 zusammengeführt wurde. Es bezieht sich nur auf die Berechtigungserteilung/Webanwendung.
Die grundlegende Verwendung von request-oauthlib ist:
sess = OAuth2Session(client_id)
token = sess.fetch_token(token_url, client_secret=client_secret, authorization_response=request.url)
Seit den Änderungen wird jedoch client_id
der Sitzung ignoriert. Ich denke, https://github.com/oauthlib/oauthlib/pull/505 hat einen Anwendungsfall behoben, aber einen anderen kaputt gemacht. Wir sollten eine Win-Win-Lösung finden.
request-oauthlib-Codeaufruf unter https://github.com/requests/requests-oauthlib/blob/master/requests_oauthlib/oauth2_session.py#L196 -L198 und oauthlib-Problem hier
https://github.com/oauthlib/oauthlib/blame/master/oauthlib/oauth2/rfc6749/clients/web_application.py#L128.
Mein erster Gedanke ist, die Änderungen in prepare_request_body
zu machen, um standardmäßig den self.client_id
Satz im WebApplicationClient
Konstruktor zu verwenden.
Dann sollten Inline-Dokumente entsprechend geändert werden, um &client_id=xx
zur Ausgabe von prepare_request_body
hinzuzufügen.
Um den ursprünglichen Fix zu ersetzen, schlage ich schließlich vor, client_id
aus den Argumenten zu entfernen und ein neues Argument zu prepare_request_body
wie include_client=True/False
hinzuzufügen, um beide client_id
hinzuzufügen client_secret
im Textkörper enthalten oder nicht beide enthalten.
Die Gedanken?
stocher @Diaoul @skion @thedrow
Wie wäre es mit:
Ich schlage vor, client_id aus den Argumenten zu entfernen und ein neues Argument zu Prepare_request_body hinzuzufügen, wie include_client=True/False, um sowohl client_id als auch client_secret in den Body hinzuzufügen oder nicht beide einzuschließen.
Vielen Dank
Ich habe das gleiche Problem tatsächlich in einem meiner Tests ausgelöst, es aber hier gegen Requests/oauthlib eingereicht: https://github.com/requests/requests-oauthlib/issues/330
Ich glaube, das Problem liegt tatsächlich an Requests_oauthlib. Ihre Dokumente – eigentlich das erste Beispiel in ihren gesamten Dokumenten, wenn Sie die Seite laden – unterstützen die Angabe von client_id
im OAuth2Session
Konstruktor. Die Logik in Zeile 200 zieht client_id
von den Kwargs, hat aber keinen Fallback, um es aus der bereits konstruierten WebApplicationClient
Instanz zu ziehen.
@jvanasco , das aktuelle Problem #585 kann allein in Requests-oauthlib oder durch Zurücksetzen von oauthlibs PR #505 behoben werden. Keine der Lösungen wird jedoch das von @skion in seinem Kommentar unter https://github.com/oauthlib/oauthlib/pull/505#issuecomment -351221107 erwähnte Verhalten beheben
Gemäß der Spezifikation muss der Parameter client_id für nicht authentifizierte Clients gesendet werden, wird jedoch für vertrauliche Clients vorzugsweise NICHT im Token-Anforderungstext gesendet, da in diesem Fall der bevorzugte Mechanismus zur Authentifizierung des Clients über die HTTP Basic-Authentifizierung erfolgt. Die WebApplication-Klasse enthält es jedoch immer (was einige Server zerstört) und bietet keinen Mechanismus zum Entfernen.
Oauthlib muss eine elegante und einfache Möglichkeit für Requests-oauthlib bieten, das Problem zu lösen. Wenn wir in dieser Diskussion eine Lösung finden, ist das großartig; weil das ein riesiger Blocker ist.
Würde es helfen, client_id=False
in prepare_request_body()
zuzulassen, um anzuzeigen, dass keine client_id gesendet werden soll? Obwohl dies in der Nähe von Zeile 126 zu dieser Hässlichkeit führen würde:
client_id = None if client_id=False else self.client_id
Ah ich sehe.
Gibt es einen vorhandenen Unit-Test, wann client_id gesendet werden soll oder nicht? Wenn nicht, hat jemand eine Auflistung, die verwendet werden kann, um eine zu erstellen? Ich würde gerne versuchen, dies und die Requests-oauthlib zu reparieren, da es gerade einen Teil meiner Arbeit blockiert.
@JonathanHuot
Ich schlage vor, client_id aus den Argumenten zu entfernen und ein neues Argument zu Prepare_request_body hinzuzufügen, wie include_client=True/False, um sowohl client_id als auch client_secret in den Body hinzuzufügen oder nicht beide einzuschließen.
Wenn ich das nochmal lese, finde ich deinen Vorschlag eigentlich ganz gut. Wir können es wahrscheinlich auf nicht brechende Weise tun, da die Funktion bereits **kwargs
.
Eine Note:
um sowohl client_id als auch client_secret im Körper hinzuzufügen
Da es sich hier um öffentliche Auftraggeber IIUC handelt, würde ich nicht glauben, dass client_secret
beteiligt ist; es sind nur die client_id
, die dem Körper hinzugefügt werden? In diesem Fall würde ich auch in Erwägung ziehen, den neuen Parameter in include_client_id=True/False
.
In diesem Fall würde ich auch in Erwägung ziehen, den neuen Parameter in include_client_id=True/False umzubenennen.
In der Tat ! client_secret
ist nicht beteiligt, da es in WebApplicationClient
nicht vorhanden ist.
@jvanasco , wenn Sie eine PR machen möchten, sollten die Änderungen meiner Meinung nach sein:
1) Zurücksetzen https://github.com/oauthlib/oauthlib/pull/505
2) Ändern Sie die Signatur von prepare_request_body()
, um client_id
prepare_request_body()
zu entfernen und include_client_id=True/False
hinzuzufügen ( True
(Standard): es fügt self.client_id
)
Requests-oauthlib hat dann in https://github.com/requests/requests-oauthlib/blob/master/requests_oauthlib/oauth2_session.py#L196-L211 die Wahl zwischen:
A) Fügen Sie client_id
allein in den Körper ein
self._client.prepare_request_body(..)
B) Fügen Sie client_id
und client_secret
in auth
und fügen Sie sie nicht in den Textkörper ein (RFC-bevorzugte Lösung)
self._client.prepare_request_body(include_client_id=False, ..)
auth = requests.auth.HTTPBasicAuth(client_id, client_secret)
C) Fügen Sie client_id
und client_secret
in den Körper ein (RFC-Alternativlösung)
self._client.prepare_request_body(client_secret=client_secret, ..)
Ich werde heute PR's für beide Projekte generieren.
Ich habe so ziemlich eine PR und Tests für OAuthlib durchgeführt. Ich habe aber eine Frage...
Sollte client_id
noch als Kwarg erlaubt sein? Dies dient zum Teil der Abwärtskompatibilität, aber auch für Randfälle. Da diese Methode etwas kaputt war, denke ich, dass es sich lohnen kann, sie entweder wie beabsichtigt zu arbeiten (wie in prepare_request_body
erlaubt, die self.client_id zu überschreiben) oder eine Ausnahme bei falscher Verwendung auszulösen (wie in ein Fehler, wenn client_id
wird, aber nicht mit self.client_id
übereinstimmt).
Ich würde nicht sehen, wie Sie client_id
jemals mit einem anderen Wert überschreiben möchten, und würden daher für das Auslösen einer Ausnahme stimmen, wenn sie sich unterscheiden.
Sollen wir zusätzlich ein DeprecationWarning
protokollieren, wenn client_id
überhaupt als Kwarg bereitgestellt wurde?
PR #593 eingereicht. Es löst eine DeprecationWarning aus, wenn client_id
gesendet wird, und einen ValueError, wenn es sich von self.client_id
. Außerdem gibt es einen neuen Test, der die Einhaltung der drei Szenarien sicherstellt, die
stieß auf mein erstes Problem mit Anfragen – oauthlib PR-Kandidat, als ich einige Tests schreibe
Es ruft prepare_request_body
IMMER mit username=username, password=password
. Dies scheint falsch zu sein. Ich hoffe, jemand hier ist mit dem RFC besser vertraut und kennt die Antworten auf die folgenden:
username
+ password
überhaupt ein Parameter im request.body sein?username
+ password
im HTTP Basic Auth-Header erscheinen, sollten sie dann im Anforderungstext dupliziert werden?username
+ password
Body-Parameter als auch einen HTTPBasicAuth-Header mit den Client-Details zu haben?Danke, dass du darauf gestoßen bist.
username
und password
müssen immer im Hauptteil der Anfrage vorhanden sein. Sie dürfen nur für die Passworterteilung, auch bekannt als Legacy, verwendet werden. Diese dürfen nicht für andere Grants (implizit, Code, Client-Anmeldeinformationen) verwendet werden.client_secret
). Benutzeranmeldeinformationen dürfen niemals in HTTP Basic Auth enthalten sein.Vielen Dank. Nur um zwei Dinge klarzustellen, und zögern Sie nicht, mit mir zu sprechen, als wäre ich fünf. Ich möchte sicherstellen, dass ich dies und die Tests richtig mache:
Sofern sie nicht ausdrücklich angegeben sind, sollten sie nicht vorhanden sein, richtig?
Bitte verzeihen Sie mir, dass ich so ausführlich und besessen von diesen kleinen Details bin. Ich möchte nur sicherstellen, dass ich das richtige Verhalten bekomme und Tests schreiben kann, die sicherstellen, dass wir keine weitere Regression bekommen.
@jvanasco , ich kann über den OAuth2-RFC berichten, bin mir jedoch nicht sicher, wie requests-oauthlib
und flask-oauthlib
zusammenpassen.
Richtig.
Es ist mein Verständnis, aber es wird gut sein, sich mit der Realität des Feldes zu vergleichen; dh Anfragen-oauthlib und verschiedene öffentliche Anbieter Erfahrungen. Mehrere Anfragen-oauthlib-Diskussionen https://github.com/requests/requests-oauthlib/issues/218 , https://github.com/requests/requests-oauthlib/issues/211 , https://github.com/requests /requests-oauthlib/issues/264 , bereits passiert.
Ich glaube, sie hatten eine Verwechslung zwischen client password
und client secret
was eigentlich zwei Formulierungen für genau dasselbe sind.
Wenn wir der Begründung hinter https://github.com/requests/requests-oauthlib/pull/206 folgen, hätte der Inhalt der PR nie so sein sollen, als würde man HTTPAuth(username, password)
hinzufügen, sondern HTTPAuth(client_id, client_secret
(das Passwort des Kunden).
Poke @Lukasa , @chaosct , @ibuchanan, die an den Diskussionen von Requests-oauthlib teilgenommen haben.
Groß! Vielen Dank. Ich dachte, das ist, was los ist, wollte es aber bestätigen.
Ich glaube, ich weiß jetzt, wie ich die Anfragen strukturieren möchte. Ich habe eine Handvoll Commits für das Hauptanforderungsprojekt, daher weiß ich, was die Betreuer an PRs und Funktionalität sehen möchten.
- Benutzername und Passwort werden nur in einer bestimmten Art von Erteilung verwendet, die sie erfordert. Wenn sie verwendet werden, dürfen sie nur im Anfragetext vorhanden sein.
Ja, zum ersten Teil: Nur eine bestimmte Art von Stipendium erfordert sie. Aber im 2. Teil über das Senden im Anfragetext sagt die Spezifikation:
Einschließen der Client-Anmeldeinformationen in den Request-Body
Die Verwendung der beiden Parameter wird NICHT EMPFOHLEN
und SOLL auf Kunden beschränkt sein, die nicht direkt nutzen können
das HTTP Basic-Authentifizierungsschema...
Aber für Server lautet es:
Der Autorisierungsserver KANN einschließlich unterstützen
die Client-Anmeldeinformationen im Request-Body...
Ein konformer Client würde die Anmeldeinformationen im Anforderungstext nicht senden.
Bei einigen teilweise implementierten Servern werden sie jedoch nur im Anforderungstext akzeptiert.
Wenn ich mich richtig erinnere, hat mein PR diese Verwirrung gelöst, indem ich den Auth-Header hinzugefügt habe,
so sendet der Client beide.
Ich glaube, @JonathanHuot hat mit dem zweiten Punkt
Hallo @ibuchanan , die Anführungszeichen, auf die Sie sich beziehen, verwenden den Begriff client credentials
. Wir müssen sehr vorsichtig sein, um den Client nicht mit dem Ressourcenbesitzer (dem tatsächlichen Benutzer) zu vermischen.
Die Rollen werden hier rfc6749#1.1 anschaulich erklärt.
Dieses client credentials
bezieht sich auf client_id
und client_secret
und der Ressourcenbesitzer bezieht sich auf username
und password
. Diese sind nicht austauschbar.
Das Lesen des RFC mit diesen Rollen bedeutet also, dass ein konformer Client Client-Anmeldeinformationen ( client_id
, client_secret
) in HTTP Basic senden SOLLTE und Benutzeranmeldeinformationen senden MUSS ( username
, password
) im Anfragetext (hier niemals eine Alternative lesen); siehe rfc6749#4.3.2 .
Einige Server lehnen die Anfrage ab (400), wenn die client_id in der Anfrage enthalten ist
Karosserie. Ich denke, die Standardeinstellung sollte die von der Spezifikation empfohlene sein.
Okay. Ich denke, die aktuelle PR für oauthlib
erfüllt die oben genannten Bedenken - das include_client_id
Flag erlaubt explizit, dass die client_id
gesendet werden oder nicht.
In Bezug auf requests_oauthlib
denke ich Folgendes:
username
und password
erscheinen nur im Body (nicht als HTTP Basic). Wenn dieses Verhalten für eine nicht konforme Serverintegration erforderlich ist, kann der Implementierer ein auth
oder headers
Argument an fetch_token()
senden.
Die Angabe von client_id
an der richtigen Stelle ist etwas nervig, aber ich denke, ich habe die Logik und die Anwendungsfälle im Griff. Dies wird definitiv eine Überprüfung benötigen.
Eine Frage, die ich an @JonathanHuot und @ibuchanan habe :
oauthlib
's OAuth2 Client
und requests_oauthlib
's OAuth2Session
behalten das client_secret
und müssen es wiederholt aufrufen. Dies war bei OAuth1 nicht der Fall und ich denke, dies war das eigentliche Problem hinter #370. Der RFC erwähnte keine Notwendigkeit dafür, und ich konnte keine Historie finden.
Für mich ist es sinnvoll, den Client um das Speichern von client_secret
und die Abhängigkeit von OAuth2Session von der Übergabe von client_secret
an fetch_token
und request
abzulehnen.
Ich habe den PR für oauthlib (#593) leicht geändert, um die Testvariablen für Benutzername/Passwort und client_id/client_secret zu standardisieren. Ich denke, das sollte in Zukunft Verwechslungen zwischen den beiden vermeiden.
Die vorgeschlagene Änderung für request-oauthlib : https://github.com/requests/requests-oauthlib/compare/master...jvanasco :fix-oauthlib_client_id
Dieser ist etwas drastischer, denn wenn man sich den Code und die Tests ansieht, scheint es, dass die Bibliothek nur versucht hat, alle möglichen Dinge zum Laufen zu bringen, die nicht funktionieren sollten.
Der Fix bewirkt ein paar Dinge:
Die Überprüfung auf username
und password
geschieht nur für LegacyApplicationClient
Instanzen -- da dies die einzige Art sein sollte, die benötigt wird (richtig?). Es gibt einen Abschnitt unter der Überprüfung, der den Benutzernamen/das Passwort in das kwargs-Dikt einfügt. Es ist derzeit ausgerückt, da die Tests darauf hindeuten, dass andere Clients diese Informationen möglicherweise weitergeben möchten.
Die Logik für die Behandlung der client_id/auth-Header wurde neu geschrieben, um sicherzustellen, dass die richtigen Authentifizierungstypen standardmäßig an der richtigen Stelle ausgeführt werden. Wenn ein Benutzer Anmeldeinformationen auf andere Weise erzwingen möchte, ist dies immer noch möglich.
Frage: Gibt es eine Situation, in der client_secret
nicht bestanden wird? Ich kann mir keine vorstellen, aber es gibt viele oAuth-Flows.
Also in der Requests-oAuthlib-Version:
| include_client_id
| auth
| Verhalten |
| ------------------- | -------------- | -------- |
| Keine (Standard) | Keine (Standard) | Erstellen Sie ein Auth-Objekt mit dem client_id
. Senden Sie die client_id nicht im Body. Dies ist das Standardverhalten, da es vom RFC empfohlen wird. |
| Keine (Standard) | präsentieren | Verwenden Sie das Auth-Objekt. Rufen Sie prepare_request_body
oauthlib mit include_client_id=False
|
| Falsch | präsentieren | Verwenden Sie das Auth-Objekt. Rufen Sie prepare_request_body
oauthlib mit include_client_id=False
|
| Wahr | präsentieren | Verwenden Sie das Auth-Objekt. Rufen Sie prepare_request_body
oauthlib mit include_client_id=True
|
| Wahr | Keine (Standard) | Erstellen Sie ein Auth-Objekt mit dem client_id
. Rufen Sie prepare_request_body
oauthlib mit include_client_id=True
|
| Falsch | Keine (Standard) | Erstellen Sie ein Auth-Objekt mit dem client_id
. Rufen Sie prepare_request_body
oauthlib mit include_client_id=False
|
oder anders angegeben:
@jvanasco : sieht auf der oauthlib- und der Requests-oauthlib-Seite sehr gut aus.
Zu deiner 3. Frage:
Sie können öffentliche Clients ohne client_secret (oder leer, das ist aus Sicht des RFC) haben; Daher muss die Python-API "kein Geheimnis" unterstützen.
Der reale Anwendungsfall ist oft für native Anwendungen, bei denen Sie es vorziehen, Autorisierungscode zu verwenden, aber Sie können die Sicherheit nicht garantieren, um das Geheimnis sicher zu halten, also akzeptieren Sie entweder ein client_id
ohne client_secret
(oder leere client_secret
die für den RFC identisch sind); oder Sie haben auch einen anderen PKCE RFC zur Verfügung (siehe https://oauth.net/2/pkce/ ). Letzteres ist jedoch noch nicht auf oauthlib
Seite implementiert.
Danke. Ich werde einige Testfälle hinzufügen, um sicherzustellen, dass wir client_id
ohne client_secret
einreichen können. Es tut mir leid, dass ich so viele Fragen stelle, es sind einfach so viele korrekte Implementierungen dieser Spezifikation möglich (und noch mehr fehlerhafte Implementierungen, die funktionieren müssen).
@JonathanHuot die vorhandene Implementierung unterstützt nicht das Senden einer leeren Zeichenfolge für client_secret
. Es wird in dieser Logik entfernt https://github.com/oauthlib/oauthlib/blob/master/oauthlib/oauth2/rfc6749/parameters.py#L90 -L125 -- insbesondere Zeile 122
Um es zu unterstützen, kann man direkt nach dieser Routine so etwas hinzufügen:
if ('client_secret' in kwargs) and ('client_secret' not in params):
if kwargs['client_secret'] == '':
params.append((unicode_type('client_secret'), kwargs['client_secret']))
Das würde einen leeren String für client_secret
senden, wenn das Geheimnis ein leerer String ist, aber nicht client_secret
wenn der Wert None
.
Ich denke, es lohnt sich, dies zu unterstützen, denn wenn der RFC eine der beiden Varianten unterstützt ... wird es wahrscheinlich viele defekte Implementierungen geben, die nur eine Variante unterstützen.
Dieses ursprüngliche Problem wurde in oauthlib behoben. Das Verhalten ist jedoch immer noch da, bis https://github.com/requests/requests-oauthlib/pull/331 behoben ist.
Hilfreichster Kommentar
Ich würde nicht sehen, wie Sie
client_id
jemals mit einem anderen Wert überschreiben möchten, und würden daher für das Auslösen einer Ausnahme stimmen, wenn sie sich unterscheiden.Sollen wir zusätzlich ein
DeprecationWarning
protokollieren, wennclient_id
überhaupt als Kwarg bereitgestellt wurde?