2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

deviseでパスワードがない状態のままRememberableを適用する方法【Rails7系】

Posted at

当方、初学者であるため、内容に不備がある可能性があります。 不備があった場合はこっそり教えてくれると幸いです。

deviseでGoogle認証を行う際に、セッションを維持したいということでRememberableモジュールを導入して使用するとエラーが返ってきたので、解決方法を調べてみました。

解決方法は結論は簡潔なのですが、deviseのメソッドの処理を見ることでなぜそのようなエラーが出るのかを理解することが出来ます。

発生した事象

デプロイする度にGoogleログインのセッションが破棄されてしまうという状態が発生していたため、remember_me(@user)でセッションを記憶しておくようにしようと変更を加えました。

しかし、その変更を加えるとGoogle認証に失敗するようになってしまいました。

結論(一旦)

結論としては、「remember_tokenカラムをusersテーブルに追加する」もしくは「ユーザー情報を保存する際にパスワードの値をfriendly_tokenでランダムなパスワードを設定する」で解決出来ます。

ランダムであれパスワードを設定したくなかったので、今回は前者の「remember_tokenカラムをusersテーブルに追加する」を行っています。

deviseの公式ドキュメントに以下のように書かれています。

remember_me(@user)

You should include module Devise::Controllers::Rememberable on your controller to use it and ensure a password is always set or have a remember_token column in your model or implement your own rememberable_value in the model with custom logic.

include Devise::Controllers::Rememberableでモジュールを導入することは前提として、「パスワードが常に設定されている」もしくは「remember_tokenカラムが存在している」もしくは「独自のロジックでrememberable_valueメソッドがモデルに存在している」ことのいずれかを満たしていること。

と書かれていますが、私はいずれも満たしていないことからGoogle認証に失敗していました。(Googleログインのみの場合、ユーザーテーブルのパスワードはnilとなっているからです。)

remember_tokenカラムを追加する前に、なぜこのようなことが必要なのかが分からなかったので、Rememberableが持つメソッドの挙動を確認することにしました。

Rememberableの全体の処理の流れ

まず前提として、rememberableの大まかな処理の流れは以下のようになっています。

  1. Cookieの保存(ログイン時)
  2. Cookieの検証(次回ログイン試行時)
  3. Cookieの破棄(サインアウト時)

Cookieの検証

Cookieの検証について注目としてみると、検証の途中の内部処理は以下のDevise.secure_compare(rememberable_value, token)にて、引数のtokenrememberable_valueを比べて**「このクッキーのトークンはまだ期限内で、ユーザーの最新の remember_token と一致してるかどうか」**の判定を行っています。

      def remember_me?(token, generated_at)
        # TODO: Normalize the JSON type coercion along with the Timeoutable hook
        # in a single place https://github.com/heartcombo/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
        if generated_at.is_a?(String)
          generated_at = time_from_json(generated_at)
        end

        # The token is only valid if:
        # 1. we have a date
        # 2. the current time does not pass the expiry period
        # 3. the record has a remember_created_at date
        # 4. the token date is bigger than the remember_created_at
        # 5. the token matches
        generated_at.is_a?(Time) &&
         (self.class.remember_for.ago < generated_at) &&
         (generated_at > (remember_created_at || Time.now).utc) &&
         ## ココ!!
         Devise.secure_compare(rememberable_value, token)
      end

次に、rememberable_valueを確認してみると、以下のようになっています。
ここでは、「remember_tokenカラムがあればその値を返し、authenticatable_salt があればその値を返し、どちらも無ければ例外処理を発生させる」といった処理になっていることが確認出来ます。

authenticatable_salt はパスワードから生成されるものですので、パスワードが存在していなければいけません。

よって、最初の問題に戻ると、remember_tokenカラムも存在していけなければ、パスワードもセットされていないことが理由でCookieの検証が出来ず、例外処理が発生していたということになります。

      def rememberable_value
        if respond_to?(:remember_token)
          remember_token
        elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
          salt
        else
          raise "authenticatable_salt returned nil for the #{self.class.name} model. " \
            "In order to use rememberable, you must ensure a password is always set " \
            "or have a remember_token column in your model or implement your own " \
            "rememberable_value in the model with custom logic."
        end
      end
# パスワードがnilだと生成されない
      def authenticatable_salt
        encrypted_password[0,29] if encrypted_password
      end

終わりに

以上より、なぜdeviseのRememberableモジュールを用いる際に「remember_tokenカラムをusersテーブルに追加する」もしくは「ユーザー情報を保存する際にパスワードの値をfriendly_tokenでランダムなパスワードを設定する」のいずれかを満たしている必要があるかどうかの理由が分かりました。

以上です。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?