KENZINE

Keng
Railsのエラーハンドリングで自分の思うベストプラクティス

技術系

発生した問題というのが

ログインボタンを押下し、認可・認証プロセスへ。
認証後アプリにリダイレクトし、ユーザー情報を書き込んだあと(ログインしたあと)一度ブラウザバックし、再度ログインボタンを押すと
ActionController::InvalidAuthenticityToken
というエラーが発生する

というもの。

使っているGemの中で関係がありそうなのはこのあたり

omniauth
omniauth-oauth2
omniauth-rails_csrf_protection


ぐぐってみるとこちらにissueがありました。
このブログを書いてる時点で数日前のコメントがあるのでまだ解消していないでしょう。
てかクックパッド製だったんだ。
https://github.com/cookpad/omniauth-rails_csrf_protection/issues/8

このGemはomniauthの脆弱性を補ってくれるもので、なくてもoauth認証はできると思いますが、
流石に仕事でやっていますし、気づいてしまったのでどうにかする必要がありました。
エラーが発生すること自体は問題ではないのですが、エラーが発生したときに、こちらで用意したエラー画面が表示されないことが問題です。
エラー画面はすでに用意してあって、アプリケーションのどこで発生してもハンドリングできる想定だったので困りました。

今回は当該エラーを処理するというよりは、アプリ全体で発生したエラーをすべて拾えるようにしたいと思い調べていたところ、
エラーハンドリング用のRack Middlewareがあるということを知ったのでそちらを採用しました。
ちなみにこのエラーが気に食わねぇんだ!という人は↑のgithubのissueに対応方法が記載されていますのでそちらを参考にしていただければ良いと思います。
https://github.com/cookpad/omniauth-rails_csrf_protection/issues/8#issuecomment-550833651
もっと根本的な解決方法はリダイレクトして戻った後に再度ログインボタンを押したときにoauthで使われるcsrf_tokenを更新するとかすればいいんだと思います。
でも古いトークンを使いまわしたらエラーになるのは正しい動きなのでこの方法は間違ってるんじゃないかと思いますけど。


さて、もし私と同じような境遇の人がいるとすれば、それはコントローラーでrescue_fromをつかってエラーハンドリングしている人か、
そもそもエラーハンドリングしてない人だと思います。
あるいはそもそもこのエラーを発したくない人ですがその解決策は知らないので他所をあたってみてください。
このrescue_fromをつかったエラーハンドリングは直感的で感覚的でとてもいい方法だと今でも思っています。
しかし、この方法には欠点があり、今回私が遭遇したようにRackMiddlewareで発生したエラーは検知できないという点です。
ではどうするのかというと、ActionDispatch::ShowExceptionsを使う方法です。
これはRailsがデフォルトで用意しているもので、rails newした際に作られる400.htmlなどが表示されるのはこれが働いているからです。

config/application.rbへ次のように記載します。

config.action_dispathc.rescue_responses.merge!(
	"HogeException" => :not_found
)

これはHogeExceptionをShowExceptionsで検知したらnot_foundに置き換えてあげるというものです。
デフォルトのままであればpublic/400.htmlが表示されるはずです。

またconfig/initializers/exceptions_app.rbへ以下の様へ書く事もできるようです。

Rails.configuration.exceptions_app = ->(env) { ErrorsController.action(:error).call(env) }


ErrorsControllerを作成しておけばすべてここで管理ができそうなのでこちらがいい感じです。


ここまではわかってきたので、ではこれを使ってどうやって処理してあげるのがいいかと言うのはこれからまた考えるなり調べたりする必要がありますが、
今後新規にアプリを立ち上げるときは最初からこちらを採用しておくのがいいかなと思ってます。
妙なところで区切りますがこんなところです。では。

間違っていたりしたらtwitterへご連絡ください :bow

KENZINE © Keng 2021.