Connect Twitter Account to existing Devise Account - ruby-on-rails

A user can register through Devise.
After the registration a User should be able to connect his
Twitter Account
In the past i used omniauth to get facebook and twitter connect working. But in this particular Situation i need to LINK a twitter account to an existing Devise Account.
Can anyone give me a helping hand on where to start or read, i'm completely clueless.

gems
gem 'omniauth-twitter'
gem 'omniauth-facebook'
gem 'omniauth-linkedin'
in views
<%= check_connection('Facebook')%>
<%= check_connection('Linkedin')%>
<%= check_connection('Twitter')%>
helpers
def check_connection(provider)
if current_user.has_connection_with(provider)
html = link_to disconnect_path(social: provider.downcase), class: "#{provider}-m phone-verified row" do
content_tag :span, 'Verified', class: "verified"
end
else
html = link_to user_omniauth_authorize_path(provider: provider.downcase), class: "#{provider}-m phone-verified row" do
content_tag :span, 'Click to verify', class: "un-verified"
end
end
end
in user model - for you this might be different
devise :database_authenticatable, :registerable, :confirmable, :lockable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable,
:omniauth_providers => [:facebook, :twitter, :linkedin]
has_many :authorizations, :dependent => :destroy
def has_connection_with(provider)
auth = self.authorizations.where(provider: provider).first
if auth.present?
auth.token.present?
else
false
end
end
in routes I have
devise_for :users, :controllers => {:registrations => "users/registrations",
:sessions => "users/sessions",
:passwords => "users/passwords",
:omniauth_callbacks => "users/omniauth_callbacks" #<- this one you need
}
create authorization table - see this link uninitialized constant User::Authorization | omniauth
class CreateAuthorizations < ActiveRecord::Migration
def change
create_table :authorizations do |t|
t.string :provider
t.string :uid
t.integer :user_id
t.string :token
t.string :secret
t.string :first_name
t.string :last_name
t.string :name
t.string :link
t.timestamps
end
end
end
OmniauthCallbacksController /controllers/users/...
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
require 'uuidtools'
def facebook
oauthorize "Facebook"
end
def twitter
oauthorize "Twitter"
end
def linkedin
oauthorize "LinkedIn"
end
def passthru
render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
end
private
def oauthorize(kind)
#user = find_for_ouath(kind, env["omniauth.auth"], current_user)
if #user
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => kind
session["devise.#{kind.downcase}_data"] = env["omniauth.auth"]
sign_in_and_redirect #user, :event => :authentication
end
end
def find_for_ouath(provider, access_token, resource=nil)
user, email, name, uid, auth_attr = nil, nil, nil, {}
case provider
when "Facebook"
uid = access_token['uid']
email = access_token['info']['email']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => nil, :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'], :name => access_token['info']['name'],
:link => access_token['extra']['raw_info']['link'] }
when "Twitter"
uid = access_token['extra']['raw_info']['id']
name = access_token['extra']['raw_info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'], :name => name,
:link => "http://twitter.com/#{name}" }
when 'LinkedIn'
uid = access_token['uid']
name = access_token['info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'],
:link => access_token['info']['public_profile_url'] }
else
raise 'Provider #{provider} not handled'
end
if resource.nil?
if email
user = find_for_oauth_by_email(email, resource)
elsif uid && name
user = find_for_oauth_by_uid(uid, resource)
if user.nil?
user = find_for_oauth_by_name(name, resource)
end
end
else
user = resource
end
auth = user.authorizations.find_by_provider(provider)
if auth.nil?
auth = user.authorizations.build(:provider => provider)
user.authorizations << auth
end
auth.update_attributes auth_attr
return user
end
def find_for_oauth_by_uid(uid, resource=nil)
user = nil
if auth = Authorization.find_by_uid(uid.to_s)
user = auth.user
end
return user
end
def find_for_oauth_by_email(email, resource=nil)
if user = User.find_by_email(email)
user
else
user = User.new(:email => email, :password => Devise.friendly_token[0,20])
user.save
end
return user
end
def find_for_oauth_by_name(name, resource=nil)
if user = User.find_by_name(name)
user
else
first_name = name
last_name = name
user = User.new(:first_name => first_name, :last_name => last_name, :password => Devise.friendly_token[0,20], :email => "#{UUIDTools::UUID.random_create}#host")
user.save(:validate => false)
end
return user
end
end

Related

Omniauth Testing Error, Devise/Rails/Rspec

