web-dev-qa-db-ger.com

Wie kann ich Django OAuth Toolkit mit Python Social Auth verwenden?

Ich baue eine API mit Django Rest Framework auf. Später soll diese API von iOS- und Android-Geräten genutzt werden. Ich möchte meinen Nutzern erlauben, sich bei oauth2-Anbietern wie Facebook und Google anzumelden. In diesem Fall sollten sie überhaupt kein Konto bei meiner Plattform erstellen müssen. Benutzer sollten sich jedoch auch anmelden können, wenn Sie kein Facebook-/Google-Konto haben, für das ich Django-oauth-toolkit verwende. Ich besitze also einen eigenen oauth2-Provider.

Für externe Anbieter verwende ich Python-social-auth, das einwandfrei funktioniert und die Benutzerobjekte automatisch erstellt.

Ich möchte, dass sich die Clients mit Trägertokens authentifizieren. Dies funktioniert gut für Benutzer, die sich bei meinem Provider angemeldet haben (Django-oauth-toolkit stellt Authentifizierungsschemas und Berechtigungsklassen für das Django REST Framework) ..__ bereit.
Python-social-auth implementiert jedoch nur die sitzungsbasierte Authentifizierung. Daher gibt es keine einfache Möglichkeit, authentifizierte API-Anforderungen im Namen von Benutzern durchzuführen, die sich bei einem externen oauth2-Provider registriert haben.

Wenn ich ein access_token verwende, das von Django-oauth-toolkit generiert wurde, funktioniert eine solche Anfrage:

curl -v -H "Authorization: Bearer <token_generated_by_Django-oauth-toolkit>" http://localhost:8000/api/

Folgendes funktioniert jedoch nicht, da es kein entsprechendes Authentifizierungsschema für Django REST und das von python-social-auth bereitgestellte AUTHENTICATION_BACKENDS gibt, das nur für die sitzungsbasierte Authentifizierung funktioniert:

curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/

Die Verwendung der von Django REST zur Verfügung gestellten, durchsuchbaren API nach der Authentifizierung mit Python-social-auth funktioniert einwandfrei. Nur API-Aufrufe ohne Session-Cookie funktionieren nicht.

Ich frage mich, was der beste Ansatz für dieses Problem ist. So wie ich es sehe, habe ich grundsätzlich zwei Möglichkeiten:

A: Wenn sich ein Benutzer bei einem externen oauth2-Provider anmeldet (von Python-social-auth behandelt), müssen Sie sich in den Prozess einwählen, um ein oauth2_provider.models.AccessToken zu erstellen, und weiterhin 'oauth2_provider.ext.rest_framework.OAuth2Authentication' verwenden. Jetzt werden auch Benutzer authentifiziert, die sich bei einem externen Provider registriert haben . Dieser Ansatz wird hier vorgeschlagen: https://groups.google.com/d/msg/Django-rest-framework/ACKx1kY7kZM/YPWFA2DP9LwJ

B: Verwenden Sie Python-social-auth für die API-Anforderungsauthentifizierung. Ich könnte meine eigenen Benutzer in python-social-auth einladen, indem ich ein benutzerdefiniertes Backend schreibe und register_by_access_token verwende. Da API-Aufrufe jedoch keine Django-Sitzungen verwenden können, müsste ich ein Authentifizierungsschema für Django Rest Framework schreiben, das die von python-social-auth gespeicherten Daten verwendet. Einige Hinweise dazu finden Sie hier:
http://psa.matiasaguirre.net/docs/use_cases.html#signup-by-oauth-access-token
http://blog.wizer.fr/2013/11/angularjs-facebook-with-a-Django-rest-api/
http://cbdev.blogspot.it/2014/02/facebook-login-with-angularjs-Django.html
So wie ich es verstehe, überprüft python-social-auth das Token nur beim Anmelden und verlässt sich danach auf die Django-Sitzung. Dies würde bedeuten, dass ich einen Weg finden müsste, um zu verhindern, dass python-social-auth den gesamten oauth2-flow für jede zustandslose API-Anforderung ausführt, und stattdessen mit den in der Datenbank gespeicherten Daten zu suchen, was nicht wirklich für das Abfragen optimiert ist, da dies der Fall ist als JSON gespeichert (ich könnte jedoch UserSocialAuth.objects.get (extra_data__contains =) verwenden) .
Ich müsste auch dafür sorgen, dass der Gültigkeitsbereich eines Zugriffstokens überprüft und Berechtigungen überprüft werden, was bereits von Django-oauth-toolkit ausgeführt wird (TokenHasScope, required_scopes etc).

Im Moment neige ich dazu, Option A zu verwenden, da Django-oauth-toolkit eine gute Integration mit Django Rest Framework bietet und ich alles, was ich brauche, aus der Box bekomme. Der einzige Nachteil ist, dass ich die von python-social-auth abgerufenen access_tokens in das AccessToken-Modell von Django-oauth-toolkit "injizieren" muss, was sich irgendwie falsch anfühlt, aber wahrscheinlich der einfachste Ansatz wäre.

