over 3 years ago

系統:

Mac OS X Yosemite 10.10.1
Ruby 2.1.5
Rails 4.1.8

相關連結

Devise ( https://github.com/plataformatec/devise )
omniauth-github ( https://github.com/intridea/omniauth-github )
omniauth-facebook ( https://github.com/mkdynamic/omniauth-facebook )
omniauth-linkedin ( https://github.com/skorks/omniauth-linkedin )
omniauth-twitter ( https://github.com/arunagw/omniauth-twitter )
omniauth-google-oauth2 ( https://github.com/zquestz/omniauth-google-oauth2 )
figaro ( https://github.com/laserlemon/figaro )

設定步驟如下

1. Gemfile 設定

添加必要的 gem 套件至 Gemfile 檔中

# Use Omniauth
gem 'omniauth'
gem 'omniauth-github', github: 'intridea/omniauth-github'
gem 'omniauth-facebook'
gem 'omniauth-linkedin'
gem 'omniauth-twitter'

# Use Figaro manage auth_key & auth_secret
gem 'figaro'

2. 設定 APP_ID & APP_SECRET

config/initializers/devise.rb

# ==> OmniAuth

...
config.omniauth :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_APP_SECRET']
config.omniauth :github, ENV['GITHUB_APP_ID'], ENV['GITHUB_APP_SECRET']
config.omniauth :linkedin, ENV['LINKEDIN_APP_ID'], ENV['LINKEDIN_APP_SECRET']
config.omniauth :twitter, ENV['TWITTER_APP_ID'], ENV['TWITTER_APP_SECRET']

3. 申請 APP_ID & APP_SECRET

Fackbook ( https://developers.facebook.com/ )
GitHub ( https://github.com/settings/applications )
Linkedin ( http://developer.linkedin.com/ )
Twitter ( https://apps.twitter.com/ )

Homepage URL
http://<YOUR_SITE_URL>
Authorization callback URL
http://<YOUR_SITE_URL>/user/auth/<Provider>/callback

4. 使用 figaro 管理 APP_ID & APP_SECRET

$ figaro install

config/application.yml

FACEBOOK_APP_ID: 'insert_your_app_id_here'
FACEBOOK_APP_SECRET: 'insert_your_app_secret_here'

development:
  GITHUB_APP_ID: 'insert_your_app_id_here'
  GITHUB_APP_SECRET: 'insert_your_app_secret_here'
  LINKEDIN_APP_ID: 'insert_your_app_id_here'
  LINKEDIN_APP_SECRET: 'insert_your_app_secret_here'
  TWITTER_APP_ID: 'insert_your_app_id_here'
  TWITTER_APP_SECRET: 'insert_your_app_secret_here'

production:
  GITHUB_APP_ID: 'insert_your_app_id_here'
  GITHUB_APP_SECRET: 'insert_your_app_secret_here'
  LINKEDIN_APP_ID: 'insert_your_app_id_here'
  LINKEDIN_APP_SECRET: 'insert_your_app_secret_here'
  TWITTER_APP_ID: 'insert_your_app_id_here'
  TWITTER_APP_SECRET: 'insert_your_app_secret_here'

figaro 在安裝時,會在 .gitignore 中新增

# Ignore application configuration
/config/application.yml

所以,在開發的時候,可以執行以下指令製作 figaro configuration 暫存檔,方便其他開發者知道

$ cp config/application.yml{,.example}

Heroku 設定 ENV 方式如下:

$ figaro heroku:set -e production

5. Model 設定

新增 OmniAuth Model

$ rails g model Omniauth user:belongs_to provider:string uid:string image:string url:string --no-timestamps
$ rake db:migrate

新增 OmniauthCallbacks Concern app/models/concerns/omniauth_callbacks.rb

module OmniauthCallbacks
  extend ActiveSupport::Concern

  def self.included(base)
    base.class_eval do
      has_many :omniauths, dependent: :destroy

      # 1. check omniauth is existed or not.

      # 2. check omniauth's email is registered or not.

      # 3. new a user by omniauth data.

      # 4. bind omniauth service

      def self.find_or_create_by_omniauth(auth)
        unless user = User.find_by_omniauth(auth)
          unless user = User.find_by_email(auth.info.email)
            user = User.create_by_omniauth(auth)
          end
          user.bind_omniauth_service(auth)
        end
        user
      end

      def self.find_by_omniauth(auth)
        includes(:omniauths).where(omniauths: {provider: auth.provider, uid: auth.uid}).first
      end

      def self.create_by_omniauth(auth)
        User.create do |user|
          user.email        = auth.info.email
          user.password     = Devise.friendly_token[0, 20]
          user.confirmed_at = Time.now
        end
      end

      def bind_omniauth_service(auth)
        omniauths.create do |omniauth|
          omniauth.user_id  = id
          omniauth.provider = auth.provider
          omniauth.uid      = auth.uid
          omniauth.image    = auth.info.image
          omniauth.url      = auth.info.urls.Facebook if auth.provider == 'facebook'
          omniauth.url      = auth.info.urls.GitHub if auth.provider == 'github'
          omniauth.url      = auth.info.urls.public_profile if auth.provider == 'linkedin'
        end
      end
    end
  end
end

app/models/user.rb

  ...
  include OmniauthCallbacks
  ...
  devise ...
         :omniauthable, omniauth_providers: [:facebook, :github, :linkedin, :twitter]
  ...

app/models/omniauth.rb

class Omniauth < ActiveRecord::Base
  belongs_to :user
end

6. Controller 設定

新增檔案 app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def auth
    @user = User.find_or_create_by_omniauth(env['omniauth.auth'])
    if @user.persisted?
      sign_in_and_redirect(@user, event: :authentication)
    else
      session['devise.omniauth_data'] = env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end

  User.omniauth_providers.each do |provider|
    alias_method provider, :auth
  end
end

7. route 設定

config/routes.rb

devise_for :user, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }

8. view 設定

<%= link_to "Sign in with Facebook", omniauth_authorize_path(:user, :facebook) %>
<%= link_to "Sign in with GitHub", omniauth_authorize_path(:user, :github) %>
<%= link_to "Sign in with Linkedin", omniauth_authorize_path(:user, :linkedin) %>
<%= link_to "Sign in with Twitter", omniauth_authorize_path(:user, :twitter) %>

9. 移除 facebook 登入後網址的 #_=_

新增 coffee codes

$ ->
  # remove the url word '#_=_' after OmniAuth Facebook login. 
  if window.location.hash and window.location.hash == '#_=_'
    window.location.hash = ''
    history.pushState('', document.title, window.location.pathname)

或 javascript code

(function() {
  $(function() {
    if (window.location.hash && window.location.hash === '#_=_') {
      window.location.hash = '';
      return history.pushState('', document.title, window.location.pathname);
    }
  });
}).call(this);
← Devise - part 1 - 基本安裝 Devise - part 3 - Add User Profile with avatar →
 
comments powered by Disqus