LoginSignup
11
11

More than 3 years have passed since last update.

deviseのTwitterログイン時はメール認証とメール送信をスキップする

Last updated at Posted at 2019-09-05

したいこと

以下の記事(devise + Twitterログイン)を参考に導入したRailsアプリで、Twitterログイン時のみメール送信処理を省きたかったのですが、地味に苦戦してしまったので備忘録として残します。
[Rails] deviseの使い方(rails5版)

なお、以降の記事は上記の記事に沿って話を進めるので、一度読んでおくことを推奨します。

実装手順

1. skip_confirmation! メソッドを利用する

Twitterで認証した後の処理なので、次のコールバック用コントローラーをどうにかすれば良さそう。

app/controllers/omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
    def twitter
        @user = User.from_omniauth(request.env["omniauth.auth"].except("extra"))

        if @user.persisted?
            sign_in_and_redirect @user
        else
            session["devise.user_attributes"] = @user.attributes
            redirect_to new_user_registration_url
        end

    end
end

skip_confirmation! というメソッドを使えばメール認証をスキップできるらしいので、以下のように else 以下を修正する。

app/controllers/omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
    def twitter
        @user = User.from_omniauth(request.env["omniauth.auth"].except("extra"))

        if @user.persisted?
            sign_in_and_redirect @user
        else
            @user.skip_confirmation!
            @user.save!
            # session["devise.user_attributes"] = @user.attributes
            # ↑ 認証データを覚える必要はないので削除
            # redirect_to new_user_registration_url
            # ↑ ログインすることになるので以下のように修正
            sign_in_and_redirect @user
        end
    end
end

ついでに記事内で作っていた self.new_with_session は必要ないので削除します。

app/models/user.rb
class User < ApplicationRecord
  ##省略

  #↓削除
  #def self.new_with_session(params, session)
  #  if session["devise.user_attributes"]
  #    new(session["devise.user_attributes"]) do |user|
  #      user.attributes = params
  #    end
  #  else
  #    super
  #  end
  #end
end

これで行けるかなとおもったらそんなに甘くない。。 emailpassword がないよって怒られてしまいました。
知らなかったのですが、deviseが用意するUserモデルではこいつらが必要なようです(当たり前といえばそうか)。。
スクリーンショット 2019-09-06 8.03.21.png

2. Email と Password をセットする

というわけで Email と Password をセットします。これは user.rb でレコードを作成している箇所があるので、そこについでに追加します。

app/models/user.rb
class User < ApplicationRecord
  ##省略

  def self.from_omniauth(auth)
    find_or_create_by(provider: auth["provider"], uid: auth["uid"]) do |user|
      user.provider = auth["provider"]
      user.uid = auth["uid"]
      user.username = auth["info"]["nickname"]
      user.password = Devise.friendly_token[0,20] #<-追加
      user.email = User.dumy_email(auth) #<-追加
    end
  end

  #↓追加
  private
    def self.dumy_email(auth)
      "#{auth.uid}-#{auth.provider}@example.com"
    end
end

passwordは Devise.friendly_token メソッドでランダムな値を自動生成します。便利ですね。

さて、emailに関してですが、こちら実はTwitterに登録されているemailは取ってくることができません。 user.username = auth["info"]["email"] のようにすると nil が返って来てしまいます。正確には可能なのですが、どうやら申請が必要らしく、許可が降りるのに2~3日かかるそうです。。

そのため今回は一意のメールアドレスを登録するようにメソッドを作って設定しています。

さて、これで問題なくできました! と思ったのですが、まだ必要なことがあるっぽい。。

3. メールを飛ばないようにする

さて、実際にTwitterでログインしてみるとちゃんとリダイレクトされたページに飛びました。見た目には問題なくできていたのですが、コンソールを見てみるとなぜかメールが送信された痕跡が。。(開発環境だったので、実際にメールは飛んでいません)

Devise::Mailer#confirmation_instructions: processed outbound mail in 306.2ms
Sent mail to XXXXXXXXXXXXX-twitter@example.com (998.8ms)
Date: Fri, 06 Sep 2019 10:19:04 +0000
From: test@example.com
Reply-To: test@example.com
To: XXXXXXXXXXXXX-twitter@example.com
Message-ID: <XXXXXXXXX@XXXXXX.mail>
Subject: Confirmation instructions
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

<p>Welcome XXXXXXXXXXXXX-twitter@example.com!</p>

<p>You can confirm your account email through the link below:</p>

<p><a href="http://localhost:3000/users/confirmation?confirmation_token=XXXXXX">Confirm my account</a></p>

??? なんで???
。。。と小一時間悩んでいたのですが、理由が解明しました。どうやらdeviseのconfirmableモジュールを利用していると、Userデータがデータベースに登録されたときにメールが飛ぶようになっているんですね。

omniauth_callbacks_controller.rb@user.save!skip_confirmation! メソッドの後に実行しているのですが、実はその前にすでにセーブされちゃっていたみたいです。
先程の user.rb では find_or_create_by メソッドを使っていますが、ここでデータが保存されてしまっていますね。。。

なので、以下のように create から initialize に変更して保存しないようにしましょう!
本家の記事では、おそらく email と password が設定されていないため保存されず、ちょうどいい感じの処理になっているんだと思います。)

app/models/user.rb
class User < ApplicationRecord
  ##省略

  def self.from_omniauth(auth)
    # find_or_create_by(provider: auth["provider"], uid: auth["uid"]) do |user|
    find_or_initialize_by(provider: auth["provider"], uid: auth["uid"]) do |user|
      user.provider = auth["provider"]
      user.uid = auth["uid"]
      user.username = auth["info"]["nickname"]
      user.password = Devise.friendly_token[0,20]
      user.email = User.dumy_email(auth)
    end
  end

  ##省略
end

これでやっと期待通りの処理ができるようになりました!

参考

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