ワシはワシが育てる

週刊少年ジャンプと任天堂のゲームが三度のメシより好きです。

Deviseでパスワード変更時にログアウトしない方法

Railsデファクト認証システムの一つであるDeviseにおいて、デフォルトではログイン中にパスワードを変更するとサインアウトされてしまいます。
原因としては保持しているセッション情報と、更新されたデータベースとの整合が取れずにサインアウトされてしまうのだと思います。

そこでログアウトしない方法はないかと調べた所、サインインする際に「bypass: true」という属性を付与するといいらしい。ということで早速やってみると望み通りの挙動になりました。

といってもbypassオプションの動作を知らずに使うのも怖いので、Githubで調べたらメカニズムは単純でした。

当該Githubページはこちら

def sign_in(resource_or_scope, *args)
  options  = args.extract_options!
  scope    = Devise::Mapping.find_scope!(resource_or_scope)
  resource = args.last || resource_or_scope

  expire_data_after_sign_in!

  if options[:bypass]
    warden.session_serializer.store(resource, scope)
  elsif warden.user(scope) == resource && !options.delete(:force)
    # Do nothing. User already signed in and we are not forcing it.
    true
  else
    warden.set_user(resource, options.merge!(scope: scope))
  end
end

bypassオプションが付与されている場合はwardenのsession_serializerクラスのstoreメソッドが呼び出されています。

そしてwardenのstoreメソッドを調べると以下のようになっています。

def store(user, scope)
  return unless user
  method_name = "#{scope}_serialize"
  specialized = respond_to?(method_name)
  session[key_for(scope)] = specialized ? send(method_name, user) : serialize(user)
end

実際にsessionを調べるとより分かりやすいですが、事細かい理解はともかく、セッションを更新することで、最新のデータに対応していることがわかります。

ということで原理をなんとなく理解した上で、要件を達成できたのでブログに書き起こしてみました。