Godzilla74
Godzilla74

Reputation: 2512

Devise Twitter Omniauth redirects to sign_up

I've successfully implemented Devise with omniauth-facebook and am now trying my hand at Twitter. I've pretty much copied the Devise settings for Facebook into methods for Twitter. However, after successfully approving my app to use my Twitter account I'm redirected back to my user registration page (http://localhost:3000/users/sign_up). Why is this?

Console output

Started GET "/users/auth/twitter/callback?oauth_token=zeIyTgAAAAAAwAQEAAABVcusPxc&oauth_verifier=q24BAAziukc8bF6nnaxuRoouuGaPuoF3" for ::1 at 2016-07-08 14:01:51 -0400
I, [2016-07-08T14:01:51.984997 #44805]  INFO -- omniauth: (twitter) Callback phase initiated.
Processing by Users::OmniauthCallbacksController#twitter as HTML
  Parameters: {"oauth_token"=>"zeIyTgAAAAAAwAQEAAABVcusPxc", "oauth_verifier"=>"q24BAAziukc8bF6nnaxuRoouuGaPuoF3"}
  User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE "users"."provider" = $1 AND "users"."uid" = $2  ORDER BY "users"."id" ASC LIMIT 1  [["provider", "twitter"], ["uid", "248829852"]]
   (0.2ms)  BEGIN
   (0.1ms)  ROLLBACK
Redirected to http://localhost:3000/users/sign_up
Completed 302 Found in 165ms (ActiveRecord: 1.0ms)

controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def twitter
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
    else
      session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end

models/user.rb

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :omniauthable, :omniauth_providers => [:facebook, :twitter]

   def self.from_omniauth(auth)
   where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
     user.email = auth.info.email
     user.password = Devise.friendly_token[0,20]
     user.name = auth.info.name   # assuming the user model has a name
     user.image = auth.info.image # assuming the user model has an image
   end
 end

end

config/routes.rb

Rails.application.routes.draw do

  devise_for :users,
    :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }


   root 'welcome#index'
end

Upvotes: 0

Views: 796

Answers (2)

Jen V.
Jen V.

Reputation: 19

I was struggling with the same error, with Github authentication, and finally by adding scope to the config.omniauth in devise.rb, it worked! Maybe it needed scope user to retrieve email and other info

config.omniauth :github, ENV['GITHUB_APP_ID'], ENV['GITHUB_APP_SECRET'], scope: 'repo,user'

Upvotes: 0

Nic Nilov
Nic Nilov

Reputation: 5155

Add debug output to your twitter callback like this to see the exact error preventing transaction from committing:

  def twitter
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
    else
      session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")

      puts @user.errors

      redirect_to new_user_registration_url
    end
  end

Twitter is known to not require email during the registration so it may not return the value when doing oauth against such an account.

To the question in point, Devise provides automatic validations for some fields by default. From Devise source:

  def self.included(base)
    base.extend ClassMethods
    assert_validations_api!(base)

    base.class_eval do
      validates_presence_of   :email, if: :email_required?

To switch off the default email presence validation, put this in your User model:

def email_required?
  false
end

Depending on your use case you may also want to change the default authentication key to not use the email:

in config/initializers/devise.rb:

config.authentication_keys = [ :username ]

UPDATE

When using Devise-supplied generator, the user table may end up with null: false constraint on the email field. To remove it, create a migration with:

change_column :users, :email, :string, :null => true

and update the database schema:

bundle exec rake db:migrate

Upvotes: 2

Related Questions