设计在更新时不确认电子邮件
Posted
技术标签:
【中文标题】设计在更新时不确认电子邮件【英文标题】:Devise not confirming email on update 【发布时间】:2022-01-03 14:26:21 【问题描述】:我正在使用设备进行身份验证。我正在覆盖设计令牌生成器,以便我可以使用 6 位代码并覆盖它,以便我可以支持手机号码确认。
如果用户使用电子邮件注册并且 OTP 是通过电子邮件发送的。注册似乎工作正常。用户使用电子邮件注册。发送 OTP 并在确认后确认用户。
但是当用户尝试更新电子邮件时。我正在使用相同的方法来发送确认码(就像在注册时一样),用户被保存在 unconfirmed_email 中。邮件通过电子邮件发送,但确认后不会将用户电子邮件从 unconfirmed_email 字段复制到电子邮件字段。
这可能是什么问题。
app/services/users/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
app/services/users/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
config/nitializers/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
移动更新似乎工作正常,但电子邮件没有更新。我正在使用 graphql 更新电子邮件
在控制台中我尝试使用.confirm
,但它似乎无法正常工作,用户电子邮件未得到确认
【问题讨论】:
【参考方案1】:在您的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
【讨论】:
以上是关于设计在更新时不确认电子邮件的主要内容,如果未能解决你的问题,请参考以下文章
发送确认链接并单击原始电子邮件后,用新电子邮件更新用户的个人资料
.Net Core Entity Framework 电子邮件确认“单击此处”链接不更新“EmailConfirmed”数据库属性