2016-05-07 10 views
1

OmniAuthを使用して、facebook、twitter、googleからユーザーのaccess_tokenとsecretを同時に取得したいと考えています。Ruby on Railsで認証されていない別のApisの認証にOmniAuthを使用する方法

私は認証にDeviseを使用しています。ユーザがサインインしたときにキーを要求し、後で使用するためにデータベースに格納する方法を知りたいと思います。

gem 'devise' 
gem 'omniauth' 
gem 'omniauth-twitter' 
gem 'omniauth-facebook' 
gem 'omniauth-linkedin' 

移行とモデル

rails generate devise:install 
rails generate devise user 
rails g migration add_name_to_users name:string 
rails g model identity user:references provider:string uid:string 

アプリ/モデル/ identity.rbを生成GEMファイルに

答えて

4

追加

class Identity < ActiveRecord::Base 
    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 

アプリ/設定/初期化子/ devise.rb

複数とのアカウントをリンクする10

# General Settings 
    config.app_domain = 'somedomain.com' 

    # Email 
    config.action_mailer.delivery_method = :smtp 
    config.action_mailer.perform_deliveries = true 
    config.action_mailer.default_url_options = { host: config.app_domain } 
    config.action_mailer.smtp_settings = { 
    address: 'smtp.gmail.com', 
    port: '587', 
    enable_starttls_auto: true, 
    user_name: 'someuser', 
    password: 'somepass', 
    authentication: :plain, 
    domain: 'somedomain.com' 
    } 

のconfig/routes.rbを

devise_for :users, :controllers => { omniauth_callbacks: 'omniauth_callbacks' } 

アプリ/コントローラ/ omniauth_callbacks_controller.rbしたがって

.RB

設定/環境/ [環境]を、プロバイダは、OAuthコールバックが返されたときにcurrent_userセッションがすでに設定されていて、User.find_for_oauthに渡されている必要があります。ユーザーがすでに

class OmniauthCallbacksController < Devise::OmniauthCallbacksController 
    def self.provides_callback_for(provider) 
    class_eval %Q{ 
     def #{provider} 
     @user = User.find_for_oauth(env["omniauth.auth"], current_user) 

     if @user.persisted? 
      sign_in_and_redirect @user, event: :authentication 
      set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format? 
     else 
      session["devise.#{provider}_data"] = env["omniauth.auth"] 
      redirect_to new_user_registration_url 
     end 
     end 
    } 
    end 

    [:twitter, :facebook, :linked_in].each do |provider| 
    provides_callback_for provider 
    end 

    def after_sign_in_path_for(resource) 
    if resource.email_verified? 
     super resource 
    else 
     finish_signup_path(resource) 
    end 
    end 
end 

アプリ/モデル/ user.rb

にログインしている間:これは複雑に聞こえるかもしれませんが、別のプロバイダをリンクするのに必要なすべてのthatsは、Facebookの例えば、user_omniauth_authorize_path redirect_toすることである(フェイスブック)を
class User < ActiveRecord::Base 
    TEMP_EMAIL_PREFIX = '[email protected]' 
    TEMP_EMAIL_REGEX = /\[email protected]/ 

    # Include default devise modules. Others available are: 
    # :lockable, :timeoutable 
    devise :database_authenticatable, :registerable, :confirmable, 
    :recoverable, :rememberable, :trackable, :validatable, :omniauthable 

    validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update 

    def self.find_for_oauth(auth, signed_in_resource = nil) 

    # Get the identity and user if they exist 
    identity = Identity.find_for_oauth(auth) 

    # If a signed_in_resource is provided it always overrides the existing user 
    # to prevent the identity being locked with accidentally created accounts. 
    # Note that this may leave zombie accounts (with no associated identity) which 
    # can be cleaned up at a later date. 
    user = signed_in_resource ? signed_in_resource : identity.user 

    # Create the user if needed 
    if user.nil? 

     # Get the existing user by email if the provider gives us a verified email. 
     # If no verified email was provided we assign a temporary email and ask the 
     # user to verify it on the next step via UsersController.finish_signup 
     email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email) 
     email = auth.info.email if email_is_verified 
     user = User.where(:email => email).first if email 

     # Create the user if it's a new registration 
     if user.nil? 
     user = User.new(
      name: auth.extra.raw_info.name, 
      #username: auth.info.nickname || auth.uid, 
      email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com", 
      password: Devise.friendly_token[0,20] 
     ) 
     user.skip_confirmation! 
     user.save! 
     end 
    end 

    # Associate the identity with the user if needed 
    if identity.user != user 
     identity.user = user 
     identity.save! 
    end 
    user 
    end 

    def email_verified? 
    self.email && self.email !~ TEMP_EMAIL_REGEX 
    end 
