8

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

 2 years ago
source link: https://blog.niclin.tw/2017/08/26/rails-5---omniauth---devise-%E5%AF%A6%E4%BD%9C%E5%8F%AF%E6%93%B4%E5%85%85%E7%9A%84%E7%AC%AC%E4%B8%89%E6%96%B9%E7%B6%B2%E7%AB%99%E7%99%BB%E5%85%A5facebook-google/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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

參考來源:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK