「omniauth」とDevise「recoverableモジュール」を同時に使う時の挙動分岐

タイトルがいまいち意味不明ですが、下記のやりたいことを実装する上で調べたことが非常に勉強になったので記録しておきます。

#前提条件
・認証にdeviseを使用。googleなどの外部サービスのログインも実装している

#やりたいこと
①通常のemail、passwordでログインした場合、ユーザー情報を変更する```（edit_user_registration_path）```時にパスワード変更フォームを表示。情報変更の際に現在のパスワードの入力を要求するように設定。

②外部サービスログインを使った場合は```（edit_user_registration_path）```でパスワード変更フォーム、現在のパスワード入力欄を非表示。情報変更の際に現在のパスワード入力も不要な設定にする。

こういった挙動を実装したい。


#実装

<h3>①ログイン認証の方式によって、Viewの表示を変更する。</h3>

```ruby:app/views/devise/registrations/edit.html.erb

  
    <%if current_user.uid == nil %>  
      <div class="field">
        <%= f.label :password %> <br />
        <%= f.password_field :password, autocomplete: "new-password" , class: "form-control form-control-comment" %>
        <% if @minimum_password_length %>
          <br />
          <em><%= @minimum_password_length %> characters minimum</em>
        <% end %>
      </div>
        <p class="comment">(変更したくない場合は空白のままにしてください)</p>

      <div class="field">
        <%= f.label :current_password %> <br />
        <%= f.password_field :current_password, autocomplete: "current-password", class: "form-control form-control-comment" %>
      </div>
        <p class="comment">(変更するには現在のパスワード入力が必要です)</p> 
    <% end %>

```

通常のメールアドレスとパスワードのログインの場合はプロバイダから返ってくる、```uid```はnilになっている。

それをif文でViewの表示を分岐させた。

<h3>②ユーザー情報を更新する時にパスワード要否を分岐させる</h3>

deviseの初期設定ではユーザー情報を更新する時は現在のパスワード入力を求められる。


```ruby:app/controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController
.
.
.

 protected
  def update_resource(resource, params)
    if current_user.uid == nil
      resource.update_with_password(params)
    else
      resource.update_without_password(params)
    end
  end
end
```



<h3>どういうことだろう？</h3>
```rails g```で自作した```Users::RegistrationsController```は```Devise::RegistrationsController```を継承している。

情報を更新する時の仕組みは
```Devise::RegistrationsController```のupdateメソッドを読んでいる。
色々辿っていくと、

```ruby
  def update_resource(resource, params)
    resource.update_with_password(params)
  end
```

この```update_with_password```メソッドにつながる。

```ruby
require 'devise/strategies/database_authenticatable'

module Devise
  module Models
.
.
.

      def update_with_password(params, *options)

      ...

      current_password = params.delete(:current_password)

        if params[:password].blank?
          params.delete(:password)
          params.delete(:password_confirmation) if params[:password_confirmation].blank?
        end

        result = if valid_password?(current_password)
          update(params, *options)
        else
          assign_attributes(params, *options)
          valid?
          errors.add(:current_password, current_password.blank? ? :blank : :invalid)
          false
        end


.
.
.
  end
end
```

このメソッドは現在のパスワードが入力されていない、もしくは間違っている場合は情報を更新しないようになっている。



 
この```update_with_password```と同じところに```update_without_password```メソッドもある。
これこそがパスワードなしで情報を変更できるメソッド。



仕組みがわかれば```Users::RegistrationsController```で```update_resource```メソッドを上書きする。
やりたいことはViewと同じであり、現在のユーザーがプロバイダから返ってくるuidを保持しているかnilかで発火させるメソッドを分岐させた。

#学び
ググれば色んな方が情報発信してくれているが、一次情報を見る癖もつけないといけない




#参考
https://github.com/heartcombo/devise/tree/769506e96cd45b36a311eeca293ce0228c58e5f3

https://wbtaro.hatenablog.com/entry/2020/03/20/231246