0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rails7 + devise_token_authで RuntimeError (Locking a record with unpersisted changes is not supported. Use `save` to persist the changes, or `reload` to discard them explicitly.)が出る

Posted at

概要

Rails7でdevise_token_authを使用してユーザー認証機能を実装しようとしたところ
サインアップはうまく動作するがなぜかログイン時にRuntimeError (Locking a record with unpersisted changes is not supported. Use save to persist the changes, or reload to discard them explicitly.)というエラーが出る。
devise_token_authのissueでも同様の事例が報告されているが修正されていないため原因を調査。

発生箇所の特定

以下のようにエラーとなっている箇所をオーバライドしてpryしどのような問題が起きているか把握する。

class Api::V1::User::Auth::SessionsController < DeviseTokenAuth::SessionsController
  def create
    if field = (resource_params.keys.map(&:to_sym) & resource_class.authentication_keys).first
      q_value = get_case_insensitive_field_from_resource_params(field)

      @resource = find_resource(field, q_value)
    end

    binding.pry
    if @resource && valid_params?(field, q_value) && (!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
      valid_password = @resource.valid_password?(resource_params[:password])
      if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
        return render_create_error_bad_credentials
      end

      create_and_assign_token

      sign_in(@resource, scope: :user, store: false, bypass: false)

      yield @resource if block_given?

      render_create_success
    elsif @resource && !Devise.paranoid && !(!@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?)
      if @resource.respond_to?(:locked_at) && @resource.locked_at
        render_create_error_account_locked
      else
        render_create_error_not_confirmed
      end
    else
      hash_password_in_paranoid_mode
      render_create_error_bad_credentials
    end
  end

  private

  def create_and_assign_token
    if @resource.respond_to?(:with_lock)
      @resource.with_lock do
        @token = @resource.create_token
        @resource.save!
      end
    else
      @token = @resource.create_token
      @resource.save!
    end
  end
end

するとcreate_and_assign_tokenメソッドの@resource.with_lockで発生していることがわかった。

原因の調査

.with_lockでは対象のインスタンスが変更されているが保存済みでない場合にこのエラーがでるらしいのでpryで実際の変更内容を見てみる

@resource.changes
=> {"uuid"=>["3984f665-0550-43d8-9495-4cc3c42c7b1d", "390556e3-fde4-4d65-ab00-6e7a2e7f39e6"]}

uuidがなぜか変更されている。
uuidはUserモデルを作成する際にafter_initializeを使用して作成しているが、ログイン時にもなぜか変更されている。
今回はその原因までは調査しないが、ログイン時にすでに存在するuserモデルがinitializeされているため、すでにuuidが存在する場合はuuidを作成しないように修正することで問題が解決した。

# frozen_string_literal: true

class User < ActiveRecord::Base
  after_initialize :add_uuid
  # Include default devise modules. Others available are:
  # :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable

  def add_uuid
    self.uuid = SecureRandom.uuid unless uuid <=ここにunless uuidを追加
  end
end

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?