【Rails5】iOS Safariでブラウザを立ち上げるたびにDeviseログインしないといけない時の対応方法
この記事ではiPhoneのデフォルトブラウザであるSafariでウェブアプリケーションにアクセスした際にDeviseのログインセッションが保たれず、毎度ログインしないといけない時の対処法を書いていきます。
この記事の対象読者は以下の条件に当てはまる場合だと思います:
- Rails 5 を使用している
- ログイン機能にはDevise Gemを採用している
1.何が起きているのか
まず自動ログインがされない理由をみていきましょう。
iOSでSafariブラウザを立ち上げ、以下の手順を踏んだとしましょう。
- ウェブアプリにログインし、ログイン後のページへ遷移する
- ブラウザを閉じる
- 再度ブラウザを立ち上げてログイン後に遷移したページへアクセスする
Deviseのログインセッションが有効になっていれば、ユーザーがauthenticateされるはずなのですが、iOS Safariの場合だと以下のエラーが返ってくると思います。
ActionController::InvalidAuthenticityToken
authenticity_tokenとはcross-site request forgery(CSRF)の対策として用いられるものになります。詳しくはRailsのセキュリティガイドラインをご覧ください。
Railsではこのauthenticity_tokenはpersistent cookieではなくsession cookieに保存されるらしく、Safariブラウザを閉じた時にsession cookieもブラウザと一緒に消えてしまうため、authenticity_tokenの不一致が起きているようです(他のブラウザで問題ないのは恐らくこのsession cookieが保たれているからだと思います)。
さらにはRails5からprotect_from_forgeryメソッドがbefore_actionのチェインから外れたため、ユーザーのauthenticationが先に走ってしまっているのも原因の一つです。
2.よく見る対処法
この問題を調べていた時に以下の対処法が頻繁に挙げられていました。
- ブラウザのcacheを無効にする
- protect_from_forgeryをオフにする
確かに上記の方法でも再ログインしないといけない問題は解決されるかもしれませんが、別の問題に突き当たる可能性が出てきます。
①ブラウザのcacheを無効にした場合、毎回リクエストを送ることになるのでパフォーマンスが落ちる可能性があります。
②protect_from_forgeryをオフにした場合、CSRFの危険性がかなり増すことになります。
この問題の対処法として色んな記事に取り上げらていたりしますけど、根本的な解決にはなっていないような気がします。
3. 解決策
個人的に一番良いと思う解決策は以下です。
ユーザーのauthenticationをprotect_from_forgeryメソッドの後に行う
ちなみにこれはdeviseの公式README.mdに記されている方法です。
application_controllerでprotect_from_forgeryを呼び出している場合は、
1 |
protect_from_forgery prepend: true |
を追加してください。
authenticate_user!を使っている場合は呼び出している順序を変える必要があります。