When using Omniauth in Rails, Google_omniauth2 creates a new user if the user exists and use different auth

Im using google_aouth2 and facebook with devise. Google aouth doesn’t seems to play well with the others. Here are some examples :

  1. I create an account in the normal way (No omniauth) –:– I try to sign in with Facebook and it doesn’t create a new account. It simply signin to the existing account (emails are same) –:– If I use Google to sign in, it tries to create a new user and gives this error ‘Validation failed: Email has already been taken’

  2. I create an account using Facebook –:– If I use Google to sign in, it tries to create a new user and gives this error ‘Validation failed: Email has already been taken’

  3. I create an account using Google –:– I try to sign in with Facebook and it doesn’t create a new account. It simply signin to the existing account (emails are same)

How can I make Google to work like Facebook? Detect the existing user account with the same email and sign in without creating another user with the same email?

My user.rb :

class User < ActiveRecord::Base


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

 VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }

  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.email,
          email: auth.info.email,
          #email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
          password: Devise.friendly_token[0,20],
          autouser: true
        )

        if auth.provider == 'google_oauth2'
          user.gplus = auth.info.urls.Google
        end
            

        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

Identity.rb :

	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

omniauth_callbacks_controller.rb :

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}".gsub(/_/," ").split[0...1].join(' ').capitalize) if is_navigational_format?
        else
          session["devise.#{provider}_data"] = env["omniauth.auth"]
          redirect_to new_user_registration_url
        end
      end
    }
  end

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

  def after_sign_in_path_for(resource)
      super resource
  end
end

Found the answer! In the user.rb file, it checks whether the email is verified by Facebook or not by accessing ‘auth.verified’ attribute. It will only take the email and check for an existing account if it’s verified. The problem is,Google does not provide that attribute. So, if someone authenticate with Google I will bypass the verification like this :

  if auth.provider == 'google_oauth2'
    email = auth.info.email
  else
    email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
    email = auth.info.email if email_is_verified
  end

I saw mention at the meta Discourse site of problems with members using Facebook SSO when their Facebook settings were to keep their email address “secure”.

The Discourse team is still working on a graceful solution (currently they’re presented with a disabled email address input, and so “stuck”).

Just something for you to keep in mind if you run into similar problems.

1 Like

Ah I didn’t think about that problem! Can you please give me the link to that discussion so I can follow it?

I don’t know what the outcome will be, but here’s the discussion
https://meta.discourse.org/t/facebook-initial-login-create-account-dialog-leaves-email-field-blank/13815

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.