Appreciate any opinions out there.
This is the exact error I am getting. I can't figure out why.
No route matches {:action=>"users/auth/:facebook", :controller=>"omniauth_callbacks"} missing required keys: [:action]
Omniauth Controller
def facebook
auth = env["omniauth.auth"]
#user = User.connect_to_facebook(request.env["omniauth.auth"],current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.facebook_uid"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
User Model
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,:omniauthable
has_many :games
has_many :comments, dependent: :destroy
def self.connect_to_facebook(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
if user
return user
else
registered_user = User.where(:email => auth.info.email).first
if registered_user
return registered_user
else
user = User.create(username:auth.info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20],)
end
end
end
config/initializers/devise.rb
require 'omniauth-facebook'
config.omniauth :facebook, ENV['FACEBOOK_PUBLIC'], ENV['FACEBOOK_SECRET'], scope: 'email', info_fields: 'email, name'
routes
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks" }
root to: 'home#index'
-spec/controllers/omniauth_callbacks_controller_spec.rb
require 'rails_helper'
RSpec.describe OmniauthCallbacksController, type: :controller do
def set_omniauth(opts = {})
default = {:provider => :facebook,
:uuid => "1234",
:facebook => {
:email => "foobar#example.com",
:gender => "Male",
:first_name => "foo",
:last_name => "bar"
}
}
credentials = default.merge(opts)
provider = credentials[:provider]
user_hash = credentials[provider]
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[provider] = {
'uid' => credentials[:uuid],
"extra" => {
"user_hash" => {
"email" => user_hash[:email],
"first_name" => user_hash[:first_name],
"last_name" => user_hash[:last_name],
"gender" => user_hash[:gender]
}
}
}
end
def set_invalid_omniauth(opts = {})
credentials = { :provider => :facebook,
:invalid => :invalid_crendentials
}.merge(opts)
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[credentials[:provider]] = credentials[:invalid]
end
describe "GET '/users/auth/facebook'" do
before(:each) do
set_omniauth
request.env["devise.mapping"] = Devise.mappings[:user]
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:facebook]
get "users/auth/facebook"
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:facebook]
end
it "should set user_id" do
expect(session[:user_id]).to eq(User.last.id)
end
end
end
Thanks in advance to anyone who can shed some light or at least give me some ideas.
Since you're writing a controller test, you should provide a controller action in get call, not an actual url, like this:
get :facebook
Or, you can change your test to a request spec, this way you'll be testing the whole stack, and it would work with the url.

Getting GooglePlus sign-in working with Rails

So, I'm running into this error when trying to get GooglePlus sign-in working in my rails app:
ActionController::UrlGenerationError in Home#welcome
Showing /Users/user/Sites/new/app/views/users/_verifications.html.erb where line #22 raised:
No route matches {:action=>"passthru", :controller=>"users/omniauth_callbacks", :provider=>"googleplus"} missing required keys: [:provider]
<%= check_connection('Facebook')%>
<%= check_connection('LinkedIn')%>
<%= check_connection('Twitter')%>
<%= check_connection('GooglePlus')%> <----- this line is causing it.
users_helper.rb check_connection method
def check_connection(provider)
if current_user.has_connection_with(provider)
link_to disconnect_path(social: provider.downcase) do
content_tag :div, class: "verified-m #{provider.downcase}-verified row" do
(content_tag :p, provider) +
(content_tag :span, 'Verified', class: "verified")
end
end
else
link_to user_omniauth_authorize_path(provider: provider.downcase) do
content_tag :div, class: "verified-m #{provider.downcase}-verified row" do
(content_tag :p, provider) +
(content_tag :span, 'Click to verify', class: "un-verified")
end
end
end
end
Devise.rb
config.omniauth :facebook, ENV['FR_FACEBOOK_KEY'], ENV['FR_FACEBOOK_SECRET'], :scope => 'email,user_birthday'
config.omniauth :twitter, ENV['TWITTER_CONSUMER_KEY'], ENV['TWITTER_CONSUMER_SECRET']
config.omniauth :linked_in, ENV['FR_LINKEDIN_KEY'], ENV['FR_LINKEDIN_SECRET'], :scope => 'r_fullprofile r_emailaddress r_network'
config.omniauth :gplus, ENV['GPLUS_KEY'], ENV['GPLUS_SECRET'], scope: 'userinfo.email, userinfo.profile'
Gemfile (relevant snippet)
gem 'omniauth-twitter'
gem 'omniauth-facebook'
gem 'omniauth-linkedin'
gem 'omniauth-gplus', '~> 1.0'
gem 'google-api-client', :require => 'google/api_client'
gem 'uuidtools'
And here is the relevant snippit from my OmniauthController, bare in mind, Twitte, Facebook, and LinkedIn validations are working - having trouble with GooglePlus only.
OmniauthController (relevant snippet)
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
require 'uuidtools'
def facebook
oauthorize "Facebook"
end
def twitter
oauthorize "Twitter"
end
def linkedin
oauthorize "LinkedIn"
end
def google_plus
oauthorize "GooglePlus"
end
private
def oauthorize(kind)
#user = find_for_ouath(kind, env["omniauth.auth"], current_user)
if #user
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => kind
session["devise.#{kind.downcase}_data"] = env["omniauth.auth"]
sign_in_and_redirect #user, :event => :authentication
end
end
def find_for_ouath(provider, access_token, resource=nil)
user, email, name, uid, auth_attr = nil, nil, nil, {}
case provider
when "Facebook"
uid = access_token['uid']
email = access_token['info']['email']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => nil, :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'], :name => access_token['info']['name'],
:link => access_token['extra']['raw_info']['link'] }
when "Twitter"
uid = access_token['extra']['raw_info']['id']
name = access_token['extra']['raw_info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'], :name => name,
:link => "http://twitter.com/#{name}" }
when 'LinkedIn'
uid = access_token['uid']
name = access_token['info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'],
:link => access_token['info']['public_profile_url'] }
when 'Google+'
uid = access_token['uid']
name = access_token['info']['email']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'],
:link => access_token['info']['public_profile_url'] }
else
raise 'Provider #{provider} not handled'
end
if resource.nil?
if email
user = find_for_oauth_by_email(email, resource)
elsif uid && name
user = find_for_oauth_by_uid(uid, resource)
if user.nil?
user = find_for_oauth_by_name(name, resource)
end
end
else
user = resource
end
auth = user.authorizations.find_by_provider(provider)
if auth.nil?
auth = user.authorizations.build(:provider => provider)
user.authorizations << auth
end
auth.update_attributes auth_attr
return user
end
What am I doing wrong? Thanks!
I think problem is in making url
Try using:
<%= check_connection('GPlus')%>
Hope this helps.

