I'm trying to pull out profile information from my user model. Currently I'm using Omniauth (twitter & facebook) to create users and just writing all the information to the user table... this works great, however I was told that proper Rails convention is to separate the user & profile.
After a couple of days of trial and error, I'm having trouble getting the has_one relationship working while creating a profile with the users info from the Omniauth hash.
I've tried to user a before_filter to automagically create a profile before the user is create, which properly creates the user_id foreign key in the profiles table, however I can't seem to get any of the omniauth hash data to write.
my sessions controller:
def create
auth = request.env["omniauth.auth"]
if user = User.find_by_provider_and_uid(auth["provider"], auth["uid"])
redirect_to root_url, :notice => 'Signed In'
else
user = User.create_with_omniauth(auth)
redirect_to profile_path(user), :notice => 'Please verify your profile'
end
in my user model:
before_create :create_profile
def self.create_profile(auth)
create! do |profile|
profile.user_id = session[:user_id]
profile.name = auth["info"]["name"]
profile.email = auth["info"]["email"]
profile.nickname = auth["info"]["nickname"]
profile.location = auth["info"]["location"]
profile.image = auth["info"]["image"]
end
end
...
The other option I tried was to write the profile data with the profile model... which works for the Omniauth hash data, but wont write the correct user_id (so no foreign key)
profile.rb
def self.create_profile_with_omniauth(auth)
create! do |profile|
profile.user_id = session[:user_id]
profile.name = auth["info"]["name"]
profile.email = auth["info"]["email"]
profile.nickname = auth["info"]["nickname"]
profile.location = auth["info"]["location"]
profile.image = auth["info"]["image"]
end
end
Related
I've trying to manage user sign up with google account for my rails 4.0.0 app. Devise works perfectly. And there is working sign in with Google Account for existing users. But I have some difficulties with new user registration using Google Oauth 2. For example: i've got google account "example#google.com". It's logged in on my current PC. And when I try to sign up with this account to my app it generates blank register form. If I dont manually provide email, login, full name, etc. - I've got error message that they "cannot be blank". I guess solution is create default value to text fields to fetch user details.
So, my question is how can I provide values for variables in view that equals variables from google account?
Email field in form_for in new user registration:
= f.email_field :email, :autofocus => true, :value => 'how can i put auth.info.email here?'
omniauth_callbacks_controller.rb:
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in Through Google!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
flash.notice = "You are almost Done! Please provide a password to finish setting up your account"
redirect_to new_user_registration_url
end
end
end
omniauth method from user model:
def self.from_omniauth(auth)
if user = User.find_by_email(auth.info.email)
user.provider = auth.provider
user.uid = auth.uid
user
else
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.full_name = auth.info.name
user.email = auth.info.email # THIS (user.email) value i want to provide to my registration form as default value
user.birthday = auth.info.birthday
user.avatar = auth.info.image
end
end
end
I had the same problem with GitHub you can take a look at my user model
https://github.com/flower-pot/pastebin/blob/master/app/models/user.rb
I'm having some trouble storing a user's Twitter access token in the User model. I have installed the Omniauth gem as per Railscast #241 and was successful in setting up the Twitter authentication, storing the "uid" and "name". In order to make authenticated Twitter API calls, I wanted to store the user's access token and access token secret and thus created a migration to create those fields. I did that successfully and can assign those fields successfully in the Rails Console to records that do not have the stored. When trying to authenticate a new user, however, and pull this information in from the start, I get the error listed in the title. HEre is additional error information:
app/models/user.rb:13:in `block in create_from_omniauth'
app/models/user.rb:10:in `create_from_omniauth'
app/models/user.rb:6:in `from_omniauth'
app/controllers/sessions_controller.rb:5:in `create'
I follow down this path from the sessions controller to the User model but can't figure out what is causing the error. I have included those documents below.
Sessions Controller
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_url, notice: "Signed in"
end
def destroy
session[:user_id] = nil
redirect_to root_url, notice: "Signed out"
end
end
User Model
class User < ActiveRecord::Base
attr_accessible :name, :uid, :access_token, :access_token_secret
has_many :events
def self.from_omniauth(auth)
where(auth.slice('uid')).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.access_token = auth["extra"]["access_token"]["token"]
user.access_token_secret = auth["extra"]["access_token"]["secret"]
end
end
end
Can anyone help me troubleshoot this error? I know it has to do with setting the access_token fields in the create_from_omniauth method as it works fine without them. I've been banging my head trying to figure out why these won't work. Thanks in advance for any help.
I doubt why you need to save these tokens as they looks of no use. In this case, the most important thing is the Twitter returned uid for identifying or create user.
Anyway, the reason of your error is there are not such keys.
auth['extra']['access_token'] is a string, there is no further sub keys.
:extra => {
:access_token => "", # An OAuth::AccessToken object
To access token and secret, you can use auth['credentials']
:credentials => {
:token => "a1b2c3d4...", # The OAuth 2.0 access token
:secret => "abcdef1234"
},
Reference:
https://github.com/arunagw/omniauth-twitter#authentication-hash
https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema#schema-10-and-later
I was following this tutorial on Omniauth: http://railscasts.com/episodes/235-omniauth-part-1?view=asciicast
I keep getting this error:
no such column: authentication.provider:
Now the main thing I want to know is why "provider" isn't being accepted. It exists in the class... the authentications database exists... so why is it saying it isn't there?
Here's my authentications controller:
class AuthenticationsController < InheritedResources::Base
def index
#authentications = current_user.authentications if current_user
end
def create
#user = User.where(authentication: auth).first_or_create(:provider => auth['provider'], :uid => auth['uid'])
self.current_user = #user
# auth = request.env["omniauth.auth"] current_user.authentications.create(:provider => auth['provider'], :uid => auth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
end
def auth
request.env['omniauth.auth']
end
def destroy
#authentication = current_user.authentications.find(params[:id])
#authentication.destroy
flash[:notice] = "Successfully destroyed authentication."
redirect_to authentications_url
end
end
I can assure you I have a model called authentication and that this model has a provider and uid field. I've also tried where(authentications: auth) and where(auth: auth)
each with no luck.
Any ideas would be appreciated.
UPDATE
authentication.rb (model)
class Authentication < ActiveRecord::Base
attr_accessible :create, :destroy, :index, :provider, :uid, :user_id
belongs_to :user
end
UPDATE 2
I'm basically attempting to adapt this tutorial to rails 3.2.
The original line from the tutorial is commented out above.
UPDATE 3
Here is the entire first line of error:
SQLite3::SQLException: no such column: authentication.provider: SELECT "users".* FROM "users" WHERE "authentication"."provider" = 'facebook' AND "authentication"."uid" = '2222222' AND "authentication"."info" = '--- !ruby/hash:OmniAuth::AuthHash::InfoHash
Hate to be a burden... but the clock's really ticking, my ass is on the line, and I'm about to go completely insane trying to figure this out. If you can tell me just why provider isn't being accepted I'm sure I can figure out the rest.
your create action has not sense
User.where(authentication: auth) converts to SELECT * FROM users WHERE authentication = a_hash
You shoul do something like
auth1 = Authentication.where(provider: auth['provider'], uid: auth['uid']).first
if !auth1.nil?
current_user = auth.user
else
user = User.new
user.authentications.build(provider: auth['provider'], uid: auth['uid'])
user.save!
current_user = user
end
Since you are just adding a record in the authentications table, I am unable to understand why you are reassigning this.current_user. Also is current_user a helper method or a member, if it's a member where is it declared?
Don't you just want to create an authentication for the current user as such?:
def create
current_user.authentications.first_or_create(:provider => auth['provider'], :uid => auth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
end
This finds the first authentication record by provider and uid, if not found then creates that authentication record.
Also by that error, I hope you have figured out the answer to this question:
Now the main thing I want to know is why "provider" isn't being
accepted. It exists in the class... the authentications database
exists... so why is it saying it isn't there?
It is because you are calling first_or_create() on User object, not Authentication.
I also faced this issue recently. At first I thought I had forgotten to add a provider column to users table, but that wasn't it.
This is how I eventually solved it:
def self.from_omniauth(auth)
where(provider: auth["provider"], uid: auth["uid"]).first_or_create do |user|
user.email = auth["info"]["email"]
user.password = Devise.friendly_token[0, 20]
user.logo = auth["info"]["image"]
# if you use confirmable, since facebook validates emails
# skip confirmation emails
user.skip_confirmation!
end
end
auth is a hash like the one below, so instead of auth.provider, I used auth["provider"] etc:
omniauth.auth: {"provider"=>"facebook", "uid"=>"11111111111111", "info"=>{"email"=>"some#email.com", "image"=>"http://graph.facebook.com/v2.6/11111111111111/picture"}, "credentials"=>{"token"=>"sometoken", "expires_at"=>1506680013, "expires"=>true}, "extra"=>{"raw_info"=>{"email"=>"some#email.com", "id"=>"11111111111111"}}}
I'm trying to set up Omniauth login for Twitter/FB. I created my own authentication system that validates for password and email upon creation of a user. However, I do not want to validate for password or email when my users log in through Twitter/Fb.
I created a user attribute called omniauth_login. I am using it as a boolean to test whether validation is required or not in my should_validate_password? method.
User.rb
attr_accessor :password, :updating_password, :omniauth_login
validates_presence_of :password, :if => :should_validate_password?
validates_confirmation_of :password, :if => :should_validate_password?
def should_validate_password?
(updating_password || new_record?) && !(self.omniauth_login == 'true')
end
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["name"]
end
end
Here is my controller used to create the user:
sessions_controller.rb
def omniauth_create
auth = request.env["omniauth.auth"]
user = User.new
user.omniauth_login = 'true'
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) ||
User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to user
end
When I try to create a new user by logging in via twitter, I still get the validation error:
Validation failed: Password can't be blank, Email can't be blank, Email is not valid.
How do I skip validation of password and email if my object is being created in the create_with_omniauth method?
Thanks.
I think the problem here is that you have two separate instances of User, one that has omniauth_login set to 'true', and another that does not. The first is gotten from
user = User.new
user.omniauth_login = 'true'
The second is
User.create_with_omniauth(auth)
. The second instance created here doesn't have omniauth_login set to 'true', so it still runs the validations. Try this instead
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) ||
User.create_with_omniauth(auth)
and in create_with_omniauth, add user.omniauth_login = 'true'
.
I'm trying to get Koala to work with Omniauth. A User model logs in with Facebook using Omniauth and I want to use Koala as a client to pull the list of a user's friends that are using the app. I don't seem to be saving the tokens properly:
Controller
#friends = Array.new
if current_user.token
graph = Koala::Facebook::GraphAPI.new(current_user.token)
#profile_image = graph.get_picture("me")
#fbprofile = graph.get_object("me")
#friends = graph.get_connections("me", "friends")
end
DB Schema
create_table "users", :force => true do |t|
t.string "provider"
t.string "uid"
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
end
User model has
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["user_info"]["name"]
end
end
Koala.rb initializer has:
module Facebook
CONFIG = YAML.load_file(Rails.root.join("config/facebook.yml"))[Rails.env]
APP_ID = CONFIG['app_id']
SECRET = CONFIG['secret_key']
end
Koala::Facebook::OAuth.class_eval do
def initialize_with_default_settings(*args)
case args.size
when 0, 1
raise "application id and/or secret are not specified in the config" unless Facebook::APP_ID && Facebook::SECRET
initialize_without_default_settings(Facebook::APP_ID.to_s, Facebook::SECRET.to_s, args.first)
when 2, 3
initialize_without_default_settings(*args)
end
end
alias_method_chain :initialize, :default_settings
end
Sessions controller has:
def create
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
session['fb_auth'] = request.env['omniauth.auth']
session['fb_access_token'] = omniauth['credentials']['token']
session['fb_error'] = nil
redirect_to root_url
end
The problem like you already know is that the fb_access_token is only available in the current session and not being available to Koala.
Does your user model have a column to store "token"? If not, then make sure you have that column in the user model. When you have that column in the user model, you will need to store something in it at the time you create the user (create_with_omniauth method in the User class). After successfull authorization from facebook you should find that the token field is populated with the facebook oauth token. If it is populated, then your Koala code should work. In this case there is no need to store the facebook credentials in the session.
If however you are not getting offline access from Facebook (which means the access is only provided for a short duration, then storing the facebook credentials in the session makes sense. In this case you should not use "current_user.token" but session["fb_auth_token"] instead with Koala.
Hope this helps!
So if you want offline access (long term storage of facebook authorization), change your model code to store fb_auth_token as below
# User model
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["user_info"]["name"]
user.token = auth['credentials']['token']
end
end
# SessionsController
def create
auth = request.env["omniauth.auth"]
user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
# Note i've also passed the omniauth object
session[:user_id] = user.id
session['fb_auth'] = auth
session['fb_access_token'] = auth['credentials']['token']
session['fb_error'] = nil
redirect_to root_url
end
If you have short term access then change your "other" controller to use sessions
# The other controller
def whateverthissactionis
#friends = Array.new
if session["fb_access_token"].present?
graph = Koala::Facebook::GraphAPI.new(session["fb_access_token"]) # Note that i'm using session here
#profile_image = graph.get_picture("me")
#fbprofile = graph.get_object("me")
#friends = graph.get_connections("me", "friends")
end
end
Avoid writing when you can just test this out:
https://github.com/holden/devise-omniauth-example/blob/master/config/initializers/devise.rb
I've used that app as a basis with success.
I've fixed a few issues but haven't committed on github however. But those are very minor. I think the example app works.
Your problem may not be Koala but the fact that the token was not saved so you can't query anything or even connect to Facebook.
Your issue looks to be that you're passing the wrong thing into Koala:
if #token = current_user.token
#graph = Koala::Facebook::GraphAPI.new(oauth_callback_url)
#friends = #graph.get_connections("me", "friends")
end
Try changing it to the following:
#friends = Array.new # I think it returns a straight array, might be wrong.
if current_user.token
graph = Koala::Facebook::GraphAPI.new(current_user.token)
#friends = graph.get_connections("me", "friends")
end