#はじめに
Railsアプリの実装でFacebookとGoogleでのSNS認証を実装したのでその方法を備忘録としてここに記します
最近ではSNS認証ができないアプリケーションのが珍しい?くらいになっています。Railsでは"devise"と"omniauth"で簡単に実装できるので是非実装してみましょう。
今回はFacebookとGoogleだけ実装しましたが、GithubやTwiiterなどたくさんのSNSも同様のやり方で実装できます!!
#Gemのインストール
みなさんご存知のユーザー登録gem"devise"とOAuth関連のgemを入れていきます。'dotenv-rails'は環境変数を管理するgemなのでついでに入れておきましょう。
gem 'devise'
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'dotenv-rails'
rails g devise:install
でデバイス関連のファイルを生成しておきましょう。
#OAuthの申請
FacebookとGoogleのKeyとSecretを取得します。
###Facebook
https://developers.facebook.com
①手順にしたがってアカウントとアプリを作成します。
②アプリを作成すればKeyとSecretは自動で生成されます。
アプリ⇨設定⇨ベーシック
③リダイレクトURIの設定をします。
Facebookログイン⇨設定⇨有効なOAuthリダイレクトURI
ここに
https://localhost:3000/users/auth/facebook/callback
と入力して、保存しておきましょう。
###Google
https://console.cloud.google.com
①GCPのプロジェクトを作成しAPI認証の設定からOauthクライアントIDを作成します。
ここに
http://localhost:3000/users/auth/google_oauth2/callback
と入力して保存しておきましょう。
ローカル環境で試すにはこの手順で大丈夫です。
#IDとSECRETの設定
一番上の階層(ルートディレクトリ)に.envというファイルを作成し、そこに先ほど手に入れたIDとSECRETを記載します。GoogleのIDは"~.com"まで入れてください。
FACEBOOK_CLIENT_ID='************'
FACEBOOK_CLIENT_SECRET='******************'
GOOGLE_CLIENT_ID='*************************************************'
GOOGLE_CLIENT_SECRET='*****************'
ここで初歩的な疑問。なぜ直接devise.rbに書き込まないのか。
理由は簡単で、GithubをPublicにしているからです。
APIのIDとSECRETはとてもセンシティブな情報で悪い人に知られると悪用され、多額の請求をされる可能性があります。
(ちなみに私は一度、IDとSECRETをそのままあげてしまい、Githubパトロールに叱られました。Githubありがとう)
アプリケーションをGithubと連携させている場合はこの方法をお勧めします。
しかしこのままではGithubに上がってしまうので上げないようにしましょう。
.env
これで大丈夫です。
#IDとSECRETの読み込み
先ほど設定したIDとシークレットを読み込みます。
ここで気をつけるのはgoogleの末尾に"skip_jwt: true"をつけること。
理由はOSのクロックのズレを修正する役割を持っています。この記述をしないと後にJWT::InvalidIatErrorがででしまいます。(私はここで1時間あたふたしました)
Devise.setup do |config|
〜省略〜
config.omniauth :facebook, ENV['FACEBOOK_CLIENT_ID'], ENV['FACEBOOK_CLIENT_SECRET']
config.omniauth :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], skip_jwt: true
〜省略〜
end
#モデルの作成
モデルを作成します。
###①Userモデル
rails g devise User
app/models/user.rb
config/routes.rb
db/migrate/20170211042541_devise_create_users.rb
test/fixtures/users.yml
test/models/user_test.rb
ファイルが生成されます。
カラムは各自必要なものを入れてください!!
ここでuser.rbの記述を変更しましょう。
class User < ApplicationRecord
has_many :sns_credentials, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:omniauthable, omniauth_providers: %i[facebook google_oauth2]
end
###②SNS_Credentialモデル
oauthの結果には、providerと各ユーザーを識別するuidが返ってきます。
次に、データにアクセスするためのaccess_tokenも返ってきます。
それを保存するテーブルを作成する必要があります。
rails g model sns_credential
次にマイグレーションファイルの編集をします。
class CreateSnsCredentials < ActiveRecord::Migration[5.2]
def change
create_table :sns_credentials do |t|
t.string :provider
t.string :uid
t.references :user, foreign_key: true
t.timestamps
end
end
end
bundle install
アソシエーションも設定しておきましょう。
class SnsCredential < ApplicationRecord
belongs_to :user
end
#コールバック関数
まずはdeviseのルーティングを変更します。
Rails.application.routes.draw do
devise_for :users,
controllers: {
sessions: 'users/sessions',
registrations: "users/registrations",
omniauth_callbacks: 'users/omniauth_callbacks'
}
end
これでコールバック関数が呼び出せるようになりました。
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
callback_for(:facebook)
end
def google_oauth2
callback_for(:google)
end
def callback_for(provider)
@omniauth = request.env['omniauth.auth']
info = User.find_oauth(@omniauth)
@user = info[:user]
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
@sns = info[:sns]
render template: "devise/registrations/new"
end
end
def failure
redirect_to root_path and return
end
end
ここではSNS認証を利用してユーザー登録していればサインインさせる。
していなければ、サインアップさせる処理をおこなっています。
find_oauthメソッドはモデルで定義していきましょう。
def self.without_sns_data(auth)
user = User.where(email: auth.info.email).first
if user.present?
sns = SnsCredential.create(
uid: auth.uid,
provider: auth.provider,
user_id: user.id
)
else
user = User.new(
nickname: auth.info.name,
email: auth.info.email,
)
sns = SnsCredential.new(
uid: auth.uid,
provider: auth.provider
)
end
return { user: user ,sns: sns}
end
def self.with_sns_data(auth, snscredential)
user = User.where(id: snscredential.user_id).first
unless user.present?
user = User.new(
nickname: auth.info.name,
email: auth.info.email,
)
end
return {user: user}
end
def self.find_oauth(auth)
uid = auth.uid
provider = auth.provider
snscredential = SnsCredential.where(uid: uid, provider: provider).first
if snscredential.present?
user = with_sns_data(auth, snscredential)[:user]
sns = snscredential
else
user = without_sns_data(auth)[:user]
sns = without_sns_data(auth)[:sns]
end
return { user: user ,sns: sns}
end
これでサーバーサイド側の処理は完了です。
#view
最後にviewページを作成すれば完了です。
サインアップページ/サインインページに以下の記述を加えましょう。
= link_to user_facebook_omniauth_authorize_path do
= link_to user_google_oauth2_omniauth_authorize_path do
また今回のアプリケーションではSNS認証をした場合はpassword不要にしました。
しかし、deviseのデフォルト機能ではpasswordは必須です。
そこでヘルパーメソッドを使い、パスワードを自動生成することにしました。
password = Devise.friendly_token.first(7)
if session[:provider].present? && session[:uid].present?
@user = User.create(nickname:session[:nickname], email: session[:email], password: "password", password_confirmation: "password", f_name_kana: session[:f_name_kana],l_name_kana: session[:l_name_kana], f_name_kanji: session[:f_name_kanji], l_name_kanji: session[:l_name_kanji], birthday: session[:birthday], tel: params[:user][:tel])
sns = SnsCredential.create(user_id: @user.id,uid: session[:uid], provider: session[:provider])
else
@user = User.create(nickname:session[:nickname], email: session[:email], password: session[:password], password_confirmation: session[:password_confirmation], f_name_kana: session[:f_name_kana],l_name_kana: session[:l_name_kana], f_name_kanji: session[:f_name_kanji], l_name_kanji: session[:l_name_kanji], birthday: session[:birthday], tel: params[:user][:tel])
end
こんな感じですね。
SNSでサインアップした場合はpassword不要(passwordがなくても入れるため)
emailでサインアップした場合はpasswordが必要です。
#本番環境では?
ちなみに本番環境ではこのままだとSNS連携できません。
理由は2つあります。
①gitignoreによって本番環境でenvファイルが存在しないためIDとSECRETを読み込めない。
②リダイレクトURIをローカルでしか設定していない。
###①gitignoreによって本番環境でenvファイルが存在しないためIDとSECRETを読み込めない。
これの解決は簡単です。
本番環境の環境変数に入力しましょう。
FACEBOOK_CLIENT_ID='************'
FACEBOOK_CLIENT_SECRET='******************'
GOOGLE_CLIENT_ID='*************************************************'
GOOGLE_CLIENT_SECRET='*****************'
これで大丈夫です。
###②リダイレクトURIをローカルでしか設定していない。
これはリダイレクトURIをlocalhost:3000からドメインに変更。
その後、プライバシーポリシーページを追加して & StatusをONにすれば大丈夫です。
ただ、ここで注意しないといけないのはIPアドレスのままでは設定できないということです。
ドメインの取得とSSL化が必要です。
以下の記事を参考にしてください。
参考記事(https://qiita.com/nakanishi03/items/25278fb4dfad60ebfac4)
#終わりに
SNS認証はユーザビリティーの向上には大変役立ちます。
ボタン一つで登録できてしまうのは控えめに言って神です
どんどん実装していきましょう。
記事を書くモチベーションになるので、もし参考になったらいいねお願いします