end 

config/routes.rb 
match '/users/:id/finish_signup' => 'users#finish_signup', via: [:get, :patch], :as => :finish_signup 

アプリ/コントローラ/ users_controller.rb

class UsersController < ApplicationController 
    before_action :set_user, only: [:show, :edit, :update, :destroy] 

    ... 

    # GET /users/:id.:format 
    def show 
    # authorize! :read, @user 
    end 

    # GET /users/:id/edit 
    def edit 
    # authorize! :update, @user 
    end 

    # PATCH/PUT /users/:id.:format 
    def update 
    # authorize! :update, @user 
    respond_to do |format| 
     if @user.update(user_params) 
     sign_in(@user == current_user ? @user : current_user, :bypass => true) 
     format.html { redirect_to @user, notice: 'Your profile was successfully updated.' } 
     format.json { head :no_content } 
     else 
     format.html { render action: 'edit' } 
     format.json { render json: @user.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # GET/PATCH /users/:id/finish_signup 
    def finish_signup 
    # authorize! :update, @user 
    if request.patch? && params[:user] #&& params[:user][:email] 
     if @user.update(user_params) 
     @user.skip_reconfirmation! 
     sign_in(@user, :bypass => true) 
     redirect_to @user, notice: 'Your profile was successfully updated.' 
     else 
     @show_errors = true 
     end 
    end 
    end 

    # DELETE /users/:id.:format 
    def destroy 
    # authorize! :delete, @user 
    @user.destroy 
    respond_to do |format| 
     format.html { redirect_to root_url } 
     format.json { head :no_content } 
    end 
    end 

    private 
    def set_user 
     @user = User.find(params[:id]) 
    end 

    def user_params 
     accessible = [ :name, :email ] # extend with your own params 
     accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank? 
     params.require(:user).permit(accessible) 
    end 
end 

アプリ/ビュー/ユーザー/ finish_signup.html.erb

<div id="add-email" class="container"> 
    <h1>Add Email</h1> 
    <%= form_for(current_user, :as => 'user', :url => finish_signup_path(current_user), :html => { role: 'form'}) do |f| %> 
    <% if @show_errors && current_user.errors.any? %> 
     <div id="error_explanation"> 
     <% current_user.errors.full_messages.each do |msg| %> 
      <%= msg %><br> 
     <% end %> 
     </div> 
    <% end %> 
    <div class="form-group"> 
     <%= f.label :email %> 
     <div class="controls"> 
     <%= f.text_field :email, :autofocus => true, :value => '', class: 'form-control input-lg', placeholder: 'Example: [email protected]' %> 
     <p class="help-block">Please confirm your email address. No spam.</p> 
     </div> 
    </div> 
    <div class="actions"> 
     <%= f.submit 'Continue', :class => 'btn btn-primary' %> 
    </div> 
    <% end %> 
</div> 

アプリ/コントローラ/ application_controller.rb

次のメソッドはオプションですが、あなたは、ユーザーが特定のリソースにアクセスする前に、すべての必要な情報を提供していることを確認したい場合には便利です。

あなたはとてもようにbefore_filterでそれを使用することができますbefore_filter:ensure_signup_complete、のみ:[:、新しい:、更新:作成、破棄]

class ApplicationController < ActionController::Base 

    ... 

    def ensure_signup_complete 
    # Ensure we don't go into an infinite loop 
    return if action_name == 'finish_signup' 

    # Redirect to the 'finish_signup' page if the user 
    # email hasn't been verified yet 
    if current_user && !current_user.email_verified? 
     redirect_to finish_signup_path(current_user) 
    end 
    end 
end 
関連する問題