33
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Deviseのconfirmableをオンにした時の挙動の変化の仕組みを理解する

Posted at

始まり

  • Facebookからのサインアップ時のみ、確認メールを飛ばすことなくそのまま続きの処理をする、という流れにするために、confirmableで何をやっているのかのコードを読んだ。

サインアップ時の処理

  • まずはサインアップ時の処理をやっているapp/controllers/devise/registrations_controller.rbcreateメソッドから見て行く。
  • ここを見ると、まず新規Userモデルを保存した後、user.active_for_authentication?を呼び出している。
  • このメソッドの大元はlib/devise/models/authenticatable.rbに書かれているが、lib/devise/models/confirmable.rbでオーバーライドされて以下のようになっている。
def active_for_authentication?
  super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
end
  • superはデフォルトでtrueを返すので、実質&&以降の処理。
  • 後半の三つは、「confirmed_atに値が入っているか?」「確認メールの期限が切れた状態になっていないか」を確認している。
  • 早い話がconfirmed_atに値が入って入ればtrueになる仕掛け。
  • なので、最初のサインアップした直後はこの値が必ずnilなので、返り値が必ずfalseになるようになっている。
  • 結果としてafter_inactive_sign_up_path_forに飛ばされる。これが、最初のページに飛ばされる仕組み。(trueの場合には、after_sign_up_path_forへ飛ばされる)
  • ではメールはどこで飛んでいるのか。実はlib/devise/models/confirmable.rbの中で、以下のようなコードがある。
after_commit :send_on_create_confirmation_instructions, on: :create, if: :send_confirmation_notification?
  • つまり、confirmed_atに値が入ってなければ、メールを飛ばす処理を仕込んでいるのだ。
  • 結論として、confirmableをオンにした状態で、確認処理を無くしたい場合には、新規Userを作成する時にconfirmed_atに値が入っている必要がある。
  • なので、app/controllers/devise/registrations_controller.rbcreateメソッドをオーバーライドしてこのようにしてあげればいい。
def create
  build_resource(sign_up_params)

  # skip_confirmationはconfirmed_atに現在時刻(UTC)を入れるメソッド
  resource.skip_confirmation!
  resource.save
  ...
end

FaceBookの場合

  • さてFacebookだが、実はFBではapp/controllers/devise/registrations_controller.rbではなく、app/controllers/devise/omniauth_callbacks_controller.rbfacebookメソッドに処理が帰ってくる。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end
  • これはdevise公式の例なのだが、この処理のどこかで、skip_confirmation!を実行する必要がある。
  • この中でUserを保存しているのはUser.from_omniauthだ。
def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.email = auth.info.email
    user.password = Devise.friendly_token[0,20]
    user.name = auth.info.name   # assuming the user model has a name
    user.image = auth.info.image # assuming the user model has an image
    # If you are using confirmable and the provider(s) you use validate emails, 
    # uncomment the line below to skip the confirmation emails.
    # user.skip_confirmation!
  end
end
  • ここではfirst_or_createを使っているので、もう直接引数で与えてしまう方が早い気がする。
def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    # skip confirmation
    user.confirmed_at = Time.now.utc
  end
end
  • これなら、FBの時はconfirmationを回避することができるようになる。
33
27
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
33
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?