とりあえずメモ。
:request_path
オプションと:callback_path
オプション
OmniAuthでproviderを宣言する際に:request_path
オプションにProcを渡すことで、デフォルトだと/auth/:provider
となっているリクエストフェーズ開始のパスを動的に設定できる。
たとえば次のようにすると、パスが/auth/foo
で終わってるリクエストはすべてfooプロバイダでのリクエストフェーズであるとして処理されるようになり、たとえば/quwup/auth/foo
とかでもヒットする。
Rails.application.config.middleware.use OmniAuth::Builder do provider :foo, request_path: -> { |env| # パスそのものではなく、このリクエストがリクエストフェーズ開始のもので # あるか否かをBooleanで返すようにするのがポイント request = Rack::Request.new(env) request.end_with?("/auth/foo") } end
このへんの処理はOmniauth::Strategy#on_request_path?あたりで行われている。
同様にコールバックのパスも、:callback_path
オプションにProcを渡すことで動的に設定できる。
Rails.application.config.middleware.use OmniAuth::Builder do provider :foo, on_callback_path: -> { |env| # パスそのものではなく、このリクエストがコールバックフェーズ開始のもので # あるか否かをBooleanで返すようにするのがポイント request = Rack::Request.new(env) request.end_with?("/auth/foo/callback") } end
このへんの処理はOmniAuth::Strategy#on_callback_path?あたりで行われている。on_request_path?
よりも複雑なことをやっている。
コールバックのパスは動的に作られない
前述の例のようにrequest_path
とcallback_path
にProcを渡した場合、それが使われてるのはあくまで今来たリクエストがリクエストフェーズのものもしくはコールバックフェーズのものであるかを判定しているに過ぎず、リクエストのenv
に応じて文字列を作り出しているわけではない。
そこで厄介になるのが、プロバイダ側に送信するコールバックのパスはそのままだと動的に作れないということだ。
omniauth-openidではリクエストフェーズで呼ばれたOmniAuth::Strategy#callback_pathの返り値を含んだOmniAuth::Strategy#callback_urlの返り値がreturn_to
パラメータとしてプロバイダ側に送信される。
このcallback_path
の決定が難儀なもので、リクエストフェーズでのことなので2行目のoptions[:callback_path].call(env)
はtrueになることはなく、あれよあれよと/auth/foo/callback"
みたいに決め打ちされる。分岐はあるけど外からいじれる感じではない。
このため:callback_path
オプションでカジュアルにいじると、OpenIDとかで事前にプロバイダに送っておいた決め打ちのcallback_url
と、認証が終わってリダイレクトされてきたカジュアルなURLとがマッチしないということが発生する。つらい。
モンキーパッチ当てるとか、SCRIPT_NAMEをいじるとか
じゃあ実際どうするのよというと、OmniAuth::Strategy#callback_url
にモンキーパッチをあててしまうのが手っ取り早い。
module OmniAuthCasualCallbackUrl def callback_url # ちなみにenvも使える full_host + script_name + "/quwup/auth/foo/callback" + query_string end end module OmniAuth::Strategy prepend OmniAuthCasualCallbackUrl end
もしくはprefixを付けたいぐらいであれば、before_callback_phase
でSCRIPT_NAMEをいじるのも有りかもしれない。envをいじるので微妙な気はするけども…。
Rails.application.config.middleware.use OmniAuth::Builder do provider :foo, on_callback_path: (env)-> { # パスそのものではなく、このリクエストがコールバックフェーズ開始のもので # あるか否かをBooleanで返すようにするのがポイント request = Rack::Request.new(env) request.end_with?("/auth/foo/callback") } end OmniAuth.configure do |config| config.before_request_phase = (env)-> { env["SCRIPT_NAME"] = "quwup" } end