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.wsgi
は django.core.settings
をインポートしていますので、 django.core.settings
の評価後でないとパッチできません。メイン app の app.py や url.py の評価時にパッチすれば良いと思います。