Search code examples
ruby-on-railsrubydeviseomniauthomniauth-twitter

Omniauth Twitter sign up takes over existing Devise user record (if any with same username) instead of stopping the sign up with username exists error


I'm using Devise (form) and Omniauth Twitter. My sign up form fields are:

  • username (unique, required)
  • email (unique, required)
  • full name
  • password

Both sign up works fine when there is no existing record with the same username. User can sign up either via Twitter or email form.

The problem is:

If user tries to sign up via form using an existing username, it doesnt allow. It is OKAY.

But when user tries to sign up via Twitter and if twitter nickname (username in my website) already exists, it doesnt block the sign up, it transfers the existing account to the new sign up Twitter, it updates existing user details with the ones from Twitter profile (API) . How can I stop it?

Thank you!


omniauth_callbacks_controller.rb

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

designer.rb

class Designer < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable, omniauth_providers: [:twitter]

  validates_presence_of     :email
  validates_uniqueness_of   :email   
  validates_presence_of     :username
  validates_uniqueness_of   :username    
  validates_presence_of     :password, if: :password_required? # recommended
  validates_confirmation_of :password, if: :password_required? # recommended
  validates_length_of       :password, within: password_length, allow_blank: true # recommended


  extend FriendlyId
  friendly_id :username, use: [:slugged, :history]

  has_many :posts

  mount_uploader :avatar, AvatarUploader

  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |designer|
      designer.provider = auth.provider
      designer.uid = auth.uid

      designer.slug = auth.info.nickname
      designer.username = auth.info.nickname
      designer.twitter_username = auth.info.nickname

      designer.email = auth.info.email 
      designer.password = Devise.friendly_token[0, 20]
      designer.fullname = auth.info.name 
    end
  end

  def self.new_with_session(params, session)
    if session["devise.designer_attributes"]
      new(session["devise.designer_attributes"]) do |designer|
        designer.attributes = params
        designer.valid?
      end
    else
      super
    end
  end

  def password_required?
    super && provider.blank?
  end

  def update_with_password(params, *options)
    if encrypted_password.blank?
      update_attributes(params, *options)
    else
      super
    end
  end
end

controllers/registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController


  private

  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, :password, :password_confirmation, :current_password)
  end


  protected

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

end

devise/registration/new.html.erb

<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= f.error_notification %>

      <div class="form-inputs">
        <%= f.input :fullname, required: true, placeholder: "Fullname", label: false, input_html: { maxlength: 120 } %>
        <%= f.input :username, unique: true, required: true, placeholder: "Username", label: false, input_html: { maxlength: 120 } %>
        <%= f.input :email, required: true, placeholder: "Email", label: false, input_html: { maxlength: 120 } %>
        <% if f.object.password_required? %>
          <%= f.input :password, required: true, placeholder: "Password (minimum 6 chars)", label: false, input_html: { maxlength: 120 } %>
        <% end %>
        </div>

      <div class="form-actions tl pl1">
        <%= f.button :submit, "Sign up" %>
      </div>
    <% end %>

From this I redirect to profile editing for additional profile information which is designer/edit.html.erb


Solution

  • I fixed it with adding the codes below to designer.rb 🤦‍♂️

      def should_generate_new_friendly_id?
        slug.blank? || username_changed?
      end