web-dev-qa-db-ja.com

Django OAuth Toolkit with Python Social Auth?

Django Rest Frameworkを使用してAPIを構築しています。後でこのAPIはiOSおよびAndroidデバイスで使用されることになっています。 FacebookやGoogleなどのoauth2-providersでサインアップします。この場合、プラットフォームでアカウントを作成する必要はまったくありませんが、ユーザーはFacebook/Googleアカウントを持っていないときにサインアップできる必要があります。 Django-oauth-toolkitを使用しているので、独自のoauth2-providerがあります。

外部プロバイダーでは、python-social-authを使用しています。これは正常に機能し、ユーザーオブジェクトを自動的に作成します。

ベアラートークンを使用してクライアントを認証します。これは、プロバイダーにサインアップしたユーザーに対して正常に機能します(Django-oauth-toolkitは、Django REST Framework)。
ただし、python-social-authはセッションベースの認証のみを実装しているため、外部のoauth2プロバイダーによって登録されたユーザーに代わって認証済みのAPIリクエストを行う簡単な方法はありません。

Django-oauth-toolkitによって生成されたaccess_tokenを使用する場合、次のようなリクエストを行うと機能します。

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

ただし、Django REST Frameworkおよびpython-social-authが提供するAUTHENTICATION_BACKENDSは、セッションベース認証:

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

Django REST python-social-authで認証した後のフレームワークは問題なく動作し、セッションCookieのないAPI呼び出しのみが機能しません。 。

この問題に対する最善のアプローチは何だろうと思っています。私の見方では、基本的に2つのオプションがあります。

A:ユーザーが(python-social-authによって処理される)外部oauth2プロバイダーにサインアップすると、プロセスにフックしてoauth2_provider.models.AccessTokenを作成し、'oauth2_provider.ext.rest_framework.OAuth2Authentication'、外部プロバイダーに登録したユーザーも認証するようになりました。このアプローチはここで提案されています: https://groups.google.com/d/msg/Django-rest-framework/ACKx1kY7kZM/YPWFA2DP9LwJ

B:APIリクエスト認証にpython-social-authを使用します。カスタムバックエンドを作成し、register_by_access_tokenを使用して、自分のユーザーをpython-social-authに取り込むことができました。ただし、API呼び出しはDjango=セッションを利用できないため、これはDjango python- social-auth:これを行う方法に関するいくつかのポインターはここにあります:
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
しかし、python-social-authはログイン時にトークンを検証するだけで、その後Djangoセッションに依存します。これは私が見つけなければならないことを意味します。 python-social-authが各ステートレスAPIリクエストに対してoauth2-flow全体を実行するのを防ぎ、DBに保存されているデータをチェックする方法。これは、JSONとして保存されているためクエリに対して最適化されていません(UserSocialAuth .objects.get(extra_data__contains =)ただし)。
アクセストークンのスコープを確認し、Django-oauth-toolkitがすでに行っているアクセス許可のチェックに使用する必要があります(TokenHasScoperequired_scopesなど)。

現時点では、Django-oauth-toolkitはDjango Rest Frameworkとの良好な統合を提供し、必要なものはすべて箱から取り出します。唯一の欠点は私はpython-social-authによって取得されたaccess_tokensをDjango-oauth-toolkitのAccessTokenモデルに「挿入」する必要があることを知っています。

誰かがそれを行うことに異議を唱えたり、同じ問題に別の方法で取り組んだりしているのでしょうか?明らかな何かを失い、自分の人生を必要以上に難しくしているのでしょうか?誰かがすでにDjango-oauth-toolkitをpython-social-authおよび外部のoauth2プロバイダーと統合している場合、私はいくつかの指針や意見に非常に感謝します。

51
jeverling

OAuthの実装における多くの困難は、承認フローがどのように機能するかを理解することに帰着します。これは主に、これがログインの「開始点」であり、サードパーティのバックエンドで作業するとき(PythonSocial Authのようなものを使用)、実際にはこれを2回行う :APIに1回、サードパーティAPIに1回。

APIとサードパーティのバックエンドを使用してリクエストを承認する

必要な認証プロセスは次のとおりです。

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

ここではサードパーティのバックエンドとして「Facebook」を使用していますが、プロセスはどのバックエンドでも同じです。

モバイルアプリの観点から、リダイレクト先はDjangoOAuthToolkitによって提供される/authorize URLのみです。そこから、モバイルアプリは標準のOAuth認証フローと同様に、コールバックURLに到達します。他のほとんどすべて(Djangoログイン、ソーシャルログインなど)は、DjangoOAuthツールキットまたはPythonバックグラウンドでのソーシャル認証。

