Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

RailsでFacebookとGoogleのOAuth連携。SNS認証の方法

More than 1 year has passed since last update.

はじめに

Railsアプリの実装でFacebookとGoogleでのSNS認証を実装したのでその方法を備忘録としてここに記します:head_bandage::head_bandage:
最近ではSNS認証ができないアプリケーションのが珍しい?くらいになっています。Railsでは"devise"と"omniauth"で簡単に実装できるので是非実装してみましょう。
今回はFacebookとGoogleだけ実装しましたが、GithubやTwiiterなどたくさんのSNSも同様のやり方で実装できます!!

Gemのインストール

みなさんご存知のユーザー登録gem"devise"とOAuth関連のgemを入れていきます。'dotenv-rails'は環境変数を管理するgemなのでついでに入れておきましょう。

Gemfile
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は自動で生成されます。
スクリーンショット 2019-09-28 14.38.00.png
アプリ⇨設定⇨ベーシック

③リダイレクトURIの設定をします。
スクリーンショット 2019-09-28 14.45.34.png
Facebookログイン⇨設定⇨有効なOAuthリダイレクトURI

ここに

https://localhost:3000/users/auth/facebook/callback

と入力して、保存しておきましょう。

Google

https://console.cloud.google.com
①GCPのプロジェクトを作成しAPI認証の設定からOauthクライアントIDを作成します。
スクリーンショット 2019-10-18 0.11.56.png

②KeyとSecretは自動で生成されます。
スクリーンショット 2019-10-18 0.13.36.png

③リダイレクトURIの設定をします。
スクリーンショット 2019-09-28 15.00.01.png

ここに

http://localhost:3000/users/auth/google_oauth2/callback

と入力して保存しておきましょう。

ローカル環境で試すにはこの手順で大丈夫です。

IDとSECRETの設定

一番上の階層(ルートディレクトリ)に.envというファイルを作成し、そこに先ほど手に入れたIDとSECRETを記載します。GoogleのIDは"~.com"まで入れてください。

.env
FACEBOOK_CLIENT_ID='************'
FACEBOOK_CLIENT_SECRET='******************'
GOOGLE_CLIENT_ID='*************************************************'
GOOGLE_CLIENT_SECRET='*****************'

ここで初歩的な疑問。なぜ直接devise.rbに書き込まないのか。
理由は簡単で、GithubをPublicにしているからです。
APIのIDとSECRETはとてもセンシティブな情報で悪い人に知られると悪用され、多額の請求をされる可能性があります。
(ちなみに私は一度、IDとSECRETをそのままあげてしまい、Githubパトロールに叱られました。Githubありがとう:raised_hand::raised_hand:
アプリケーションをGithubと連携させている場合はこの方法をお勧めします。
しかしこのままではGithubに上がってしまうので上げないようにしましょう。

.gitignore
.env

これで大丈夫です。

IDとSECRETの読み込み

先ほど設定したIDとシークレットを読み込みます。
ここで気をつけるのはgoogleの末尾に"skip_jwt: true"をつけること。
理由はOSのクロックのズレを修正する役割を持っています。この記述をしないと後にJWT::InvalidIatErrorがででしまいます。(私はここで1時間あたふたしました:sweat::sweat:

devise.rb
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の記述を変更しましょう。

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

次にマイグレーションファイルの編集をします。

********_create_sns_credentials.rb
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

アソシエーションも設定しておきましょう。

sns_credential.rb
class SnsCredential < ApplicationRecord
  belongs_to :user
end

コールバック関数

まずはdeviseのルーティングを変更します。

routes.rb
Rails.application.routes.draw do
  devise_for :users,
  controllers: {
    sessions: 'users/sessions',
    registrations: "users/registrations",
    omniauth_callbacks: 'users/omniauth_callbacks'
  }
end

これでコールバック関数が呼び出せるようになりました。

omniauth_callbacks_controller.rb
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メソッドはモデルで定義していきましょう。

user.rb
 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は必須です。
そこでヘルパーメソッドを使い、パスワードを自動生成することにしました。

registrations_controller.rb
 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認証はユーザビリティーの向上には大変役立ちます。
ボタン一つで登録できてしまうのは控えめに言って神です:relaxed::relaxed:
どんどん実装していきましょう。
記事を書くモチベーションになるので、もし参考になったらいいねお願いします:thumbsup_tone1::thumbsup_tone1:

nakanishi03
早稲田大学文化構想学部→JT(法人営業)→ITベンチャー(エンジニア)
https://qiita.com/settings/profile
anconsulting
フリーランスITエンジニア向け案件・求人サイト「フリエン」を運営するスタートアップ
https://furien.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away