How to pull the number of Google+ friends from GPlus API?

I'd like to pull the number of Google+ friends that my users have once they have successfully connected their Google+ account through omniauth-gplus: the gplus gem.
I was able to display the number of Twitter Followers and Facebook Friends that my user have using the omniauth-twitter, and omniauth-facebook gems and this was easier as the documentation was clearer on those two gems and API's.
Please see the below code for how I am pulling the Facebook and Twitter followers/friends under the Users::OmniauthCallbacksController:
Authorizations:
class Authorization < ActiveRecord::Base
attr_accessible :provider, :uid, :token, :secret, :first_name, :last_name, :link, :name, :connections_count
def update_connections_number(provider)
if provider == 'Facebook'
connections_count = Koala::Facebook::API.new(self.token).get_connections("me", "friends").count
self.update_attributes(connections_count: connections_count)
elsif provider == 'LinkedIn'
client = LinkedIn::Client.new(ENV['FR_LINKEDIN_KEY'], ENV['FR_LINKEDIN_SECRET'])
client.authorize_from_access(self.token, self.secret)
self.update_attributes(connections_count: client.connections.total)
end
end
end
Users
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def find_for_ouath(provider, access_token, resource=nil)
user, email, name, uid, auth_attr = nil, nil, nil, {}
case provider
when "Facebook"
uid = access_token['uid']
email = access_token['info']['email']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => nil, :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'], :name => access_token['info']['name'],
:link => access_token['extra']['raw_info']['link'] }
when "Twitter"
uid = access_token['extra']['raw_info']['id']
name = access_token['extra']['raw_info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'], :name => name,
:link => "http://twitter.com/#{name}", :connections_count => access_token['extra']['raw_info']['followers_count'] }
when 'LinkedIn'
uid = access_token['uid']
name = access_token['info']['name']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'],
:link => access_token['info']['public_profile_url'] }
when 'GPlus'
uid = access_token['uid']
name = access_token['info']['email']
auth_attr = { :uid => uid, :token => access_token['credentials']['token'],
:secret => access_token['credentials']['secret'], :first_name => access_token['info']['first_name'],
:last_name => access_token['info']['last_name'],
:link => access_token['info']['public_profile_url'] }
else
raise 'Provider #{provider} not handled'
end
if resource.nil?
if email
user = find_for_oauth_by_email(email, resource)
elsif uid && name
user = find_for_oauth_by_uid(uid, resource)
if user.nil?
user = find_for_oauth_by_name(name, resource)
end
end
else
user = resource
end
auth = user.authorizations.find_by_provider(provider)
if auth.nil?
auth = user.authorizations.build(:provider => provider)
user.authorizations << auth
end
auth.update_attributes auth_attr
auth.update_connections_number(provider)
return user
end
def find_for_oauth_by_uid(uid, resource=nil)
user = nil
if auth = Authorization.find_by_uid(uid.to_s)
user = auth.user
end
return user
end
def find_for_oauth_by_email(email, resource=nil)
if user = User.find_by_email(email)
user
else
user = User.new(:email => email, :password => Devise.friendly_token[0,20])
user.save
end
return user
end
def find_for_oauth_by_name(name, resource=nil)
if user = User.find_by_name(name)
user
else
first_name = name
last_name = name
user = User.new(:first_name => first_name, :last_name => last_name, :password => Devise.friendly_token[0,20], :email => "#{UUIDTools::UUID.random_create}#host")
user.save(:validate => false)
end
return user
end
end
How can I do this with Google+?
Cheers!

