deviseのconfirmableの仕組み

deviseのconfirmableを使うには対象modelで宣言

  devise :confirmable

必要なカラム

class AddConfirmableToUser < ActiveRecord::Migration[5.0]
def change
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_column :users, :unconfirmed_email, :string # Only if using reconfirmable
add_index :users, :confirmation_token, unique: true
end
end

メール呼び出し制御

unconfirmed_emailに関してはメールアドレス変更時に、確認メールを送って、そのリンクを踏んだ場合に変更になる仕様の場合のみ追加すればokです。

# config/initializer/devise.rb
config.reconfirmable = true

もうこれだけで,
– 新規登録時に確認メールを送信して、リンクを踏んだ場合
– メールアドレス変更時に確認メールを送信して、リンクを踏んだ場合だけ有効
になります。

新規登録時のメールだけ送りたくない場合はskip_confirmation!をsaveメソッドの前にユーザーオブジェクトに対して呼んでください。
メールアドレス変更時に確認メールを送信をスキップしたい場合は
`skip_reconfirmation’です。

内部的な仕組み

仕組みとしては宣言したモデルのメールアドレスが変更になったのを検知して、
上記追加したカラムにupdateが走ります。
ここで変更する予定のメールアドレスがunconfirmed_emailに一時的に格納されます。
その後mailerが呼ばれます。

  SQL (0.5ms)  UPDATE "users" SET "updated_at" = ?, "confirmation_token" = ?, "confirmation_sent_at" = ?, "unconfirmed_email" = ? WHERE "users"."id" = ?  [["updated_at", 2016-12-13 06:43:56 UTC], ["confirmation_token", "CDk43QgXWYxoUqs4ubU4"], ["confirmation_sent_at", 2016-12-13 06:43:56 UTC], ["unconfirmed_email", "change@email.jp"], ["id", 5]]
(1.6ms)  commit transaction
Rendering users/mailer/confirmation_instructions.text.erb
Rendered users/mailer/confirmation_instructions.text.erb (0.7ms)
Devise::Mailer#confirmation_instructions: processed outbound mail in 43.2ms

そしてメールが飛び、そこにリンクがあります。
リンクにtokenが埋め込まれているので、そのトークンをもとにユーザーをテーブルから呼び出します。
かんたんなコードにするとこんな感じ。
そしてemailカラムにunconfirmed_emailを代入して、その後nilにする処理がdeviseのconfirmメソッドで行われます。

@user = User.find_by(confirmation_token: params[:confirmation_token])
Started GET "/users/confirmation?confirmation_token=CDk43QgXWYxoUqs4ubU4" for ::1 at 2016-12-13 15:46:21 +0900
Processing by Users::ConfirmationsController#show as HTML
Parameters: {"confirmation_token"=>"CDk43QgXWYxoUqs4ubU4"}
User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."confirmation_token" = ? ORDER BY "users"."id" ASC LIMIT ?  [["confirmation_token", "CDk43QgXWYxoUqs4ubU4"], ["LIMIT", 1]]
(0.1ms)  begin transaction
SQL (0.3ms)  UPDATE "users" SET "confirmed_at" = ?, "unconfirmed_email" = ?, "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["confirmed_at", 2016-12-13 06:46:21 UTC], ["unconfirmed_email", nil], ["email", "change@email.jp"], ["updated_at", 2016-12-13 06:46:21 UTC], ["id", 5]]
      def confirm(args={})
pending_any_confirmation do
if confirmation_period_expired?
self.errors.add(:email, :confirmation_period_expired,
period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
return false
end
self.confirmed_at = Time.now.utc
saved = if pending_reconfirmation?
skip_reconfirmation!
self.email = unconfirmed_email
self.unconfirmed_email = nil
# We need to validate in such cases to enforce e-mail uniqueness
save(validate: true)
else
save(validate: args[:ensure_valid] == true)
end
after_confirmation if saved
saved
end
end

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です