Deviseの機能をカスタマイズする（例えばSNSアカウントでログインしているときはパスワード再発行できないようにする）方法

## 背景

Deviseを用いてログイン機能をつくり、
そしてGoogleログインとGithubログインを作った。
（もちろんググってコピペしながら！ [deviseとomniauthを使ったGoogle認証の流れ](https://qiita.com/s-show/items/9de77de4eb480f779aa4)を参考にしました！）

そして、GoogleログインとかGithubログインしている場合のみ、
登録してる名前とかアドレスをパスワード無しで変更できるようにしたくなりがち。
ユーザにとって「パスワードってなんや？」ってなるので。
（もちろんググってコピペしながら！ [Devise+OmniAuthでQiita風の複数プロバイダ認証](https://qiita.com/mnishiguchi/items/e15bbef61287f84b546e)の下の方を参考にしました！）

基本コピペやで！

## 問題発生

ということはですよ、
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ログインを作っていた感触はある！

## 解決までの方針

そこで私は解決までの方針を立てた。

1. パスワード変更はDeviseのコードのどの部分にあたるのか調べる
2. そのファイルを上書き

短いなぁ。

## パスワード変更はDeviseのコードのどの部分にあたるのか調べる

まずパスワード再発行のときの画面をchromeデベロッパーで見てみる。

[![https://gyazo.com/14121185a1965a04dfea120e189d6cc2](https://i.gyazo.com/14121185a1965a04dfea120e189d6cc2.png)](https://gyazo.com/14121185a1965a04dfea120e189d6cc2)

なるほど、`/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メソッドであると狙いをつける。

具体的には以下である（はず）

```github.com/plataformatec/devise/blob/master/app/controllers/devise/passwords_controller.rb
# 省略
  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`コントローラを上書きした（っぽい）。
その手順は、まずルーティング。

```config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    # 省略
    omniauth_callbacks: "users/omniauth_callbacks"
  }
end
```

そして、`app/controllers/users/omniauth_callbacks_controller.rb`を新規作成し、Deviseの`OmniauthCallbacksController`を継承。

```app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def google
    # 省略
  end
  # 省略
end
```

この手法をまねてみよう。
今回は`passwords`コントローラを上書きしたい。

まずルーティング。

```config/routes.rb
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`メソッド！

```app/controllers/users/passwords_controller.rb
class Users::PasswordsController < Devise::PasswordsController
  def create
  end
end
```

それから、指定したメールアドレスのuserの`provider`が存在したらリダイレクトでエラーメッセージ。
それ以外は、継承そのまま。
つまり、、

```app/controllers/users/passwords_controller.rb
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
```

## 検証

いけた！おっしゃ！

[![https://gyazo.com/495270ac4fa8099ad73462ad13eb365d](https://i.gyazo.com/495270ac4fa8099ad73462ad13eb365d.png)](https://gyazo.com/495270ac4fa8099ad73462ad13eb365d)

## つまりDeviseの機能のカスタマイズは

1. どのコントローラのどのメソッドか判断し
2. そのコントローラを継承した新コントローラ作成し
3. メソッドを上書きし
4. ルーティングを書き換える

でいけるのだ！
さあ、アメトーーク！みて寝よ。