これは、使用するほとんどすべてのOAuthライブラリとも互換性があり、サードパーティのバックエンドが使用されている場合でも認証フローは同じように機能します。 Djangoの認証バックエンド(メール/ユーザー名とパスワード)およびサードパーティのログインをサポートする必要がある(一般的な)ケースも処理します。

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

ここで注意すべき重要なことは、モバイルアプリ(任意のOAuthクライアント)Facebook /サードパーティOAuthトークンを受信しないことです。これは非常に重要ですAPIがOAuthクライアントとユーザーのソーシャルアカウントとの間の仲介役として機能することを確認します。

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

それ以外の場合、OAuthクライアントはAPIをバイパスし、サードパーティAPIにユーザーに代わってリクエストを行うことができます。

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

この時点でサードパーティトークンに対するすべての制御を失うことになります。これは、ほとんどのトークンが広範囲のデータにアクセスできるため、特に危険です。 あなたの名前の下。ほとんどの場合、あなたのAPI /ウェブサイトにログインする人は、ソーシャル情報をOAuthクライアントと共有するつもりはなく、その情報をプライベートに保つことを期待していました(ただし、代わりに、その情報を全員に公開しています

APIへのリクエストの認証

モバイルアプリケーションがyourOAuthtokenを使用してyour APIにリクエストを送信すると、すべての認証はDjangoOAuthToolkit(またはOAuthプロバイダー)。 表示されるのは、リクエストにUserが関連付けられていることだけです。

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

これは重要です。承認段階の後、ユーザーがFacebookまたはDjangoの認証システムから来ている場合、違いはないはずです。APIで必要なのはUserだけで、OAuthプロバイダーは、トークンの認証と検証を処理できる必要があります。

これは、セッションバックアップ認証を使用する場合、DjangoRESTフレームワークがユーザーを認証する方法と大差ありません。

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

繰り返しますが、このすべてはDjangoOAuthToolkitによって処理され、実装するのに余分な作業は必要ありません。

ネイティブSDKでの作業

ほとんどの場合、独自のWebサイトでユーザーを認証し、PythonSocial Authを使用してすべてを処理します。ただし、1つの注目すべき例外は、ネイティブSDKを使用する場合です。認証および承認はネイティブシステムを介して処理されるため、APIを完全にバイパスしています。これはサインインが必要なアプリケーションに最適です。サードパーティ、またはAPIをまったく使用しないアプリケーションを使用しますが、両方が一緒になると悪夢です

これは、サーバーがログインを検証できないで、強制的にログインが有効で本物であると仮定しますです。つまり、PythonSocial Authあなたにあげる。

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

これにより、認証フェーズ中にAPIがスキップされ、APIに渡されたトークンについての仮定が強制されることに気付くでしょう。しかし、このリスクは価値があるかもしれませんそれは捨てる前にそれを評価する必要があります。それはユーザーのクイックログインとネイティブログイン潜在的に悪いまたは悪意のあるトークンを処理する

93
Kevin Brown

A.オプションを使用して解決しました。

私がしていることは、サードパーティを使用して、サードパーティのアクセストークンでサインアップするユーザーを登録することです。

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

このようにして、次のようなGETリクエストを発行できます。

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

そしてregister_by_access_tokenが呼び出されます。 request.backend.do_authは、トークンからのユーザー情報をプロバイダーに照会し、ユーザーアカウントを情報で魔法のように登録するか、ユーザーが既に登録されている場合はサインインします。

次に、トークンを手動で作成し、クライアントにAPIを照会させるためにJSONとして返します。

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"

トークンを生成する方法がわかりませんが、これは良い習慣ですか?まあ、その間に、それは動作します!!

9
Felix D.

多分 Django-rest-framework-social-oauth2 はあなたが探しているものです。このパッケージはpython-social-authおよびDjango-oauth-toolkit、既に使用しています。ドキュメントをすばやくスキャンしましたが、あなたがやろうとしていることを実装しているようです。

5
Serrano

私はReact expoでネイティブ、およびDjango with Django RESTフレームワーク。このブログ投稿は、facebookで登録(サインアップ)を解決する方法でした https://medium.com/@gabriel_gamil/react-native-expo-Django-facebook-authentication-sign-in-83625c49da7

tldr; Django-rest-authを使用 https://Django-rest-auth.readthedocs.io/en/latest/index.html

django-allauthを使用します https://Django-allauth.readthedocs.io/en/latest/

1
Harry Moreno