Validates doesn't work?

i am new in ruby and like the syntax! But i keep getting this error, can someone tell me why? I did also try to include it with include ActiveModel::Validations without any luck. Getting this "stack level too deep"
class HomeController < ApplicationController
def index
if params[:username]
l = Users.new(:username => params[:username], :password => params[:password], :email => params[:email]).save
z = Users.where(:username => params[:username]).limit(1).last
#debugging = "Howdy" + z[:username] + ""
end
end
end
users model:
class Users < ActiveRecord::Base
validates :username, :presence => true
attr_accessible :email, :password, :username
end
It should be in your model if you are using active record:
validates :username, :presence => true
It should be User < ActiveRecord::Base:
user.rb
class User < ActiveRecord::Base
validates :username, :presence => true #or you can also write as 'validates_presence_of :username'
attr_accessible :email, :password, :username
end
home_controller.rb
class HomeController < ApplicationController
def index
if params[:username]
l = User.new(:username => params[:username], :password => params[:password], :email => params[:email])
if l.save?
flash[:success] = "Valid user"
else
flash[:error] = "Invalid user"
end
z = User.where(:username => params[:username]).limit(1).last
#debugging = "Howdy" + z[:username] + ""
end
end
end
Your model seems ok
Try to change your index action for this:
if params[:username]
user = User.create(:username => params[:username], :password => params[:password], :email => params[:email])
if user.persisted?
#debugging = "Howdy #{user.username}"
else
#debugging = "Error while creating user"
end
end
If the user is not valid User.create will create an User object not persisted in database

Rails: Using OmniAuth (FB authorization), Reading Data From Facebook Hash

Using OmniAuth, I successfully fetched the hash data from Facebook: stored in "auth"
# extra=#] last_name="Jordan" link="http://www.facebook.com/michael" locale="en_US" middle_name="Ball" name="Michael Jordan" quotes="\"lalala\"\n\n\"lala\"" timezone=9 updated_time="2011-09-01T20:25:58+0000" username="mjordan82" verified=true>> info=# verified=true> provider="facebook" uid="123456879">
In User Model, I do as follows:
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["name"]
end
end
When I checked the database, I only got provider and uid. User.name row was empty. From testing, I figured out I couldn't store other data than provider and uid. For example, user.name = auth["provider"] or user.name = auth["uid"] stored with no problem, but when I tried something like user.name = auth["timezone"] or user.name = auth["last_name"], nothing was stored in the variable. Anyone know how to fix this? I also tried user.name = auth["user_info"]["name"], but it returned an error.
I am not sure why user.name = auth["name"] does not store anything. In other words, why is auth["name"] not "Michael Jordan" in this case?
The key was this: I was accessing the auth hash in a wrong way. The answer is you do
user.name = auth["info"]["name"]
Here's detailed information about the Auth Hash:
:provider => 'facebook',
:uid => '1234567',
:info => {
:nickname => 'jbloggs',
:email => 'joe#bloggs.com',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:image => 'http://graph.facebook.com/1234567/picture?type=square',
:urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
:location => 'Palo Alto, California',
:verified => true
},
:credentials => {
:token => 'ABCDEF...', # OAuth 2.0 access_token, which you may wish to store
:expires_at => 1321747205, # when the access token expires (it always will)
:expires => true # this will always be true
},
:extra => {
:raw_info => {
:id => '1234567',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:link => 'http://www.facebook.com/jbloggs',
:username => 'jbloggs',
:location => { :id => '123456789', :name => 'Palo Alto, California' },
:gender => 'male',
:email => 'joe#bloggs.com',
:timezone => -8,
:locale => 'en_US',
:verified => true,
:updated_time => '2011-11-11T06:21:03+0000'
}
source: https://github.com/mkdynamic/omniauth-facebook
This is why I could access "provider" and "uid" with simply auth["provider"], and I needed to do
auth["info"]["name"]
to access the name information. Similarly, to get the user's timezone, you could do
auth["extra"]["timezone"]

Resources