Hat jemand Einwände dagegen oder hat das gleiche Problem vielleicht anders angegangen? Fehlt mir etwas Offensichtliches und macht mein Leben schwieriger als nötig? Wenn jemand Django-Oauth-Toolkit bereits mit Python-Social-Auth und externen Oauth2-Providern integriert hat, wäre ich für einige Hinweise oder Meinungen sehr dankbar.

47
jeverling

Ein Großteil der Schwierigkeiten bei der Implementierung von OAuth besteht darin, zu verstehen, wie der Berechtigungsfluss funktionieren soll. Dies liegt hauptsächlich daran, dass dies der "Ausgangspunkt" für die Anmeldung ist und dass Sie bei der Arbeit mit einem Backend eines Drittanbieters (unter Verwendung von Python Social Auth) tatsächlich dies zweimal tun =: einmal für Ihre API und einmal für die API eines Drittanbieters.

Autorisieren von Anfragen mithilfe Ihrer API und eines Backends eines Drittanbieters

Der Authentifizierungsprozess, den Sie durchlaufen müssen, ist:

Sequence diagram for option A

Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Facebook : User signs in
Facebook -> Django Login : User authorizes your API
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app

Ich verwende hier "Facebook" als Backend eines Drittanbieters, aber der Vorgang ist für jedes Backend der gleiche.

Aus Sicht Ihrer mobilen App leiten Sie nur zu der URL /authorize Um, die vom Django OAuth Toolkit bereitgestellt wird. Von dort aus wartet die mobile App, bis die Rückruf-URL erreicht ist, genau wie im Standardautorisierungsablauf OAuth. Fast alles andere (Django-Login, soziales Login usw.) wird entweder vom Django OAuth Toolkit oder Python Social Auth im Hintergrund ausgeführt.

Dies ist auch mit nahezu allen von Ihnen verwendeten OAuth - Bibliotheken kompatibel, und der Autorisierungsfluss funktioniert unabhängig davon, welches Backend eines Drittanbieters verwendet wird. Es wird sogar den (üblichen) Fall behandeln, dass Sie in der Lage sein müssen, das Authentifizierungs-Backend von Django (E-Mail/Benutzername und Passwort) sowie ein Login von Drittanbietern zu unterstützen.

Option A without a third-party backend

Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app

Zu beachten ist auch, dass die mobile App (bei der es sich um einen beliebigen OAuth - Client handeln kann) niemals die OAuth - Tokens von Facebook/Drittanbietern erhält. Dies ist unglaublich wichtig, da sichergestellt wird, dass Ihre API als Vermittler zwischen dem OAuth - Client und den sozialen Konten Ihres Benutzers fungiert.

Sequence diagram with your API as the gatekeeper

Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives OAuth token
Mobile App -> Your API : Requests the display name
Your API -> Facebook : Requests the full name
Facebook -> Your API : Sends back the full name
Your API -> Mobile App : Send back a display name

Andernfalls könnte der OAuth - Client Ihre API umgehen und Anforderungen in Ihrem Namen an die APIs von Drittanbietern senden.

Sequence diagram for bypassing your API

Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives Facebook token
Mobile App -> Facebook : Requests all of the followers
Facebook -> Mobile App : Sends any requested data

Sie werden feststellen, dass Sie zu diesem Zeitpunkt die Kontrolle über die Token von Drittanbietern verloren hätten . Dies ist besonders gefährlich, da die meisten Token auf eine Vielzahl von Daten zugreifen können, was die Tür für Missbrauch öffnet und schließlich untergeht unter Ihrem Namen. Höchstwahrscheinlich hatten diejenigen, die sich bei Ihrer API/Website anmelden, nicht vor, ihre sozialen Informationen an den OAuth - Client weiterzugeben, sondern erwarteten, dass Sie diese Informationen (so weit wie möglich) geheim halten, aber stattdessen machen Sie diese Informationen für alle zugänglich .

Authentifizieren von Anforderungen an Ihre API

Wenn die mobile Anwendung dann Ihr OAuth Token verwendet, um Anforderungen an Ihre API zu stellen, erfolgt die gesamte Authentifizierung über Django OAuth Toolkit (oder Ihr OAuth Anbieter) im Hintergrund. Sie sehen nur, dass Ihrer Anfrage ein User zugeordnet ist.

How OAuth tokens are validated

Mobile App -> Your API : Sends request with OAuth token
Your API -> Django OAuth Toolkit : Verifies the token
Django OAuth Toolkit -> Your API : Returns the user who is authenticated
Your API -> Mobile App : Sends requested data back

Dies ist wichtig, da nach der Autorisierungsphase keinen Unterschied machen sollte, ob der Benutzer von Facebook oder dem Authentifizierungssystem von Django stammt . Ihre API benötigt nur ein User, um damit arbeiten zu können, und Ihr OAuth Anbieter sollte in der Lage sein, die Authentifizierung und Überprüfung des Tokens durchzuführen.

Dies unterscheidet sich nicht wesentlich davon, wie das Django REST - Framework den Benutzer authentifiziert, wenn die sitzungsgestützte Authentifizierung verwendet wird.

