簡體   English   中英

Devise 在更新時未確認 email

[英]Devise not confirming email on update

我正在使用 devise 進行身份驗證。 我正在覆蓋 devise 令牌生成器,以便我可以使用 6 位代碼並覆蓋它,以便我可以支持手機號碼確認。

如果用戶注冊 email 並且 OTP 通過 email 發送。 注冊似乎工作正常。 用戶注冊 email。 發送 OTP 並在確認后確認用戶。

但是當用戶嘗試更新 email 時。 我使用相同的方法來發送確認碼(就像在注冊中一樣),用戶被保存在 unconfirmed_email 中。 郵件在 email 中發送,但在確認后,用戶 email 未從 unconfirmed_email 字段復制到 email 字段。

這里可能是什么問題。

應用程序/服務/用戶/confirmation_code_sender.rb

# frozen_string_literal: true

module Users
  class ConfirmationCodeSender
    attr_reader :user

    def initialize(id:)
      @user = User.find(id)
    end

    # rubocop :disable Metrics/AbcSize
    def call
      generate_confirmation_token!

      if user.email?
        DeviseMailer.confirmation_instructions(
          user,
          user.confirmation_token,
          { to: user.unconfirmed_email || user.email }
        ).deliver_now
      else
        Telco::Web::Sms.send_text(recipient: user.unconfirmed_mobile || user.mobile_number, message: sms_text)
      end
    end
    # rubocop :enable Metrics/AbcSize

    private

    def generate_confirmation_token!
      user.confirmation_token = TokenGenerator.token(6)
      user.confirmation_sent_at = DateTime.current
      user.save!(validate: false)
    end

    def sms_text
      I18n.t('sms.confirmation_token', token: user.confirmation_token)
    end
  end
end

應用程序/服務/用戶/phone_or_email_updater.rb

# frozen_string_literal: true

module Users
  class PhoneOrEmailUpdater < BaseService
    def call
      authorize!(current_user, to: :user?)

      current_user.tap do |user|
        user.update!(unconfirmed_mobile: params[:unconfirmed_mobile], unconfirmed_email: params[:unconfirmed_email])
        ConfirmationCodeSender.new(id: user.id).call
      end
    end
  end
end

配置/初始化器/confirmable.rb

# frozen_string_literal: true

# Overriding this model to support the confirmation for mobile number as well

module Devise
  module Models
    module Confirmable
      def confirm(args = {})
        pending_any_confirmation do
          return expired_error if confirmation_period_expired?

          self.confirmed_at = Time.now.utc
          saved = saved(args)
          after_confirmation if saved
          saved
        end
      end

      def saved(args)
        @saved ||= if pending_reconfirmation?
                     skip_reconfirmation!
                     save!(validate: true)
                   else
                     save!(validate: args[:ensure_valid] == true)
                   end
      end

      def pending_reconfirmation?
        if unconfirmed_email.present?
          self.email = unconfirmed_email
          self.unconfirmed_email = nil
          true
        elsif unconfirmed_mobile.present?
          self.mobile_number = unconfirmed_mobile
          self.unconfirmed_mobile = nil
          true
        else
          false
        end
      end

      private

      def expired_error
        errors.add(
          :email,
          :confirmation_period_expired,
          period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago)
        )
        false
      end
    end
  end
end

移動更新似乎工作正常,但 email 沒有更新。 我正在使用 graphql 來更新 email

在控制台中我嘗試使用.confirm但它似乎無法正常工作,用戶 email 沒有得到確認

在您的pending_reconfirmation? , self.unconfirmed_email被指定為nil 好像是pending_reconfirmation? 僅在saved中調用,但是也由pending_any_confirmation調用。

https://github.com/heartcombo/devise/blob/8593801130f2df94a50863b5db535c272b00efe1/lib/devise/models/confirmable.rb#L238

# Checks whether the record requires any confirmation.
def pending_any_confirmation
  if (!confirmed? || pending_reconfirmation?)
    yield
  else
    self.errors.add(:email, :already_confirmed)
    false
  end
end

那么第二次pending_reconfirmation? saved的,pending_reconfirmation 中調用? 將返回 false,因為 unconfirmed_email 為 nil。

您最好不要在以?結尾的方法中進行實際分配。 這將是一個隱含的副作用。 也許創建一個以!結尾的新方法改變屬性的值。

例如:

module Devise
  module Models
    module Confirmable
      def confirm(args = {})
        pending_any_confirmation do
          return expired_error if confirmation_period_expired?

          self.confirmed_at = Time.now.utc
          saved = saved(args)
          after_confirmation if saved
          saved
        end
      end

      def saved(args)
        @saved ||= if pending_reconfirmation?
          reconfirm_email! if unconfirmed_email.present?
          reconfirm_mobile! if unconfirmed_mobile.present?
          skip_reconfirmation!
          save!(validate: true)
        else
          save!(validate: args[:ensure_valid] == true)
        end
      end

      def pending_reconfirmation?
        unconfirmed_email.present? || nconfirmed_mobile.present?
      end

      def reconfirm_email!
        self.email = unconfirmed_email
        self.unconfirmed_email = nil
      end

      def reconfirm_mobile!
        self.mobile_number = unconfirmed_mobile
        self.unconfirmed_mobile = nil
      end

      private

      def expired_error
        errors.add(
          :email,
          :confirmation_period_expired,
          period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago)
        )
        false
      end
    end
  end
end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM