Search code examples
rubydeviseruby-on-rails-5

Devise and Omniauth using Google OAuth2.0 why is a new user not being created?


I'm using Devise (4.7.1) and omniauth-google-oauth2 (0.8.0) within a Rails 5.2 demo app to handle 3rd party sign ups. I can sign in correctly with seeded data and create new users no problem. But, when I try to use the "sign in with Google OAuth2.0" button it doesn't work. It routes my back to the root, which is part of the callback controller action.

I've been looking into various guides on the web regarding CSRF failures and invalid credentials being used. I have fixed these problems, but am now left with this one which has confounded me. The error I get is:

User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."provider" = ? AND "users"."uid" = ? ORDER BY "users"."id" ASC LIMIT ?  [["provider", "google_oauth2"], ["uid", "106922203178875367517"], ["LIMIT", 1]]
  ↳ app/models/user.rb:21
   (0.1ms)  begin transaction
  ↳ app/models/user.rb:21
  User Exists (0.7ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", ""], ["LIMIT", 1]]
  ↳ app/models/user.rb:21
   (0.1ms)  rollback transaction

It seems as though the action of creating the new user starts, but is stopped due to there not being an Email within the params hash generated by the callback uri.

The params hash looks like this:

Parameters: {"state"=>"XXXXXXXXXXX", "code"=>"XXXXXXXXXXX", "scope"=>"email profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid", "authuser"=>"0", "prompt"=>"consent"}

My User model looks like this:

class User < ApplicationRecord

  has_many :comments
  has_many :books, through: :comments

  validates :email, uniqueness: true
  # validates :first_name, presence: {message: "Please enter your first name"}
  # validates :last_name, presence: {message: "Please enter your surname"}
  validates :encrypted_password, presence: true


  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :omniauthable, :omniauth_providers => [:google_oauth2]


  def self.from_omniauth(auth)
  # Either create a User record or update it based on the provider (Google) and the UID  

      where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.token = auth.credentials.token
      user.expires = auth.credentials.expires
      user.expires_at = auth.credentials.expires_at
      user.refresh_token = auth.credentials.refresh_token
    end
  end
end

And the callbacks controller looks like this:

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token
    def google_oauth2
      @user = User.from_omniauth(request.env["omniauth.auth"])
      if @user.persisted?
        sign_in @user, :event => :authentication #this will throw if @user is not activated
        set_flash_message(:notice, :success, :kind => "Google") if is_navigational_format?
      else
        session["devise.google_data"] = request.env["omniauth.auth"]
      end
      redirect_to '/'
     end
  end

Any help/ guidance on this will be hugely appreciated as I'm a bit stumped right now.

Thanks :)


Solution

  • From what I see your code, you're creating user without email, but you have this validation validates :email, uniqueness: true which won't work if you try to create 2 accounts.

    Could you verify this? User.where(email: "").count and let me know. I think if it's more than 0, you can't register.

    Either remove email uniqueness validation or try to get email from the auth params.

    You can debug with printing the error logs in the callback controller

          if @user.persisted?
            sign_in @user, :event => :authentication #this will throw if @user is not activated
            set_flash_message(:notice, :success, :kind => "Google") if is_navigational_format?
          else
            Rails.logger.info(@user.errors.full_messages)
            session["devise.google_data"] = request.env["omniauth.auth"]
          end