Nic Lin's Blog

喜歡在地上滾的工程師

Rails 5 + OmniAuth + Devise 實作可擴充的第三方網站登入(Facebook, Google)

Rails 5 實作第三方登入 (Facebook + google)

雖然我去年寫過一篇如何在 Rails 4 Devise使用Google實作登入

但後來要用的時候,發現原本的文章只能對應一種第三方登入,如果要實作多個第三方登入在同一個網站的時候,可擴充性就顯得相當重要了。

再加上這種套件或是API接口的升級變化越來越快,還是寫一篇新的吧(應該不會每年都寫一篇啦XD)

Gem install

gem `devise`
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
gem 'koala' # 可以將錯誤的 session 刪掉避免註冊失敗

因為 devise 有支援第三方登入,所以實作起來並不特別難。

申請官方接口 Facebook

先去 Facebook 開發者官方註冊,如果已有帳號直接登入應該就行了。

然後右上角,下拉選單選擇「新增應用程式」

螢幕快照 2017-08-26 下午10.17.06.png

輸入你的 app name

螢幕快照 2017-08-26 下午10.18.29.png

接著會跳轉到這個 app 下的後台設定,點擊左邊 Side bar 去建立新平台

螢幕快照 2017-08-26 下午10.21.34.png

新建網站平台

螢幕快照 2017-08-26 下午10.23.37.png

輸入網址,這邊注意一下,如果你想要先在 localhost 測試,可以先填入 http://localhost:3000 ,等到上去正式站或測試站在調整正確網址就行了,因為這個欄位是 Facebook 要確定打回去的 redirect url 是否跟設定一樣

螢幕快照 2017-08-26 下午10.24.14.png

最後 Deploy 上去要給別人測試的時候,記得到這裡把應用程式設定公開,否則只有開發者帳號能夠用 Facebook 的第三方登入哦

螢幕快照 2017-08-26 下午10.26.14.png

申請官方接口 Google

Google 開發者官方申請 API

點下拉選單來新增一個應用程式的專案

螢幕快照 2017-08-26 下午10.29.38.png

點擊「+」符號

螢幕快照 2017-08-26 下午10.30.21.png

接著會讓你輸入這個專案的名稱,輸入完之後會跳回原本的畫面,在選一次下拉選單,應該就會看到他正在建立了,等待建立好(約十秒)就可以點進去

然後啟用 Web Fonts Developer API and Google+ API

點擊新增 API 就會出現搜索欄可以找了,

找到之後點選啟用,接著會叫你建立憑證,憑證建立後就會拿到 api key 和密鑰

Controller 設定

新建 Users::OmniauthCallbacksController

指令 rails g controller users::omniauth_callbacks

代碼如下

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def facebook
    @user = User.from_omniauth(request.env["omniauth.auth"], current_user)
    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    else

      if @user.email.nil?
        @user.delete_access_token(request.env["omniauth.auth"])
        redirect_to new_user_registration_url, alert: "需要您同意 Email 授權唷!"
      else
        session["devise.facebook_data"] = request.env["omniauth.auth"]
        redirect_to new_user_registration_url
      end
    end
  end

  def google_oauth2
    @user = User.from_omniauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
      sign_in_and_redirect @user, :event => :authentication
    else
      session["devise.google_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to new_user_session_path, alert: "無法獲得驗證!"
  end
end

建立一個紀錄第三方資料的 model

rails g model identity user:references provider:string uid:string

rake db:migrate

初始化 model

class Identity < ApplicationRecord
  belongs_to :user

  validates_presence_of :uid, :provider
  validates_uniqueness_of :uid, :scope => :provider

  def self.find_for_oauth(auth)
    find_or_create_by(uid: auth.uid, provider: auth.provider)
  end
end

驗證如果有相同 email 帳戶直接登入,沒有的話新建一個並登入,如果你有用驗證信請記得要 skip confirm

devise: :omniauthable, :omniauth_providers => [:facebook, :google_oauth2]

  def self.from_omniauth(auth, signed_in_resource = nil)
    identity = Identity.find_for_oauth(auth)

    user = signed_in_resource ? signed_in_resource : identity.user

    if user.nil?
      email = auth.info.email

      user = User.where(email: email).first if email

      if user.nil?
        user = User.new(name: auth.info.name.gsub(/\s+/, '_'),
                        email: auth.info.email,
                        avatar: auth.info.image,
                        password: Devise.friendly_token[0,20])
        user.save!
      end
    end

    if identity.user != user
      identity.user = user
      identity.save!
    end

    user
  end

  def delete_access_token(auth)
    @graph ||= Koala::Facebook::API.new(auth.credentials.token)
    @graph.delete_connections(auth.uid, "permissions")
  end

devise 的 routes 用我們剛建立的 controller 覆蓋

  devise_for :users, :controllers => { omniauth_callbacks: "users/omniauth_callbacks" }

將你拿到的第三方金鑰與密碼填在這裡,如果不想把密鑰寫在 commit 裡面,可以使用 dotenv 這個 gem 來管理變數

Devise.setup do |config|
...
  config.omniauth :facebook, "KEY", "SECRET"
  config.omniauth :google, "KEY", "SECRET"
...
end

到這邊其實就好了,你會疑問,那 link button 呢?,可以到 devise 註冊頁面看一下,應該會有連結了,因為他在原先預設的頁面裡面就有提到這第三方登入,請參考

<%- if devise_mapping.omniauthable? %>
  <%- resource_class.omniauth_providers.each do |provider| %>
    <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %><br />
  <% end -%>
<% end -%>

如果你要任意放你的 link buuton 就用

Google user_google_oauth2_omniauth_authorize_path Facebook user_facebook_omniauth_authorize_path

如果要擴充其他第三方,就依照此模版繼續新增就可以了

參考來源:

comments powered by Disqus