I'm having trouble using omniauth & twitter gem to generate tweets.
I have been using this tutorial http://blog.assimov.net/post/2358661274/twitter-integration-with-omniauth-and-devise-on-rails-3
and I can log in using twitter, generate authentications but I cannot update tweets.
current_user.twitter.update("My Rails 3 App with Omniauth, Devise and Twitter")
This line complains about the method update.
What confuses me about the tutorial above is that at the beginning they generate a migration to store the secret in authentications, but at no point later in the tutorial is any mention of changing any code to write the secret to the database.
My understanding is that the secret is obtained from the omniauth hash that is stored in the session cookie. What am I missing here?
def hash_from_omniauth(omniauth)
{
:provider => omniauth['provider'],
:uid => omniauth['uid'],
:token => (omniauth['credentials']['token'] rescue nil),
:secret => (omniauth['credentials']['secret'] rescue nil)
}
end
So everything seems to be working apart from creating the Twitter Client hence not having the update method available?
current_user.twitter.update("first tweet")
The twitter method here should be creating the Twitter Client
def twitter
debugger
unless #twitter_user
provider = self.authentications.find_by_provider('twitter')
#twitter_user = Twitter::Client.new(:oauth_token => provider.token, :oauth_token_secret => provider.secret )rescue nil
end
#twitter_user
end
I'm sorry I'm not great at explaining the problem. Any help greatly appreciated.
Thanks
L
If you only need to post to twitter, it might just be easier to forgo the twitter gem completely. The consumer / access token generation requires the app id and secret as well as the user's access and secret tokens. I found this to be easy enough to implement that I thought the overhead of the twitter gem wasn't necessary.
module User::Social
def self.included(base)
base.instance_eval do
include Rails.application.routes.url_helpers
end
end
def promote_activity(type, profile)
url = short_profile_url(profile, :host => Conf.domain)
tw_client.request(:post, "http://api.twitter.com/1/statuses/update.json", :status => I18n.translate("tweets.#{type}", :profile => profile.to_s, :url => url))
end
def tw_client
#tw_client ||= begin
consumer = OAuth::Consumer.new(Conf.tw_app_id, Conf.tw_secret, :site => 'http://api.twitter.com')
OAuth::AccessToken.from_hash(consumer, {:oauth_token => self.access_token, :oauth_token_secret => self.secret_token})
end
end
end
class User < AR::Base
include User::Social
end
Related
I'm trying to add authentications controller for my current devise system, in order to provide multiple logins with facebook and twitter.
To do that, I'm following this tutorial: http://railscasts.com/episodes/236-omniauth-part-2
My problem is, for the person, who hasn't registered yet, and trying to register with twitter.
So I need to create both user and authentication for that.
My code is the following:
user = User.new
token = omni['credentials'].token
token_secret = omni['credentials'].secret
user.provider = omni.provider
user.uid = omni.uid
user.authentications.build(:provider => omni['provider'], :uid => omni['uid'], :token => token, :token_secret => token_secret)
if user.save
flash[:notice] = "Logged in."
sign_in_and_redirect(:user, user)
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_path
end
So at the end of the registration process, the new user is created. However in the database, I don't see any twitter authentication record with respect to that user.
Is that because of the user.authentications.build ?
That would be great if you can help me.
Thanks.
As a data point: The railscasts you're referring to references Omniauth pre-1.0, which had a slighly different strategy than what that railscsts reference. (Note: I'm using the exact method you're referencing on a live site ). In this case, the build calls "apply_omniauth" -
Make sure you've created (as they reference in the video), a registrations controller which builds the resource. Here is my current working example:
class RegistrationsController < Devise::RegistrationsController
def create
super
session[:omniauth] = nil unless #user.new_record?
end
private
def build_resource(*args)
super
if session[:omniauth]
# apply omniauth calls the user model and applies omniauth session to the info
#user.apply_omniauth(session[:omniauth])
#
#user.valid?
end
end
end
However, you still need to create the authentication record, here is my exact call:
current_user.authentication.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
Hope it helps.
Yes, it is because of build
User.build # allocates a new record for you
User.create # allocates and then saves a new record for you
So I think you want
user.authentications.create(:provider => omni['provider'],
:uid => omni['uid'],
:token => token,
:token_secret => token_secret)
In addition, you should handle the case where the create does not save (validation problem)
I suppose if you are using Devise+Omniauth , you could take a look at this more recent Railscast. There is a native support of OmniAuth in the new version of Devise gem .
Yes it is because of build, it is use to build a record without saving it in the database (like new).
If in your model you have a User has_many :authentications , you can set the autosave option to true to automatically save the authentications when you are saving the user :
has_many :authentications, autosave: true
I've set up authentication with the Foursquare API and my Rails app, and now it's time to add functionality. I am not too proficient with Rails - it is not my first language. I want to allow a merchant to connect with Foursquare (Working) and then direct the merchant to a page where they can see unique visitors to their venue, the number of checkins to the venue, and who the mayor of the venue is. I know what endpoints to use, I'm just not sure how to implement them in rails. (Access Token, etc.)
Thanks!
After Answer
I'm trying to implement Turd Ferguson's answer, but I'm not getting anywhere. I keep getting an error saying the method is undefined. I want to try a simple venue search as soon as the user is authenticated. (Create Action)
Also, I'm using OmniAuth for authentication.
sessions_controller.rb
class SessionsController < ApplicationController
require 'foursquare'
def create
auth_hash = request.env['omniauth.auth']
venues = Foursquare::search_venues("starbucks")
render :text => venues
end
def failure
end
def destroy
session[:user_id] = nil
render :text => "Logged out!"
end
def callback
code = params[:code]
#access_token = foursquare.access_token(code, callback_session_url)
session[:access_token] = #access_token
redirect_to examples_path
end
end
foursquare.rb
class Foursquare
def self.search_venues(text)
client.search_venues(:ll => '36.142064,-86.816086', :query => text)
end
def self.client
#client ||= Foursquare2::Client.new(:client_id => '0YO3F0JNZIPVKG1DE01MNPB132D4JZ0QYRQSOWTZQKHHOPKB', :client_secret => 'GMBOGWUNL2GIKZZXQPSLE4BMFNGB5LDHQREH2UKUCK1TJ1L0')
end
end
Have you looked into using a gem such as foursquare2?
Using the gem you could create a Foursquare class like:
class Foursquare
def self.search_venues(text)
client.search_venues(:ll => '36.142064,-86.816086', :query => text)
end
def self.client
#client ||= Foursquare2::Client.new(:client_id => 'your_client_id', :client_secret => 'your_secret')
end
end
You could then call this anywhere you wanted by doing something like:
venues = Foursquare::search_venues "foobar"
In a recent project, facebook Users can login using their Facebook UID to upload picture submissions based on file uploads or uploads from their personal albums etc.
Everything works quite nice on my local system in the development environment. Login via Facebook, Logout, Upload - all great.
In production though I'm facing a unknown and hard to debug problem. It seems that every once in a while (actually reproducable when uploading a new Submission to the system) the session is lost, the picture is NOT uploaded and the facebook user is logged out (!).
I'm using devise and omniauth. Omniauth is integrated into Devise.
Following is all the code that touches Devise/Omniauth or the User.
app/models/user.rb
class User < ActiveRecord::Base
devise :omniauthable, :rememberable, :omniauth_providers => [:facebook]
def self.create_with_omniauth(auth)
u = User.find_by_uid(auth["uid"])
return u unless u.nil?
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["user_info"]["name"]
user.email = auth['user_info']['email']
end
end
def after_signin_path
'/competition'
end
end
Database contains all needed fields for :rememberable, I hope.
app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model
#user = User.create_with_omniauth(env["omniauth.auth"])
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
#user.update_attributes!(:current_auth_token => env["omniauth.auth"]['credentials']['token'], :last_language => I18n.locale.to_s, :updated_at => Time.now, :remember_created_at => Time.now)
sign_in_and_redirect(:user, #user)
else
redirect_to '/competition'
end
end
protected
def after_omniauth_failure_path_for resource
'/competition'
end
end
config/initializers/devise.rb
OmniAuth.config.full_host = "http://#{APP_CONFIG[:domain]}"
Devise.setup do |config|
config.mailer_sender = "devise#myapp.host.com"
require 'devise/orm/active_record'
config.stretches = 10
config.encryptor = :bcrypt
config.timeout_in = 3.days
config.pepper = "2a4b8b2ed9e12e553a7a542176f2ace1af62c062f3ba203a590b8b6307f33042b394922807a840004a3dcdf1c4e97ae085fe2c29654ddaeab7c60f431a8078abb"
config.omniauth :facebook, APP_CONFIG[:facebook_app_id], APP_CONFIG[:facebook_app_secret], {
:scope => "email,user_photos,user_photos,publish_stream,offline_access",
:client_options => {
:ssl => {
:ca_file => "/etc/pki/tls/certs/ca-bundle.crt"
}
}
}
end
There are no auth-related methods in application_controller.rb.
routes.rb:
The interesting part below:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
match '/logout_fb' => 'start#logoutfb'
authenticate :user do
get '/users/connect/:network', :to => redirect("/users/auth/%{network}")
end
Somehow I cannot get to understand the authenticate block, which according to another post should be helpful.. ideas on this too?
So many theories:
One is that the facebook function in the omniauth_callbacks_controller runs aside of the users' session, and hence sign_in_and_redirect won't work. So I had the idea of redirecting to another page like '/auth?uid=xxx' but this sounds both wrong, insecure and not stable.
Any help or hints are appreciated!
A bit of a long shot but try turning off protect_from_forgery - I had some issues with sessions disappearing and it turned out to be the issue discussed here https://github.com/intridea/omniauth/issues/203
In my config/initializers/omniauth.rb, I had to add the following:
OmniAuth.config.full_host = "http://yourdomain.com" # Or have an environment specific URL.
You are using devise but you are not using it's own helpers. For instance, you've defined your own current_user method. To be honest, I can't see any obvious mistakes you've made, so it's just a desperate tip.
what kind of a session store do you use locally and what in production?
When you say "facebook user is logged out", this user is still logged in to facebook, but lost his session at yourapp.com ?
Are you sure that user.id is never nil or that you anywhere else than in .destroy set session[:user_id]= some_nil_variable ?
I am developing my Rails 3 application that uses Twitter OAuth and I am getting troubles because apparently I can't get the access_token, after clicking 'Allow' and Twitter redirecting me back to my application url, when I go to twitter.com/settings/connections I can't see my app there as authorized. I guess there is something wrong in my controller, I hope you can point them:
class OauthController < ApplicationController
def start
request_token = client.get_request_token(:oauth_callback => 'http://localhost:3000')
session[:request_token] = request_token.token
session[:request_token_secret] = request_token.secret
redirect_to request_token.authorize_url
end
def callback
#access_token = client.get_access_token(:oauth_verifier => params[:oauth_verifier])
render :json => access_token_get('https://api.twitter.com/account/verify_credentials.json')
end
protected
def client
#consumer = OAuth::Consumer.new(
'key','secret',
:site => 'https://api.twitter.com',
:authorize_url => 'https://api.twitter.com/oauth/authorize',
:access_token_url => 'https://api.twitter.com/oauth/access_token'
)
end
end
Please help, tell me where is my mistake, thanks for the attention!
Rodrigo Alves Vieira.
I'm not exactly sure what part of your code isn't working, probably something to do with the access_token_get method, but I'll show you how I did it--maybe it'll help..
after the line where you initialize #access_token, I do something like this:
#response = client.request(:get, "/account/verify_credentials.json", #access_token, { :scheme => :query_string })
case #response
when Net::HTTPSuccess
user_info = JSON.parse(#response.body)
unless user_info['screen_name']
# authentication failed, error handling
end
# We have an authorized user, save the information to the database using #access_token.token and #acess_token.secret
else
# error handling
end
(oh, and i was using the json gem, so make sure to gem install json and require 'json' at the top)
Hope that helps!
I have a RESTful Rails application with a resource called "Foo". I'm trying to use REST Client to do a put:
resource = RestClient::Resource.new 'http://localhost:3000/foos/1', :user => 'me', :password => 'secret'
resource.put :name => 'somethingwitty', :content_type => 'application/xml'
But my app raises:
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'
It seems like my app isn't getting the message that this is an XML request and that the AuthenticityToken should be ignored. Maybe I'm not using REST Client correctly. Any ideas on why I'm getting the exception?
Try putting an :only => [:update, :delete, :create] on the protect_from_forgery line in your application controller.
More info: http://ryandaigle.com/articles/2007/9/24/what-s-new-in-edge-rails-better-cross-site-request-forging-prevention
Use something like:
resource.put '<foo><name>somethingwitty</name></foo>', :content_type => 'application/xml'
I think you need to make two changes;
(a) Use the rails routing to tag this as an XML request
(b) Use HTTP Basic Authentication to authenticate the request.
This means changing your URL above to include the username, password like this
me:secret#localhost:3000/foos/1.xml
also note .xml bit
I guess that somewhere on your server-side you have code that authenticates in-bound requests via a before filter. This needs to work something like this ...
#
# If you haven't authenticated already then you are either
# reqirected to the logon screen (for HTML formats) or
# the browser prompts you. You are always allowed to pass
# the username/password in the URL
#
def login_required
#current_user = valid_session?
unless #current_user
if params["format"]
#
# If you specify a format you must authenticate now
#
do_basic_authentication
else
display_logon_screen
end
end
end
#
# Ask Rails for the login and password then authenticate as if this
# were a new login.
#
def do_basic_authentication
user = authenticate_with_http_basic do |login, password|
User.authenticate(login, password)
end
if user
current_user(#current_user = user)
else
request_http_basic_authentication
end
end
That's from our own app and is triggered by a before_filter in ApplicationController.
Also, I don't think you need the :content_type => 'application/xml'. What I normally do is just call post or put directly like this ..
response = RestClient.post URI.encode(url), :record => args
where the url contains the basic authentication and the ".xml"
Happy coding
Chris
Since your application is a Rails app, it might be easier to use ActiveResource for the client.
Something like:
require 'active_resource'
class Foo < ActiveResource::Base
self.site = 'http://localhost:3000/'
end
foo = Foo.new(:name => 'somethingwitty')
foo.save
You can read up on how to do the authentication on the rdoc site.