how to pass username from omniauth to stripe customer create - ruby-on-rails

I'm trying to pass the username that is collected from Twitter or facebook when my users get authenticated and created, however a simple (description = user.username) doesn't seem to do it, here's what I've got so far;
def self.create_from_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.username = auth["info"]["nickname"]
#user.email = auth["info"]["email"]
user.save!
if user.save
Stripe::Customer.create(description = user.username)
end
user
end
end
I keep getting the following error, no matter if I use #user.username or even current_user.username.
undefined method `each' for "xhtmlit":String

I was completely doing it wrong which is why it was spitting out the error for me. The actual code might not be clean but I'm currently working to try and figure out from starting with dirty version to clean version. Here's what I've got so far that works;
if user.save
Stripe::Customer.create(:email => user.username)
end

Related

Rails: Cookie overflow with omniauth twitter sign up

I am using omniauth to let people sign up/sign in with Facebook and its working well ! But I wanted to add the omniauth-twitter gem to let them connect with Twitter.
I followed the same steps than when I set up the Facebook connect: https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
But when I signing up/in I get the following error:
ActionDispatch::Cookies::CookieOverflow in OmniauthCallbacksController#twitter
at the following URL:
http://localhost:3000/users/auth/twitter/callback?oauth_token=HRjON8J4bj9EcbjiELHcpHmSXo0cPd0wCHyuWG8ATZU&oauth_verifier=ZiZb1FAKZmNML1gVu5RKBLEGzbeAPPzC80QCpPDGU
I tried different things suggested on similar posts but none of these worked :(
Here is my configuration:
omniauth_callbacks_controller.rb => app/controllers/omniauth_callbacks_controller.rb
def twitter
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.find_for_twitter_oauth(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 => "twitter") if is_navigational_format?
else
session["devise.twitter_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
user.rb => app/models/user.rb
def self.find_for_twitter_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.name = auth.info.name # assuming the user model has a name
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.twitter_data"] && session["devise.twitter_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
devise.rb => app/config/initializers/devise.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :twitter, "KEY, "KEYPASSWORD
end
Any ideas what's wrong?
As Michael says in the comments, you're storing a large hash in the session and it's too big (you're using the default CookieStore and cookies can only contain 4KB of data). That hash provided by omniauth has all the data returned by twitter, which can be quite a lot. For example, see the README: https://github.com/arunagw/omniauth-twitter#authentication-hash
If the code in your question is all the code relating to twitter login, then it looks like you only need to keep the email in the session as that is all that is used by your new_with_session code. So your line in the else in twitter which is:
session["devise.twitter_data"] = request.env["omniauth.auth"]
could be something like:
session["devise.twitter_data"] = request.env["omniauth.auth"].select { |k, v| k == "email" }
However the major flaw with this is that twitter doesn't return an email address for a user, so data["email"] will always be nil in new_with_session anyway! So it's pointless keeping anything in the session if you are only later interested in the email which is never returned by twitter. Perhaps you instead want to retrieve a name to help prefill the registration form instead of the email address. In this case, you could just keep that in the hash from omniauth. If you want to keep a few things in the hash, then instead of selecting them all to put in the session, you could do something like:
session["devise.twitter_data"] = request.env["omniauth.auth"].delete_if("extra")
which will remove the "extra" nested hash which could help everything else to fit in the session.
For a complete solution you'll have to consider messy situations like dealing with people who have signed in with Facebook and then come and sign in with Twitter and want to use the same email address and merge with their existing account on your system.
In any case, note that if you are using Rails 3 then the session cookie is not encrypted so the user or anyone with access to their computer could read the contents of the cookie with whatever data from twitter you end up keeping in there. If you're using Rails 4, then the cookie should be encrypted to protect against that.

registration of new users with Devise and omniauth-google-oauth2

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

What is the simplest way to add an profile image with the Omniauth-Facebook Gem?

I'm working with the Omniauth Facebook Gem with Rails and need a profile pick on my app for each user.
Is it as simple as retrieveing the Name of the user from facebook with the Gem, or does it need to be uploaded to Amazon S3 servers etc.?
My user model:
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
Can't find a clear answer.
Updated:
In my show page it only shows the url:
<b>IMAGE:</b> <%= #user.image %>
Thanks
It is simple to do. And you don't require to store image on your server. You can simply fetch it from facebook.
Here is how I do it:
Add an image field in your User model:
rails generate migration addImageToUsers image:string
Add it attr_accessible list.
Then in your above method, add following listing for image:
user.image = auth.info.image
This is the direct url of where facebook stores the image of the user.
You can inspect your auth hash to study different size of user image.

Rails unusual statement: where( ).first_or_create( )

I am following Ryan's Omniauth with Devise railscast. A part of the code was:
class User < ActiveRecord::Base
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
end
I am scratching my head with where().first_or_create do. What does this code exactly do?
My guess is that where() statement is applied to class User. But how does Rails know that it is equivalent to User.where(:provider => "provider_id passed by auth", :uid => "uid passed by auth")?
My guess is that it yields a collection, then if collection is empty, then create a new instance user and assign new attributes. Else pick the first instance and reassign attributes.
Are my guesses correct?
Let's slice this up into parts.
auth.slice(:provider, :uid)
auth is a hash that contains a :provider and :uid keys. calling .slice to auth returns a new hash with only these as keys. ie
auth = { provider: 'foo', uid: 'bar', some_other_key: 'blah' }
new_hash = auth.slice(:provider, :uid) # { provider: 'foo', uid: 'bar' }
first_or_create
is the equivalent of find_or_create_by. You can read the documentation and see that it searches for any record that matched the options passed to where. If no record is found, it tries to create a record with those options as attributes.
the block passed
user.provider = auth.provider
user.uid = auth.uid
user.username = auth.info.nickname
actually only needs the last line. The first 2 lines are redundant because it is in the where option.

How to get Koala to play nicely with Omniauth?

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

Resources