【SNS認証】Twitter認証によりユーザー新規登録を行う

おはようございます。
今日はSNS認証ログインについて記事を書きたいと思います。twitter認証での実装を行いました。

対象

対象となるのはdeviseによるログイン、新規登録機能ができていて、deviseの機能が何となくわかっている方とします。

実装までの流れ

① Twitter API 登録 (アカウント申請)を行う
② SNS認証に必要なgemをインストールする
③ ルーティングを編集する
④ 環境変数の設定、callback URLの設定をする
⑤ viewにtwitter登録のためのボタンをつくる
⑥ usersテーブルにuidカラムとproviderカラムをつくる
⑦ モデルにデータベース登録をするためのメソッドをかく
⑧ コントローラーにログインするための処理をかく

手順は多いですが、一つ一つの作業は少なめなので簡単に実装できます。

① twitter API登録(アカウント申請)を行う

下記のリンクでAPI登録を行います。簡単に言うと、Twitter社にTwitterに登録してある情報をアプリに使ってもいいですか?という登録になります。やり方は下記Qitta記事を見ながらしましたが、簡単にできました。申請に時間がかかるのかなと思っていましたが、1分も経たず承認されました。笑 申請に記入する欄がたくさんありますが、そこまで考え込む必要はないです。Google翻訳を使ってパパっとやっちゃいましょう。
https://developer.twitter.com/ja
https://qiita.com/kngsym2018/items/2524d21455aac111cdee


② SNS認証に必要なGemをインストールする

今回必要なGemは以下の3つです。dotenv-rails環境変数を管理することができるアプリで今回はこちらを使用しました。

gemfile

gem 'omniauth-twitter'
gem 'omniauth'
gem 'dotenv-rails'


③ ルーティングの編集をする

ルーティングを以下のように追記します。

devise_for :users, controllers: {
  omniauth_callbacks: 'users/omniauth_callbacks'
}


④ 環境変数の設定、callback URLの設定をする

twitter developerのkey and tokensのタブからAPIキーとSECRETキーの環境変数を持ってきて、アプリ側に持たせる必要があります。まずは、appなどと同じディレクトリに.envファイルを作ります。そして、そのファイル内にtwitter developerからコピーしたAPIキーとSECRETキーの環境変数を定義します。

.env

TWITTER_API_KEY = ~~~~~~
TWITTER_API_SECRET = ~~~~~~

また、環境変数は他人に見られてはいけないため、githubにpushされないよう.gitignoreファイルを編集します。
.gitignore

.env      #追記

これで環境変数を使う準備はできたので、config/initializers/devise.rbにAPIキーを記載します。
devise.rb

Devise.setup do |config|
  (中略)
  config.omniauth :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET'], scope: 'email'
end



最後にtwitter developerに、callback URLを記載します。

f:id:shun_0211:20200610105240p:plain

これで、twitter認証を行う準備はできました。
もし、403 Forbiddenというエラーが出るようならcallback URLが間違っている可能性が高いので確認しましょう。

f:id:shun_0211:20200610094620p:plain


※ callback URLの指定のやり方でlocalhost~~ではなく127.0.0.1~~としないとエラーになるという記事がありましたが、自分の場合localhost~~ で問題なかったです。


⑤ viewにtwitter登録のためのボタンをつくる

当然twitter登録のためのボタンが必要なので、viewに作っていきます。リンク先のパスはusers/omniauth_callbacksコントローラーのpassthruアクションを行うので、そちらのパスを指定します。この後にでてきますが、実際動かしているのはtwitterアクションじゃないかというツッコミが入りそうですが、omniauthの中でどのような動きをしているのかは分からないのでまた分かれば記事にしたいと思います。

.button__box
  = link_to  user_twitter_omniauth_authorize_path, class: "twitter__registration__button" do
    %i.fab.fa-twitter
     Twitterで登録する


⑥ usersテーブルにuidカラムとproviderカラムをつくる

uidとは利用者識別用のIDになります。twitterはuidを見てどのユーザーなのかを見分けているので、今回導入するtwitter認証でもこちらを利用します。また、providerカラムはどのSNSを使ってログインしようとしているのかを明記するために作ります。そうしないと、確率は低いですが、twitterのuidとfacebookでのuidがたまたま一致してしまった場合、不具合となります。後の処理ででてきますが、このuidとproviderが一致していないときにusersテーブルを新たに作成します。では、早速usersテーブルにカラムを追加します。

terminal
$ bundle exec rails g migration AddColumnsToUsers provider:string uid:string

20200606044514_add_columns_to_users.rb

class AddColumnsToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
  end
end

terminal
rails db:migrate


⑦ コントローラーにログインするための処理をかく

いよいよ本番です。コントローラーにログインするための処理を書いていきます。
omniauth_collbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
 
  def twitter
    callback_from :twitter
  end

  private
  # コールバック時に行う処理
  def callback_from(provider)
    provider = provider.to_s
    @user = User.find_for_oauth(request.env['omniauth.auth'])

    # persisted?でDBに保存済みかどうか判断
    if @user.persisted?
      # サインインする記載をします
      sign_in_and_redirect @user, event: :authentication
    else
      session["devise.#{provider}_data"] = request.env['omniauth.auth']
      redirect_to new_user_registration_path
    end
  end

find_for_oauthメソッド

モデルで定義するメソッドになります。後で書きますが、ここでは値としてuser情報を返します。引数にはrequest.env['omniauth.auth']を指定しています。これの中身をterminalで確認すると以下のようになっています。中身を見ると分かるようにTwitter情報がまるごと入っています。これら情報をメソッドの引数として渡します。

f:id:shun_0211:20200610094604p:plain

session~~

session~~の記述は何のために記載しているのか分かっていませんが、おそらくDBに保存できなかったとき、新規登録するための入力をする負担を減らすために記載しているのだと思います。DBに保存できないことはこのアプリでは起こり得ないので、今回は割愛します。


⑧ モデルにデータベース登録するためのメソッドをかく

コントローラーで使用しているメソッドをモデルに書いていきます。また、deviseのオプションの追記を行います。
user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :omniauthable, omniauth_providers: [:twitter]    # :omniauthable, omniauth_providers: [:twitter]を追記
  # self.~~:クラスメソッド(クラス内のどこでも使える変数)
  def self.find_for_oauth(user_data)
    # usersテーブルの中から指定したuidとproviderの組み合わせを持つユーザーを探す
    user = User.find_by(uid: user_data.uid, provider: user_data.provider)
    
    # ||= → 変数の中身がnilの場合、右辺のメソッドを実行する
    user ||= User.create!(
      uid: user_data.uid,
      provider: user_data.provider,
      nickname: user_data[:info][:nickname],
      # 下で定義するメソッドを使い、一意のメールアドレスを作成
      email: User.dummy_email(user_data),
      # 半角英数字8文字のランダム文字列を作成
      password: [*'a'..'z', *'0'..'9'].sample(8).join
    )
    # 返り値としてuserを返す
    return user
  end
  
  def self.dummy_email(user_data)
    "#{Time.now.strftime('%Y%m%d%H%M%S').to_i}-#{user_data.uid}-#{user_data.provider}@example.com"
  end
  
end

これでローカル環境でのtwitter認証はできるはずです。

まとめ

以上になります。rails のGemはほんとに便利で何でもやってくれますね。
ここまで読んでくださり、ありがとうございました。分かりにくいやアドバイス等ありましたらコメントくださると幸いです。では!