In the past I have use facebooker to integrate with facebook, so I already had a facebook.yml setup with all the configuration information for my facebook app:
development:
application_id: XXXXXXXXXXXX
api_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
secret_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
canvas_page_name: blah
callback_url: http://localhost:3000/
pretty_errors: true
set_asset_host_to_callback_url: false
tunnel:
public_host_username:
public_host:
public_port: 4007
local_port: 3000
server_alive_interval: 0
The first step is to add an endpoint to start the first leg of the OAuth.
def new_facebook
facebook_settings = YAML::load(File.open("#{RAILS_ROOT}/config/facebooker.yml"))
redirect_to "https://graph.facebook.com/oauth/authorize?client_id=#{facebook_settings[RAILS_ENV]['application_id']}&redirect_uri=#{APP_URL}/facebook_credentials/facebook_oauth_callback&scope=offline_access,publish_stream"
end
Here we are doing a lot of things. First we make sure that Facebook returns us the "code" parameter, we need this to complete the authentication. Once we have the code, we then need to request an "access_token" from Facebook. This was tricky for me, because the url is an SSL/HTTPS url and normal httparty or net/http tricks didn't work. Eventually I got it worked out as can be seen in the code above. Once we get the "access_token" we are pretty much done, in this case I go ahead and get the current user's Facebook data by hitting "https://graph.facebook.com/me?access_token=XXXXXXXXXXXXX" and then populate an ActiveRecord object with the data I receive (including the "access_token") for later use.
def facebook_oauth_callback
if not params[:code].nil?
facebook_settings = YAML::load(File.open("#{RAILS_ROOT}/config/facebooker.yml"))
callback = "#{APP_URL}/facebook_credentials/facebook_oauth_callback"
url = URI.parse("https://graph.facebook.com/oauth/access_token?client_id=#{facebook_settings[RAILS_ENV]['application_id']}&redirect_uri=#{callback}&client_secret=#{facebook_settings[RAILS_ENV]['secret_key']}&code=#{CGI::escape(params[:code])}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = (url.scheme == 'https')
tmp_url = url.path+"?"+url.query
request = Net::HTTP::Get.new(tmp_url)
response = http.request(request)
data = response.body
access_token = data.split("=")[1]
url = URI.parse("https://graph.facebook.com/me?access_token=#{CGI::escape(access_token)}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = (url.scheme == 'https')
tmp_url = url.path+"?"+url.query
request = Net::HTTP::Get.new(tmp_url)
response = http.request(request)
user_data = response.body
user_data_obj = JSON.parse(user_data)
flash[:notice] = 'Facebook successfully connected.'
@social_credential = SocialCredential.create_or_find_new_facebook_cred(access_token, session['rsecret'])
end
end
Keeping the "access_token" is important, because in my case it lets me update the users stream whenever I need to (hence the "offline_access" in the initial request).
I know from my Twitter stream that there is work on wrapping the Facebook OpenGraph API in ruby but I don't know where it stands. It is a pretty simple api, and won't require much, but it would be nice to have it abstracted some.
Okay, cheers and jeers in the comments please.
I love you!!!
ReplyDeleteYou rock! :)
ReplyDeleteOne question if I may - is there a way to ask user to authenticate each time when I need them authorized?
ReplyDeleteit doesnt work for me. i get this as the response:
ReplyDeleteresponse = http.request(request)
(rdb:7) n
warning: peer certificate won't be verified in this SSL session
data = response.body
(rdb:7) n
access_token = data.split("=")[1]
(rdb:7) data
"{\"id\":\"https:\\/\\/graph.facebook.com\\/oauth\\/access_token\"}"
so the data object doesnt have anything at index 1.
@Sam, I think you can check to see if the session is still any good by trying to get the person's feed (and seeing if you get a return, there maybe better endpoints to hit to check to see if the session is still good). If you get a good return (meaning good data), then you don't need to reauth.@Sam, I think you can check to see if the session is still any good by trying to get the person's feed (and seeing if you get a return, there maybe better endpoints to hit to check to see if the session is still good). If you get a good return (meaning good data), then you don't need to reauth.
ReplyDeleteGuessing that the output format has changed, I'd try data.split("=")[0] or data.split("=")[2]...
ReplyDelete"In the past I have use facebooker to integrate with facebooker" - I guess you mean "with facebook" :-)
ReplyDeleteIndeed I did, updating the post now.
ReplyDeleteBTW while I am on this post, @Gladiator I am not sure if Facebook was just down, but the code as is does work, I verified it last night.
ReplyDeletejust getting my head around RoR - may seem silly, but i've put this in a 'services' controller (i am storing multiple social network tokens per person- so the toke will be associated with a user id in the services model) - but can't seem to call it - sure i'm being stupid, but any help for a beginner would be greatly appreciated!
ReplyDeletetesting
ReplyDeleteHi Jamie,
ReplyDeletethanks a lot for posting this.
I don't understand why but what I get for "data = response.body" is
{ "error": { "type": "OAuthException", "message": "Error validating verification code." }}
I don't understand what happens.
when I just try the url in my browser with all the parameters until the access code, I can get the access token, but not this way.
on the other hand, when I try the same url with a browser with which I'm logged in into Facebook with a different user [but I reuse the code I got for the previous user], then I get the exact same error ["Error validating verification code."]. So I wonder if there's an issue with regards to the server 'not being logged in as the user who got the code'
I tried hard to make this work and couldn't figure it out. Any help would be super appreciated
Thanks!
Pierre
this is the code I use in my controller [after I logged on the client side - which successfully gives my controller a code]
------------------------------------------------------------------------------------------------
@access_code = params[:code]
url = URI.parse("https://graph.facebook.com/oauth/access_token?client_id=MYCLIENTID&redirect_uri=MYREDIRECTURI&client_secret=MYAPPSECRET&code=#{CGI::escape(@access_code)}") http = Net::HTTP.new(url.host, url.port) http.use_ssl = (url.scheme == 'https') tmp_url = url.path+"?"+url.query request = Net::HTTP::Get.new(tmp_url) response = http.request(request) data = response.body @access_token = data.split("=")[1] @test = data
------------------------------------------------------------------------------------------------