Sequence diagram for authenticating using sessions

Web Browser -> Your API : Sends session cookie
Your API -> Django : Verifies session token
Django -> Your API : Returns session data
Your API -> Django : Verifies the user session
Django -> Your API : Returns the logged in user
Your API -> Web Browser : Returns the requested data

Auch hier wird alles von Django OAuth Toolkit verwaltet und erfordert keine zusätzliche Arbeit für die Implementierung.

Arbeiten mit einem nativen SDK

In den meisten Fällen authentifizieren Sie den Benutzer über Ihre eigene Website und verwenden Python Social Auth, um alles zu erledigen. Eine bemerkenswerte Ausnahme ist jedoch die Verwendung eines nativen SDK, da die Authentifizierung und Autorisierung über das native System erfolgt. Dies bedeutet, dass Sie Ihre API vollständig umgehen . Dies ist ideal für Anwendungen, die sich bei einem Drittanbieter anmelden müssen, oder Anwendungen, die Ihre API überhaupt nicht verwenden, aber es ist ein Albtraum, wenn beide zusammenkommen .

Dies liegt daran, dass Ihr Server die Anmeldung nicht validieren kann und gezwungen ist anzunehmen, dass die Anmeldung gültig und echt ist , was bedeutet Es umgeht jegliche Sicherheit, die Python Social Auth Ihnen bietet.

Using a native SDK can cause issues

Mobile App -> Facebook SDK : Opens the authorization Prompt
Facebook SDK -> Mobile App : Gets the Facebook token
Mobile App -> Your API : Sends the Facebook token for authorization
Your API -> Django Login : Tries to validate the token
Django Login -> Your API : Returns a matching user
Your API -> Mobile App : Sends back an OAuth token for the user

Sie werden bemerken, dass dies während der Authentifizierungsphase Ihre API überspringt und Ihre API dann zwingt, Annahmen über das übergebene Token zu treffen. Es gibt jedoch definitiv Fälle, in denen dieses Risiko wert sein kann , also solltest du das auswerten, bevor du es wegwirfst. Es ist ein Kompromiss zwischen schnellen und systemeigenen Anmeldungen für Ihre Benutzer und , die möglicherweise mit schlechten oder böswilligen Token umgehen.

93
Kevin Brown

Ich habe es mit Ihrer A-Option gelöst.

Was ich mache, ist die Registrierung von Benutzern, die einen Drittanbieter verwenden, um sich mit ihrem Drittanbieter-Zugriffstoken anzumelden.

url(r'^register-by-token/(?P<backend>[^/]+)/$',
    views.register_by_access_token),

Auf diese Weise kann ich eine GET-Anfrage wie diese absetzen:

GET http://localhost:8000/register-by-token/facebook/?access_token=123456

Und register_by_access_token wird angerufen. request.backend.do_auth fragt den Anbieter nach den Benutzerinformationen vom Token ab und registriert ein Benutzerkonto auf magische Weise mit den Informationen oder meldet den Benutzer an, wenn er bereits registriert ist. 

Dann erstelle ich manuell ein Token und gebe es als JSON zurück, damit der Client meine API abfragen kann.

from oauthlib.common import generate_token
...
@psa('social:complete')
def register_by_access_token(request, backend):
    # This view expects an access_token GET parameter, if it's needed,
    # request.backend and request.strategy will be loaded with the current
    # backend and strategy.
    third_party_token = request.GET.get('access_token')
    user = request.backend.do_auth(third_party_token)

    if user:
        login(request, user)

        # We get our app!   
        app = Application.objects.get(name="myapp")

        # We delete the old token
        try:
            old = AccessToken.objects.get(user=user, application=app)
        except:
            pass
        else:
            old.delete()

        # We create a new one
        my_token = generate_token()

        # We create the access token 
        # (we could create a refresh token too the same way) 
        AccessToken.objects.create(user=user,
                                   application=app,
                                   expires=now() + timedelta(days=365),
                                   token=my_token)

        return "OK" # you can return your token as JSON here

    else:
        return "ERROR"

Ich bin nur nicht sicher, wie ich das Token generiere. Ist dies eine gute Vorgehensweise? Nun, in der Zwischenzeit funktioniert es !!

9
Felix D.

Vielleicht ist Django-rest-framework-social-oauth2 genau das, was Sie suchen. Dieses Paket hängt von python-social-auth und Django-oauth-toolkit ab, die Sie bereits verwenden. Ich habe die Dokumentation schnell gescannt und sie scheint genau das zu implementieren, was Sie versuchen.

5
Serrano

Ich habe React Native mit Expo und Django mit Django REST gemacht. Dieser Blogpost endete damit, wie ich die Registrierung mit Facebook https://medium.com/@gabriel_gamil/react-native-expo-Django-facebook-authentication-sign-in-83625c49da7

tldr; benutze Django-rest-auth https://Django-rest-auth.readthedocs.io/de/latest/index.html

benutze Django-allauth https://Django-allauth.readthedocs.io/de/latest/

0
Harry Moreno