こんにちは、hachi8833です。
Devise gemのWiki How-To翻訳、第2弾です。DeviseもRails 5.1対応で忙しそうですね。
Devise Wikiもくじリンク
- 「ワークフローのカスタマイズ」
- 「認証方法のカスタマイズ」「OmniAuth」
- 「ビュー/コンテンツのカスタマイズ」「特権/認証」
- 「テスト」「特殊な設定」
- 「アプリでのその他の設定」「JavaScript」
- 「他の認証プラグインからの移行」「アップグレード」
概要
- 原文: How To: Change the default sign_in and sign_out routes
- リビジョン: 5 Nov 2016 · 7 revisions
原文が更新されていることにお気づきの場合は、ぜひ@techrachoまでお知らせください。更新いたします。
[How-To] sign_inとsign_outのデフォルトルーティングを変更する
Deviseで使うロール(役割)が1つしかない状態で、ログイン(sign in)とログアウト用(sign out)のルーティングをデフォルトの /users/sign_in
や/users/sign_out
から/login
や/logout
に変更したいことがあります。
これはデフォルトのままのDeviseではできません。Deviseは、URLをチェックすることによってユーザーのアクセスするスコープを決定するからです。URLが/users/login
であればスコープはuser
であることがDevise側でわかりますが、/login
にアクセスすると、使われるべきスコープをDevise側で決定できません。幸いなことに、Deviseにはデフォルトスコープを指定するしくみがあるので、これによってURLを短くできます。
手順(Rails 3.0以降)
この設定は、アクセスに使うURLをルーティングのdevise_scope
に記述するだけで完了します。
devise_scope :user do
get 'login', to: 'devise/sessions#new'
end
以下のように、devise_scope
のエイリアスであるas
も使えます。
as :user do
get 'login', to: 'devise/sessions#new'
end
sign_out
の場合は次のようにします。
devise_scope :user do
delete 'logout', to: 'devise/sessions#destroy'
end
次のようにskip:
オプションですべてのセッションルーティングをスキップし、必要なルーティングだけを定義することもできます。
devise_for :users, skip: [:sessions]
as :user do
get 'signin', to: 'devise/sessions#new', as: :new_user_session
post 'signin', to: 'devise/sessions#create', as: :user_session
delete 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session
end
このようにすると、#authenticate_user!
などのヘルパーを使ったときに、定義済みの正しいカスタムページにユーザーをリダイレクトできます。
ただし、:sign_out_via
という設定オプションを使っていると上のsignout
アクションがエラーになることがあります。その場合は、デフォルトの動作を複製して次のように:sign_out_via
によってdelete
をget
に変更します。
devise_for :users, skip: [:sessions]
as :user do
get 'signin', to: 'devise/sessions#new', as: :new_user_session
post 'signin', to: 'devise/sessions#create', as: :user_session
match 'signout', to: 'devise/sessions#destroy', as: :destroy_user_session, via: Devise.mappings[:user].sign_out_via
end
別のもっとシンプルな方法
#devise_for
メソッドにはこういうときに便利なオプションパラメータが多数用意されています。
たとえば、users
という名前空間をすべてのルーティングから取り除き、sign_in
とsign_out
をそれぞれlogin
とlogout
に置き換えるには、次のようにします。
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout'}
ただし、Deviseで複数のコントローラ(パスワード用など)を使いたい場合は、最初の方法を使う必要があります。
morimorihogeより追記
上記の sign_out_via
に関連してすこし補足です。
Deviseではログインセッションをsessionという概念で管理しており、RESTful的には「新規sessionを作る(Devise::SessionsController#create)=ログインする」といった抽象化がされています。
そのため「ログアウトする」はsessionを破棄する、すなわちsessionの削除(Devise::SessionsController#destroy)というインターフェースになっています。
このとき、確かにsession削除はDELETEメソッドで呼ばれるのがRESTの概念上あるべき姿なのですが「ログアウトする」という機能自体はRailsの外から呼びたかったり、リダイレクト処理の中で強制ログアウトさせたいといったケースが考えられるため、DELETEメソッドよりもGETメソッドでログアウトできた方が便利です( _method=delete
とか付ければGETでもDELETEメソッドにbypassできますが、いちいち考えるのが面倒)。
というわけで、以下の様に sign_out_via
を設定してやることで、ログアウトについてはGETリクエストで処理でき、 link_to
ヘルパでも単なる destroy_user_session_path
へのリンクとして記述できるようになります。
Devise.config.sign_out_via = :get
= link_to 'ログアウト', destroy_user_path
関連記事(Devise)
- [Rails Devise]ユーザーのパスワードを自動生成する(シンプルな登録方法)(翻訳)
- Rails4: 古いdeviseのパスワードを新しいdeviseで使う方法
- Rails 3.1.0.rc8にしようとしたらdeviseが違うバージョンのbcryptに依存していてアップデートできない
- [Rails 3] deviseで使うモデルにfind_by_で始まる名前のscopeを定義するとrake db:migrate:resetが通らない
- [Rails 3] 失敗しないmigrationを書こう
- [Rails 3] Appサーバが複数だとdevise_openid_authenticatableで認証できない