I am setting up devise with omniauth facebook gem to build a signup system. Users can either register with email or connect with their facebook account.
If an email registered user signs in with the facebook account, I check if the email address is already registered and connect these two accounts, and then log the user in.
This whole scenario works already. The user entry is updated with the new omniauth data of facebook. I get this error, when facebooks sends the callback (data from facebook is saved to db successfully):
RuntimeError in CallbacksController#facebook
Could not find a valid mapping for true
Extracted source (around line #5):
class CallbacksController < Devise::OmniauthCallbacksController
# Define each provider for omniauth here (def twitter...)
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
sign_in_and_redirect #user
end
end
user.rb modell looks like this:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
user = where(provider: auth.provider, uid: auth.uid).first
unless user
user = where(email: auth.info.email).first_or_initialize
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.name = auth.info.name
user.nickname = auth.info.nickname
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.location = auth.info.location
user.description = auth.info.description
user.image = auth.info.image
user.phone = auth.info.phone
user.urls = auth.info.urls
user.password = Devise.friendly_token[0,20]
user.save!
end
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { :omniauth_callbacks => "callbacks" }
resources :auctions do
resources :comments
end
root 'welcome#index'
get '/', :to => 'welcome#index'
end
Your self.from_omniauth method in your User class is returning true, instead of a User model. It's because the return value of a method in Ruby is the result of the last line evaluated, and in this case user.save! is the last line that runs. The "Could not find a valid mapping for true" error is a result of passing true into sign_in_and_redirect; you can see in the Devise source that the first argument is passed into Devise::Mapping.find_scope!.
The solution's simple - make sure that you're returning your user model in from_omniauth:
def self.from_omniauth(auth)
user = where(provider: auth.provider, uid: auth.uid).first
unless user
user = where(email: auth.info.email).first_or_initialize
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.name = auth.info.name
user.nickname = auth.info.nickname
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.location = auth.info.location
user.description = auth.info.description
user.image = auth.info.image
user.phone = auth.info.phone
user.urls = auth.info.urls
user.password = Devise.friendly_token[0,20]
user.save!
end
user # Make sure we return the user we constructed.
end
Related
I'm getting this error:
The action 'github' could not be found for Users::OmniauthCallbacksController
I've looked everywhere and tried the other suggestions on other peoples posts.
This was the post on stack overflow but they had a typo and I didn't have that same error.
Devise OmniauthsController not being used
This recommendation said to check rake routes but my routes match what I'm pointing to.
https://github.com/plataformatec/devise/issues/1566
Most of the other links were all similar issues and I double checked the info with mine, changed stuff and still getting errors.
Info about my code.
gemfile:
gem 'omniauth-github'
config/routes.rb:
devise_for :users, :controllers => { :omniauth_callbacks => 'users/omniauth_callbacks' }
Users::OmniauthCallbacksController:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def github
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, event: :authentication
set_flash_message(:notice, :success, kind: "Github") if is_navigational_format?
else
redirect_to root_path
end
end
end
User Model:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:github]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.uid = auth.uid
user.provider = auth.provider
user.password = Devise.friendly_token[0, 20]
user.name = auth.info.name #assuming the user model has a name
user.oauth_token = auth.credentials.token
user.image = auth.info.image #assuming the user model has an image
user.save!
end
end
Devise initializer:
config.omniauth :github, Rails.application.secrets.github_client_id, Rails.application.secrets.github_client_secret, scope: 'user:email'
Not sure what to do since I have the method in the Users::OmniauthCallBacks controller? Am I missing something? I've been combing through for an entire day.
Update: somehow I had 2 users folders in the controller but one was hidden? It must have gotten messed up when I reverted to a previous repo last night. Once I removed the folder all was good!
Currently, login of facebook is going well, I can get profile picture.
However, only getting name is not an ordinary name, it is displayed as 4f427j700ef46f2555e6.
How to get it
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
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.avatar = auth.info.image # assuming the user model has an image
# If you are using confirmable and the provider(s) you use validate emails,
# uncomment the line below to skip the confirmation emails.
# user.skip_confirmation!
end
end
Display method
<h2><%= #user.name %></h2>
Why can not I get a name?
please tell me.
I'm playing around with the omniauth-facebook gem to log into a devise session through a facebook account. When I click the "Sign in with facebook" link, everything goes well: a new account is created, I'm signed in and bounce back to the homepage with a message confirming my new session (very good!).
Problem: However when an account already exists, upon clicking the link I am redirected to the user/sign_up page. I've been following this documentation from the Devise wiki. There is a good deal of documentation on similar errors here, here, here and here. Each of the solutions, however, are already implemented in my app (as far as I can tell) OR (in the case of the last link) seem to be based on an older configuration model that seems sufficiently different from the wiki that I'm not sure it's applicable.
My best guess is that it has something to do with the callbacks controller, as #user.persisted? seems to be coming up false.This leads me to believe that my definition of #user is not correct. See below:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
logger.debug "Inside facebook"
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
logger.debug "User is #{#user}"
if #user.persisted?
logger.debug "#user.persisted?"
debugger
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?
logger.debug "user exists"
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path, alert: "Login failed"
end
end
Additionally, my user model is as follows:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = Devise.friendly_token[0,20]
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.fname = auth.info.first_name
user.lname = auth.info.last_name
end
end
end
Any suggestions would be certainly welcome! Thanks in advance.
Try Something Like This
class Authentication < ActiveRecord::Base
belongs_to :user
# validates :provider, :uid, :presence => true
def self.from_omniauth(auth)
authenticate = where(provider: auth[:provider], :uid=>auth[:uid]).first_or_initialize
if authenticate.user
authenticate.provider = auth[:provider]
authenticate.uid =auth[:uid]
else
user = User.find_or_initialize_by(:email => email)
authenticate.provider = auth[:provider]
user.email = email
user.first_name = first_name
user.last_name = last_name
user.social_image = image
user.password = Devise.friendly_token.first(8)
user.save(validate: false)
if user.errors.any?
return user
else
authenticate.user_id = user.id
end
end
authenticate.save
authenticate.user
end
end
Try this
def after_sign_in_path_for(resource)
super resource
end
From what i perceived that you are not going to your landing page
from_omniauth never finds an existing facebook user because you are overwriting the provider attribute:
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
searches for a user with provider 'facebook' in this case, but none can be found:
user.provider = Devise.friendly_token[0,20]
changes the provider to some random token
just remove that line and it should work properly
I don't know where comes from this following error message from Devise + omniauth Google:
"Could not authenticate you from GoogleOauth2 because "Invalid credentials".
Is it came from a occurred manipulation with Google API? Or came from a mistake with my code?
Is there a right convention to call API ID and SECRET KEYS in application.yml
I checked numerous tutorials but no answers...
Please could you guide me. Thank you for your clear explanation.
Here is my controller code
def google_oauth2
user = User.find_for_google_oauth2(request.env['omniauth.auth'])
if user.persisted?
sign_in_and_redirect user, event: :authentication
set_flash_message(:notice, :success, kind: 'GoogleOauth2') if is_navigational_format?
else
session['devise.google_oauth2_data'] = request.env['omniauth.auth']
redirect_to new_user_registration_url
end
end
Here is the Model :
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, omniauth_providers: [:facebook, :google_oauth2]
has_and_belongs_to_many :oauth_credentials
def self.find_for_google_oauth2(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20] # Fake password for validation
user.first_name = auth.info.name
user.last_name = auth.info.nickname
user.picture = auth.info.image
user.token = auth.credentials.token
end
end
Thank you for your help.
I'm using the omniauth-facebook gem in combination with Devise to enable users to login with their Facebook account. This works fine. The problem now lies in the creation of a new user, when first signing up with Facebook. Each User has one ExtendedProfile with detailed user info. So the email and password are columns in the users table, but location and profile_image are stored in the extended_profiles table.
When signing up for the first time, the following method is executed:
def self.find_for_facebook_oauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
end
end
Now this creates a record for the users table, but not an extended_profile. Any idea how I can create a record for extended_profiles with the right user_id and fill it with the information I get back from Facebook? Thanks very much in advance!
So I've found the solution while trying out house9's answer:
def self.find_for_facebook_oauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.create_extended_profile(
:first_name => auth.info.first_name,
:last_name => auth.info.last_name,
:facebook_avatar => auth.info.image)
end
end
The user.create_extended_profile() line creates a relational record with the auth values filled in! :)
Have you tried setting it in the block? Not sure if this will work
def self.find_for_facebook_oauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
# ....
user.extended_profile.build
end
end
another option is to put an after_create callback on the User to create the record
http://guides.rubyonrails.org/active_record_callbacks.html#available-callbacks
or you can conditionally check if the record exists, change up that code to:
def self.find_for_facebook_oauth(auth)
user = where(auth.slice(:provider, :uid)).first
unless user
user = User.new
user.provider = auth.provider
# ....
user.extended_profile.build
user.save
end
user
end