简体   繁体   中英

Linking existing Devise user (email sign up) with Omniauth-Twitter

I have 2 sign up options for users,

  1. Devise email sign up
  2. Omniauth-Twitter sign up

I added Twitter sign up later. There are some existing users sign up with email, how can I allow these existing users to link their Twitter account so that they can login either via email or twitter?

Thank you!

User Table:

  create_table "designers", force: :cascade do |t|
    t.string "fullname"
    t.string "website"
    t.string "bio"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "avatar"
    t.string "slug"
    t.string "twitter"
    t.string "email", default: "", null: false
    t.string "username"
    t.string "location"
    t.boolean "featured", default: false
    t.string "twitter_username"
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.inet "current_sign_in_ip"
    t.inet "last_sign_in_ip"
    t.boolean "is_admin", default: false
    t.string "provider"
    t.string "uid"
    t.index ["email"], name: "index_designers_on_email"
    t.index ["reset_password_token"], name: "index_designers_on_reset_password_token", unique: true
  end

omniauth_callbacks_controller

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
    def all
        designer = Designer.from_omniauth(request.env['omniauth.auth'])

        if designer.persisted?
            sign_in_and_redirect designer, notice: "Signed in!"
        else
            session["devise.designer_attributes"] = designer.attributes
            redirect_to new_designer_registration_url
        end
    end
    alias_method :twitter, :all
end

registration_controller.rb

class RegistrationsController < Devise::RegistrationsController

  def sign_up_params
    params.require(:designer).permit(:username, :fullname, :email, :password, :password_confirmation)
  end

  def account_update_params
    params.require(:designer).permit(:username, :fullname, :email, :location, :website, :twitter, :bio, :featured, :is_admin, :password, :password_confirmation, :current_password)
  end


  protected

  def after_sign_up_path_for(resource)
    edit_designer_path(current_designer) if current_designer
  end

end

You should tell a bit more about your models and database, but the usual approach is:

You have an Identity model with some information like this:

Table Identities:

id serial NOT NULL
user_id integer, # The user in your Users table
provider text,   # Linkedin, Twitter, Yahoo, any other provider
uid text,        # Other data provided by the Oauth provider....
email text,
name text,
token text,
profile_url text,
image_url text,
secret text,
CONSTRAINT identities_pk PRIMARY KEY (id)

When your user subscribes or logs in using the Twitter button, you search him in the identities table to see if you already have it there (you search by provider and uid).

class OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def twitter
    callback(:twitter)
  end

  def facebook
    callback(:facebook)
  end

  def google
    callback(:google)
  end

  def callback(provider)
    @user = User.find_for_oauth(request.env["omniauth.auth"], current_user)

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

User Model

def self.find_for_oauth(auth, signed_in_resource = nil)

  # Get the identity or create it if it does not exist
  identity = Identity.find_for_oauth(auth)

  user = signed_in_resource ? signed_in_resource : identity.user

  # Create the user if needed (if no logged in user and the identity has no user associated)
  if user.nil?

    # Get the existing user by email if the provider gives us an email.
    # If no 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
    user = User.find_by(:email => email) if email

    email ||= "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com"
    username = auth.info.nickname ? auth.info.nickname : 
             ( auth.extra.raw_info.nickname ? auth.extra.raw_info.nickname : 
             ( auth.extra.raw_info.username ? auth.extra.raw_info.username : "nickname: " + auth.uid) ) # Same as Identity.rb (in find_for_oauth)
    new_username = username

    # Create the user if it's a new registration.
    # Use default values that will be updated later
    if user.nil?
      # Control if username is taken
      user_same_name = User.find_by(:username => new_username)
      while user_same_name
        rnd = SecureRandom.random_number(10000).to_s
        new_username = username + " (" + rnd + ")"
        user_same_name = User.find_by(:username => new_username)
      end
      user = User.new(
        name: auth.extra.raw_info.name,
        username: new_username,
        email: email,
        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

Identity model

class Identity < ApplicationRecord
  belongs_to :user
  validates_presence_of :uid, :provider
  validates_uniqueness_of :uid, :scope => :provider

  def self.find_for_oauth(auth)
    identity = find_or_create_by(uid: "nickname: " + auth.uid, provider: auth.provider) # Same as User.rb (in find_for_oauth)

    identity.name = auth.extra.raw_info.name
    identity.email = auth.info.email
    identity.image_url = auth.info.image
    identity.profile_url = nil
    identity.token = auth.credentials.token
    identity.secret = auth.credentials.secret
    identity.save
    identity
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM