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 開發者官方註冊,如果已有帳號直接登入應該就行了。
然後右上角,下拉選單選擇「新增應用程式」
輸入你的 app name
接著會跳轉到這個 app 下的後台設定,點擊左邊 Side bar 去建立新平台
新建網站平台
輸入網址,這邊注意一下,如果你想要先在 localhost 測試,可以先填入 http://localhost:3000
,等到上去正式站或測試站在調整正確網址就行了,因為這個欄位是 Facebook 要確定打回去的 redirect url 是否跟設定一樣
最後 Deploy 上去要給別人測試的時候,記得到這裡把應用程式設定公開,否則只有開發者帳號能夠用 Facebook 的第三方登入哦
申請官方接口 Google
點下拉選單來新增一個應用程式的專案
點擊「+」符號
接著會讓你輸入這個專案的名稱,輸入完之後會跳回原本的畫面,在選一次下拉選單,應該就會看到他正在建立了,等待建立好(約十秒)就可以點進去
然後啟用 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
如果要擴充其他第三方,就依照此模版繼續新增就可以了
參考來源: