背景
Deviseを用いてログイン機能をつくり、
そしてGoogleログインとGithubログインを作った。
(もちろんググってコピペしながら! deviseとomniauthを使ったGoogle認証の流れを参考にしました!)
そして、GoogleログインとかGithubログインしている場合のみ、
登録してる名前とかアドレスをパスワード無しで変更できるようにしたくなりがち。
ユーザにとって「パスワードってなんや?」ってなるので。
(もちろんググってコピペしながら! Devise+OmniAuthでQiita風の複数プロバイダ認証の下の方を参考にしました!)
基本コピペやで!
問題発生
ということはですよ、
GoogleログインとかGithubログインしている場合のみ、**Forgot your password?**の機能も消したくなりがち。
ユーザにとって「パスワードってなんや?」ってなるので。
コピペばっかしてたからDeviseのカスタマイズ方法わからん。
その時の解決方法を以下に書いてみた。
Deviseの正体
いろいろググってるとDeviseは
https://github.com/plataformatec/devise/tree/master/app/controllers/devise
に基づいて動いているらしい。
うん、確かにomniauth_callbacks_controller.rb
とかregistrations_controller.rb
をオーバーライドする感じでGoogleログインを作っていた感触はある!
解決までの方針
そこで私は解決までの方針を立てた。
- パスワード変更はDeviseのコードのどの部分にあたるのか調べる
- そのファイルを上書き
短いなぁ。
パスワード変更はDeviseのコードのどの部分にあたるのか調べる
まずパスワード再発行のときの画面をchromeデベロッパーで見てみる。
なるほど、/users/password
にPOSTでデータを送っているっぽい。
つぎはルーティングを見てみる。
$ rails routes
(省略)
new_user_password GET /users/password/new(.:format) users/passwords#new
edit_user_password GET /users/password/edit(.:format) users/passwords#edit
user_password PATCH /users/password(.:format) users/passwords#update
PUT /users/password(.:format) users/passwords#update
POST /users/password(.:format) users/passwords#create
(省略)
なるほど、つまり、users/passwords
のcreateメソッドがパスワード再発行をつかさどるはず。
つまりパスワード再発行のソースは https://github.com/plataformatec/devise/blob/master/app/controllers/devise/passwords_controller.rb のcreateメソッドであると狙いをつける。
具体的には以下である(はず)
# 省略
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
# 省略
そのファイルを上書き
そう、つぎはこのファイルを上書きせねばならない。
Googleログイン実装時は、omniauth_callbacks
コントローラを上書きした(っぽい)。
その手順は、まずルーティング。
Rails.application.routes.draw do
devise_for :users, controllers: {
# 省略
omniauth_callbacks: "users/omniauth_callbacks"
}
end
そして、app/controllers/users/omniauth_callbacks_controller.rb
を新規作成し、DeviseのOmniauthCallbacksController
を継承。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google
# 省略
end
# 省略
end
この手法をまねてみよう。
今回はpasswords
コントローラを上書きしたい。
まずルーティング。
Rails.application.routes.draw do
devise_for :users, controllers: {
# 省略
omniauth_callbacks: "users/omniauth_callbacks",
passwords: "users/passwords"
}
end
つぎは、app/controllers/users/passwords_controller.rb
を新規作成。
DeviseのPasswordsController
を継承させる。
で、いじるのはcreate
メソッド!
class Users::PasswordsController < Devise::PasswordsController
def create
end
end
それから、指定したメールアドレスのuserのprovider
が存在したらリダイレクトでエラーメッセージ。
それ以外は、継承そのまま。
つまり、、
class Users::PasswordsController < Devise::PasswordsController
def create
if User.find_by(email: params["user"]["email"]).provider.present?
redirect_to new_user_password_path, notice: 'GoogleアカウントもしくはGithubアカウントで登録している場合はパスワードは使用されていません'
else
super
end
end
end
検証
いけた!おっしゃ!
つまりDeviseの機能のカスタマイズは
- どのコントローラのどのメソッドか判断し
- そのコントローラを継承した新コントローラ作成し
- メソッドを上書きし
- ルーティングを書き換える
でいけるのだ!
さあ、アメトーーク!みて寝よ。