[Django] python-social-auth + social-auth-app-django で、リダイレクトURLのスキーム(http/https)をコントロールする


python-social-auth + social-auth-app-django を使ってログインシステムを構築している場合、OAuth プロバイダーにリダイレクトする際にクエリパラメーターに redirect_uri パラメーターを付与し、認証完了後にそのURLに戻ってきます。

その時、サーバーの構成によっては redirect_uri の URL のスキームが、本来 https:// でなければならない所が http:// になって困ることがあります。逆に PC で開発している時は、今度は http:// になって欲しい箇所が https:// になって困ることもあります。

それらのコントロールをする際、どのような設定をすればいいか、どこを見ればいいかを書いておきます。

平文の URIに戻される理由

サーバーシステムが全て https:// なのに、 redirect_uri のスキームが平文の http:// になる場合があります。

この原因は、システムの途中のリバースプロキシが https をデクリプトし、平文 http にしてバックエンドサーバーにリクエストするためです。

Django はそれを平文http のリクエストとして受け付けるため、平文 http リダイレクトURLを生成します。

django.core.handlers.wsgi.WSGIRequest の _get_scheme で行っています。

対策

A. SECURE_PROXY_SSL_HEADER を使う

settings

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

と書いておけば、リクエストヘッダーに X-Forwarded-Proto: https が含まれてた場合、リダイレクトのスキームを https:// にします。

使用箇所は、 django/http/request.py の HttpRequest.scheme プロパティを見てください。

注意点として、この設定の2項目目の 'https' は、「X-Forwarded-Proto ヘッダーが無かった時のデフォルトのスキーム」ではありません

X-Forwarded-Proto ヘッダーが、この値と一致した時は https になり、不一致の時は http になります。

そのため、もし

SECURE_PROXY_SSL_HEADER = ('HTTP_X_REDIRECT_URL_IS_HTTPS', '1')

という設定にした場合は、リクエストヘッダーが X-Redirect-Url-Is-Https: 1 となっていた場合に https となり、それ以外は http となります。

B. SOCIAL_AUTH_REDIRECT_IS_HTTPS を使う

social_core/strategy.py の BaseStrategy.absolute_uri() を見ると、Django の settings

SOCIAL_AUTH_REDIRECT_IS_HTTPS = True

もしくは

REDIRECT_IS_HTTPS = True

と書くと、ログイン時の redirect_uri のスキームを強制的に https に書き換えれることがわかります。

*_REDIRECT_IS_HTTPS この prefix は、ライブラリの使い方によって変化しますのでご注意ください。

C. 認証バックエンドの get_redirect_uri をオーバーライドする

social.backends.oauth.BaseOAuth2 等を継承して、独自の認証バックエンドを作って使っている場合は、get_redirect_uri メソッドをオーバーライドしても良いと思います。

def get_redirect_uri(self, state=None):
uri = super().get_redirect_uri(state)
# ここで、条件によって URL スキームを文字列置換
return uri

D. 独自 Strategy の absolute_uri をオーバーライドする

social_core.strategy.BaseStorategy を継承した独自の Strategy を作って使っている場合は、 absolute_uri メソッドをオーバーライドすることでURIスキームを自由にコントロールできます。

E. django.core.handlers.wsgi.WSGIRequest の _get_scheme をパッチする

絶対URLを生成する際、リクエストのスキームの判定のため django.http.request.HttpRequest.scheme や、もしくはそこから django.core.handlers.wsgi.WSGIRequest の _get_scheme  がコールされます。

少し強引ですがそこをパッチして、返すスキームをコントロールすることができます。

例えば、下記のようにパッチすれば、絶対URLを作る時はデフォルトで https スキームになります。

from django.core.handlers.wsgi import WSGIRequest

def _get_scheme(self):
return 'https'

WSGIRequest._get_scheme = _get_scheme

また、下記のようにパッチをすれば settings の値で動作を変えられます。

from django.core.handlers.wsgi import WSGIRequest
from django.conf import settings

def _get_scheme(self):
return getattr(settings, 'ABSOLUTE_URL_SCHEME', 'https')

WSGIRequest._get_scheme = _get_scheme

この場合、social-auth-app-django + python-social-auth に限らず、広い範囲で絶対URL を作る際に適用されますのでご注意ください。

また、 django.core.handlers.wsgidjango.core.settings をインポートしていますので、 django.core.settings評価後でないとパッチできません。メイン app の app.py や url.py の評価時にパッチすれば良いと思います。

現在未評価

コメント

コメントを投稿
コメントするには TORICO-ID にログインしてください。
ログイン コメント利用規約