Godzilla74 Godzilla74 - 4 months ago 63
Ruby Question

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

Answer

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
Comments