Omniauth Testing Error, Devise/Rails/Rspec - ruby-on-rails

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.

Related

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!

Connect Twitter Account to existing Devise Account

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

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

omniauth mock facebook response

I'm want to test my login through facebook. Im using pure omniauth, w/o Devise. I check the wiki page and do following:
helper for request specs
module IntegrationSpecHelper
def login_with_oauth(service = :facebook)
visit "/auth/#{service}"
end
end
spec_helper.rb
RSpec.configure do |config|
config.include IntegrationSpecHelper, :type => :request
end
Capybara.default_host = 'http://example.org'
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:facebook, {
:provider => 'facebook',
:uid => '12345',
:user_info => {
:name => 'dpsk'
}
})
my spec
require 'spec_helper'
describe 'facebook' do
it "should login with facebook", :js => true do
login_with_oauth
visit '/'
page.should have_content("dpsk")
end
end
#OmniAuth routes
match "/auth/:provider/callback" => "sessions#create"
match "/signout" => "sessions#destroy", :as => :signout
match "/signin" => "sessions#signin", :as => :signin
match "/auth/failure" => "sessions#failure", :as => :auth_failure
But in spec nothing returns instead of my mock i got an error:
Failure/Error: visit "/auth/facebook"
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
Where is my error?
My problem was in the mock, its has wrong structure.
OmniAuth.config.mock_auth[:facebook] = {
'user_info' => {
'name' => 'Mario Brothers',
'image' => '',
'email' => 'dpsk#email.ru' },
'uid' => '123545',
'provider' => 'facebook',
'credentials' => {'token' => 'token'}
}
This structure worked for me (December 2016)
OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new({
'provider' => 'facebook',
'uid' => '123545',
'info' => {
'email' => 'dpsk#email.ru',
'name' => 'Mario Brothers',
'image' => '' },
'credentials'=> {
'token'=> '12345',
'expires_at' => 1486718672,
'expires' => true },
'extra' => {
'raw_info' => {
'email' => 'dpsk#email.ru',
'name' => 'Mario Brothers',
'id' => '12345' }
}
})

Resources