#はじめに
devise と omniauth を利用したSNSアカウントでのログイン認証を実装してみました。
プログラム自体、書き始めてまだ2か月程なので、間違ってる部分とかかなりある気がしますが、
あくまでも自分用メモとして残しておこうと思います!
もしも参考にしていただける場合は、この記事を過信しすぎないようお願いします…(´・ω・`)
また間違いなどがありましたら、コメントいただけますと幸いですm(_ _)m
#動作環境
- AWS Cloud9
- ruby on rails -v 5.2.2
- devise
- omniauth-facebook (6.0.0)
- omniauth-google-oauth2 (0.8.0)
- omniauth-twitter (1.4.0)
#Gemfileにgemを追加して保存
gem 'devise'
gem 'omniauth-twitter'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
#gemのインストール
$ bundle install
#Deviseのインストール
$ rails g devise:install
#DeviseでUserモデルの作成
$ rails g devise User
作成されたモデル ↓
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
gem omniauth を利用するために、devise:内に:omniauthable
を追加する。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable
end
今回は、通常アカウント作成時にはユーザー名の登録を必須としたいのと、
SNSアカウントを利用したログインの場合はSNSアカウントで利用しているユーザー名をそのまま利用したいので、
usersテーブルにnameカラムを追加します。
また、Facebook/Twitter/Google
アカウントでログインする場合に必要になる下記3つのカラムも同時に追加します。
追加するカラム
name
/provide
/uid
/image
$ rails g migration add_columns_to_users provider uid name image
name については名無しは不可としたいので、マイグレーションファイルを開いて、
nameカラムにnull: false
を指定し、マイグレートします。
class AddColumnsToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :name, :string, null: false
add_column :users, :provider, :string
add_column :users, :uid, :string
add_column :users, :image, :string
end
end
$ rails db:migrate
#Twitter/Google/Facebook Developerアカウントの取得
こちらについては、まとめるとえらい時間かかりそうなので省略します・・・。
今後時間があるときにまとめようと思いますので、その時に追記します!
現時点でわからない方は、ググって頑張りましょう!
(完全初心者の僕でもどうにかなったので、たぶん大丈夫!)
#OAuthの設定
###omniauthを利用するための初期設定
devise.rb
の250行目付近にある omniauth の設定箇所にて、
facebook/twitter/googleのプロバイダーを設定します。
環境変数の設定および、各種プロバイダーのApp_ID/Keyの取得については、
省略しますので、頑張ってググってください!
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
config.omniauth :facebook, ENV['FB_APP_ID'], ENV['FB_APP_SECRET'] #追加
config.omniauth :twitter, ENV['TW_APP_ID'], ENV['TW_APP_SECRET'] #追加
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'] #追加
今回はgem 'dotenv'
で環境変数を管理するので、Railsアプリのルートディレクトリに.env
ファイルを作成し、
中身に以下の通り記述します。
FB_APP_ID= Facebook Developersで取得したアプリIDを記載
FB_APP_SECRET= Facebook Developersで取得したシークレットキーを記載
TW_APP_ID= Twitter Developersで取得したアプリIDを記載
TW_APP_SECRET= Twitter Developersで取得したシークレットキーを記載
GOOGLE_CLIENT_ID= Google APIsで取得したクライアントIDを記載
GOOGLE_CLIENT_SECRET= Google APIsで取得したシークレットキーを記載
###Userモデルにメソッドを追加
app/models/user.rb
に以下のメソッドを追加します。
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.email = User.dummy_email(auth)
user.password = Devise.friendly_token[0, 20]
user.image = auth.info.image.gsub("_normal","") if user.provider == "twitter"
user.image = auth.info.image.gsub("picture","picture?type=large") if user.provider == "facebook"
user.image = auth.info.image if user.provider == "google_oauth2"
end
end
private
def self.dummy_email(auth)
"#{auth.uid}-#{auth.provider}@example.com"
end
end
####where(provider: auth.provider, uid: auth.uid).first_or_create
各種SNSアカウントでログインを試みたユーザーのporovider
/uid
が存在するかを確認。
- 存在していた場合:見つかったユーザーでそのままログインする
- 存在していなかった場合:アカウントが自動生成され、そのままログインする
####user.provider = auth.provider
ログインに使用したプロバイダー名をusersテーブルのproviderカラムに追加
プロバイダー名は、twitter/facebook/googleの内、
config/initialize/devise.rb
に追加したconfig.omniauth
で設定したプロバイダ名が入る
####user.uid = auth.uid
ログインユーザー識別用IDをusersテーブルのuidカラムに追加
####user.name = auth.info.name
ログインに使用したアカウントのユーザー名を取得し、usersテーブルのnameカラムに追加
例)Twitterアカウントのユーザー名がまひろ
だとすると、そのまままひろ
がnameカラムに追加される
####user.email = User.dummy_email(auth)
バリデーションでメールアドレスの登録は必須としているので、
providerとuidをミックスしてダミーメールアドレスを作成している
ダミーメールアドレスを作成しているself.dummy_email(auth)
メソッドはprivate部分に記述してます。
※各種デベロッパー側の設定によって、SNSアカウントのメールアドレスを取得できるようだけど、
設定がややこしくてよくわからなかったので、とりあえず今回はダミーメールアドレス作成で乗り切ります
private
def self.dummy_email(auth)
"#{auth.uid}-#{auth.provider}@example.com"
end
####user.password = Devise.friendly_token[0, 20]
ログイン時にパスワード入力などは行わないが、パスワードは必須の為、
バリデーションに引っかかってエラーにならないようランダムパスワードを生成しています
####user.image = auth.info.image.gsub("_normal","") if user.provider == "twitter"
user.provider
がtwitter
の時、auth.info.image.gsub("_normal","")
が実行されます。
auth.info.image
でTwitterのプロフィール画像のURLを取得しています。
※imageカラムに追加されるのは、画像ではなく、取得したプロフィール画像のURLとなります。
.gsub("_normal","")
は、取得したプロフィール画像のURLから"_normal"
という文字列を""(空文字)
に置き換えています。
これを行う事で、取得したプロフィール画像が荒くなるのを防いでいます。
(※わかりやすくいうと、"_normal"を空文字に置き換える事で、実質的には指定の文字列を削除しています)
####user.image = auth.info.image.gsub("picture","picture?type=large") if user.provider == "facebook"
user.provider
がfacebook
の時、auth.info.image.gsub("picture","picture?type=large")
が実行されます。
auth.info.image
でFacebookのプロフィール画像のURLを取得しています。
※imageカラムに追加されるのは、画像ではなく、取得したプロフィール画像のURLとなります。
.gsub("picture","picture?type=large")
は、取得したプロフィール画像のURLから
"picture"という文字列を"picture?type=large"に置き換えています。
これを行う事で、大きめのプロフィール画像が取得されます。
(後でプロフィール画像のサイズを調整する際に荒くなりにくい)
####user.image = auth.info.image if user.provider == "google_oauth2"
user.provider
がgoogle_oauth2
の時、auth.info.image
が実行されます。
auth.info.image
でGoogleのプロフィール画像のURLを取得しています。
※imageカラムに追加されるのは、画像ではなく、取得したプロフィール画像のURLとなります。
画像サイズについては、デフォルトでちょうどよいサイズなので特に指定はしていません。
サイズを変えたい場合は、取得したURLの末尾に=wサイズの値(例:=w500)
を追加する処理を書く事で、
プロフィール画像のサイズを任意に変更できます。
#omniauth_callback_controllerの作成
omniauthを使ってログインする際のコールバックコントローラを作成します。
初期状態で、devise/omniauth_callbacks#Action
が設定されていますが、
controllerフォルダにdeviseというフォルダがなく、実体化されていない?ため、必須の作業となるようです。
(※軽くググった限りでは、deviseフォルダのomniauth_callbacksを編集する方法については出てこなかったです…)
$ rails g devise:controllers users
omniauth_callbacks_controller.rb # 使うのはこれだけ
confirmations_controller.rb
passwords_controller.rb
registrations_controller.rb
sessions_controller.rb
unlocks_controller.rb
コントローラを変更したいのは、omniauth_callbacks_controller.rb
のみで、
他はdeviseの標準のコントローラを利用するため、他のコントローラファイルは削除します。
routes.rb
にルーティングを設定します。
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root to: "toppages#index"
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
end
生成されたomniauth_callbacks_controller.rb
を開いて、以下の内容を記述します。
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"].except("extra")
redirect_to new_user_registration_url
end
end
def twitter
# 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: "Twitter") if is_navigational_format?
else
session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")
redirect_to new_user_registration_url
end
end
def google_oauth2
# 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: "Google") if is_navigational_format?
else
session["devise.google_data"] = request.env["omniauth.auth"].except("extra")
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
メソッド名は必ず、config.omniauth
で設定したプロバイダー名を利用しましょう。
勝手なメソッド名をつけるとエラーになります。
これで、URLからドメイン名/users/sign_up
にアクセスして、
各種プロバイダーでのサインアップリンクをクリックすれば、SNSアカウントでのログインに成功すると思います。
以上で完了です。
細かいデザインなどはまた別の機会にやる事にします。
#終わり
書くのめっちゃ疲れたけど、だいぶ頭の中が整理できた気がする…
間違ってる部分もあると思います…(間違っている箇所に気づける程、まだ慣れていません…
もし気づいた点などありましたら、コメントください・・・