I have this model in Ruby but it throws a ActiveModel::ForbiddenAttributesError
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(auth.slice("uid", "nickname", "image")).first || create_from_omniauth(auth)
end
def self.create_from_omniauth(auth)
create! do |user|
user.uid = auth["uid"]
user.name = auth["info"]["nickname"]
user.image = auth["info"]["image"]
end
end
end
when i run this action
def auth_callback
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_url, notice: "signed in!"
end
end
on ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux]
Can you please tell me how to get rid of this error?
Thanks in advance.
I think that the slice method it no longer available to bypass strong parameters.
Try this code, which tries to be more specific for auth options:
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.save
end
end
You may probably need to use auth[:provider] instead of auth.provider.
Let me know in the comments if this works for you.
You can also browse this question that helped me out.
Related
I have two facebook buttons in my application. One on users/sign_up and other on /visitors/owner-faq. I want to save the request.referrer in order to know from which page the user has signed up. I had implemented the same for external signup(which uses a form) and it worked. Now I'm unable to implement the same for facebook omniauth.
Code:
#user.rb
def self.from_omniauth(auth)
email = auth.info.email
user = User.find_by_email(email) # first tries to find an existing user who signed up normal way with the same email to sign them in
if user && user.confirmed?
user.provider = auth.provider
user.uid = auth.uid
return user
end
where(provider: auth.provider, uid: auth.uid).first_or_create do |user| # then tries to find the user who authenticated through FB, and if
user.email = auth.info.email # not present, creates that user
user.password = Devise.friendly_token[0,20]
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.social_photo_url = auth.info.image
user.skip_confirmation!
end
end
#users/omniauth_callbacks_controller
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"]
Rails.logger.info(#user.errors.inspect)
redirect_to new_user_registration_url
end
end
#application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :store_referrer_url, :only => [:new]
private
def store_referrer_url
session[:referrer] = URI(request.referer).path
end
end
Attempt #1:
I managed to save the request.referrer like this in users/omniauth_callbacks_controller
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
#user.referrer_url = session[:referrer] #here
#user.save!
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"]
Rails.logger.info(#user.errors.inspect)
redirect_to new_user_registration_url
end
end
But the problem here is the value of referrer_url is overwritten when the existing user logs in from another page. I don't want to get the referrer_url to be overwritten.
Attempt #2:
I tried to save the request.referrer in the from_omniauth(auth) method User model like this
def self.from_omniauth(auth)
email = auth.info.email
user = User.find_by_email(email) # first tries to find an existing user who signed up normal way with the same email to sign them in
if user && user.confirmed?
user.provider = auth.provider
user.uid = auth.uid
return user
end
where(provider: auth.provider, uid: auth.uid).first_or_create do |user| # then tries to find the user who authenticated through FB, and if
user.email = auth.info.email # not present, creates that user
user.password = Devise.friendly_token[0,20]
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.social_photo_url = auth.info.image
user.referrer_url = session[:referrer]
user.skip_confirmation!
end
end
But it gave me this error
undefined local variable or method `session' for #<Class:0x0000000e1c46f8>
Any suggestions would be greatly helpful.
If you want the referrer_url property to stay the same after it's first set, you should use the OR Equal operator, that will only set the value if it's currently nil or false:
#user.referrer_url ||= session[:referrer]
If I understand correctly, your facebook callback is called both on sign up and login, and of course the value would be overwritten. OR Equals will prevent the value from being overwritten once it's set.
Regarding Attempt #2, the session hash is only available in the controller and the view, so you can't use it in the model.
Another possibility:
You are setting session[:referrer] in your store_referrer_url method which is called in a before_filter callback on the new method.
Most likely your login is also made with a new method (for example, with Devise it would be in SessionsController), so the callback is called again and the value is overwritten (all Devise controllers inherit from your ApplicationController). In this case, you could take out the before_filter callback out of the ApplicationController to the controller where you are handling signing up (would be RegistrationsController with Devise).
I have an app, build on Rails 4.2 and tried to do twitter authentication, using RailsCasts #241 Simple OmniAuth.
But have this issue: Validation failed: Password can't be blank!
I searched answer everywhere, but did not find the solution!
user.rb
class User < ActiveRecord::Base
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
end
end
end
sessions_controller.rb
class SessionsController < ApplicationController
def login
end
def create
#user = User.find_by_email(params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to root_path
else
redirect_to :back
end
end
def create_session
auth = request.env['omniauth.auth']
user = User.find_by_provider_and_uid(auth['provider'], auth['uid']) || User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url, notice: 'Signed in!'
end
def logout
reset_session
redirect_to root_path
end
end
routes.rb
get 'sessions/login'
get 'sessions/logout'
post 'sessions' => 'sessions#create'
get '/auth/:provider/callback' => 'sessions#create_session'
post '/auth/:provider/callback' => 'sessions#create_session'
get 'registration' => 'users#new', as: 'registration'
Solution
After editing user.rb model looks like:
class User < ActiveRecord::Base
has_secure_password
validates :password, length: { minimum: 6 }
require 'securerandom'
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.password = SecureRandom.urlsafe_base64
end
end
end
An alternative approach is to enter a random password on user creation. For example as shown in the omniauth-google-oauth2 readme. So you could alter your code to this:
require 'securerandom'
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.password = SecureRandom.urlsafe_base64
end
end
I think it's problem with
has_secure_password
try commenting it, and you can override the has_secure_password Or
has_secure_password(validations: false)
This is happening as it automatically adds
validates_presence_of :password_digest
So when specify allow_blank or allow_null it will not work
I'm trying to use OmniAuth to integrate Facebook with my website, and I think I'm getting a few errors here. Right now when I click "Sign in with Facebook" it does bring me to Facebook, but soon as I sign in I get an error saying ActiveModel::ForbiddenAttributesError. Also, I think there might be an issue my routes as well but I'm not sure.
Also, I followed this RailsCasts tutorial: http://railscasts.com/episodes/360-facebook-authentication?autoplay=true
Edit: The error is on this line here, where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
omniauth.rb
OmniAuth.config.logger = Rails.logger
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_APP_ID'], ENV['FACEBOOK_SECRET']
end
user.rb
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
routes.rb
Rails.application.routes.draw do
get 'auth/:provider/callback', to: 'sessions#create'
get 'auth/failure', ('/posts/index')
get 'signout', to: 'sessions#destroy', as: 'signout'
resources :welcome
resources :posts
root "welcome#index"
sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_url
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
Modify your finder like so:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
Creating a Rails app that interacts heavily with Facebook. I want to users to be able to look at profiles of other users and see information on those users that has been pulled in from Facebook.
I'm using the omniauth-facebook and koala gems. I followed Ryan Bates' Railscasts on Facebook authentication and Facebook Graph API to get where I'm at.
I would prefer not to have to store a user's Facebook info in my database, but that seems to be the only way of doing it, because otherwise, when I try to view the profile page of another user who may have logged out, I get: "Koala::Facebook::AuthenticationError in Profiles#show"
type: OAuthException, code: 190, error_subcode: 467, message: Error validating access token: The session is invalid because the user logged out. [HTTP 400].
Is there a way to bring in this data from Facebook, even if that user has logged out?
Here's the necessary code:
user.rb:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.image = auth.info.image
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
block_given? ? yield(#facebook) : #facebook
rescue Koala::Facebook::APIError
logger.info e.to_s
nil
end
end
sessions_controller.rb
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to feed_path
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
rescue ActiveRecord::RecordNotFound
end
helper_method :current_user
end
profiles_controller.rb
class ProfilesController < ApplicationController
def show
#user = User.find(params[:id])
end
end
profiles_helper.rb:
module ProfilesHelper
def facebook_profile_info
#user.facebook.get_object("#{#user.uid}",fields:'gender, locale, bio, birthday,
picture, relationship_status')
end
end
Also, if you have suggestions on better ways to accomplish these tasks than the trajectory I'm headed in, I welcome your suggestions.
I am having a little trouble with Twitter authentication. I keep getting the email can't be blank, and redirect to sign up, but after inserting email and clicking sign up, I still get the same error. I tried making it unneeded, but I get the error saying that someone with "" email exists already.
Thanks.
Devise Routes.rb
def has_role?(role)
return true;
end
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.username = auth.info.nickname
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session["devise.user_attributes"]) do |user|
user.attributes = params
user.valid?
end
else
super
end
end
def password_required?
super && provider.blank?
end
def update_with_password(params, *options)
if encrypted_password.blank?
update_attributes(params, *options)
else
super
end
end
end
Call Back Controller
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
alias_method :twitter, :all
end
You can define username to be your "authentication key" instead of email by
uncommenting this line
config.authentication_keys = [ :email ]
in config/initializers/devise.rb
and changing it to
config.authentication_keys = [ :username ]
i also had the same problem.
this error arises due the fact that in devise User model Email field is set to NotNull.
Solutions:-
1.Set email field in devise to allow null values.
2.so i had saved the email from twitter in my devise email field this is the code for twitter
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
temp = Tempfile.new([auth["uid"], ".jpg"])
temp.binmode
temp.write(open(auth["info"]["image"]).read)
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20],
image:temp
)
user.build_profile(name:user.name,image:temp)
user.profile.save
end
user
end