【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を記載します。
これで、twitter認証を行う準備はできました。
もし、403 Forbiddenというエラーが出るようならcallback URLが間違っている可能性が高いので確認しましょう。
※ 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情報がまるごと入っています。これら情報をメソッドの引数として渡します。
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認証はできるはずです。