I have successfully implemented oAuth with omniauth-facebook
and omniauth-google
but I am having difficulties with implementing omniauth-twitter
as it does not provide an email address.
I searched through available solutions on the Internet and one solution pointed out at removing Devise's email validation but email validation is needed for my app.
What I want instead is to redirect the user to a new form where the user only has to enter a new email address to complete his oAuth registration but I am not sure how to link it with the oAuth data to complete the registration as I am getting errors when I submit the form since username and password can't be blank from devise while only email is displayed in that form.
CallbackController
def twitter
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect @user, :event => :authentication
else
session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")
redirect_to twitter_register_path
end
end
twitter_register_path
contains the view where the user has to fill in his email address to complete registration.
User Model
def self.from_twitter_omniauth(auth,email)
where(provider: auth["provider"], uid: auth["uid"]).first_or_create do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.email = email
user.name = auth["info"]["name"]
user.password = Devise.friendly_token[0,20]
user.remote_poster_image_url = auth["info"]["image"].gsub('http://','https://')
end
end
Twitter signup form (twitter_register_path)
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :email, required: true,label: false %>
</div>
<div class="signup">
<%= f.button :submit, "Sign up",:class =>"register-button" %>
</div>
<% end %>
Even though the Twitter API recently allows applications to request additional permissions for their email, I am unsure of how to implement that.
I finally managed to solve it this way.
After the user is redirected and if it's a new user, we will render a form TwitterForm
for the user to input an email to complete the registration.
Next, the form will be submitted to a custom action that we specified, in my case I put it under UsersController
that'll complete the sign up process using the Twitter session data and the email that the user inputted.
You can't put the action together inside the CallbacksController due to some issues with Devise which I am unsure as to why there's an error.
Devise CallbacksController
def twitter
auth = request.env["omniauth.auth"]
@user = User.where(provider: auth.provider, uid: auth.uid).first_or_create
if @user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect @user, :event => :authentication
else
@form = TwitterForm.new
session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")
render :layout => 'none'
end
end
UsersController
def twitter_register
@form = TwitterForm.new(params[:twitter_form])
if @form.valid?
email = params[:twitter_form][:email]
@user = User.from_twitter_omniauth(session["devise.twitter_data"],email)
if @user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect @user, :event => :authentication
else
redirect_to register_path
end
else
render 'callbacks/twitter',layout: 'none'
end
end
TwitterForm
class TwitterForm
include ActiveModel::Model
attr_accessor :email
validates :email, presence: true,:format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
end
User Model
def self.from_twitter_omniauth(auth,email)
where(provider: auth["provider"], uid: auth["uid"]).first_or_create do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.email = email
user.name = auth["info"]["name"]
user.password = Devise.friendly_token[0,20]
user.remote_poster_image_url = auth["info"]["image"]
end
end