Facebook app: within Canvas, Facebook treats GETs as POSTs? - ruby-on-rails

In my Rails FB app, when running in the canvas, if I go to "/users", instead of GETting the index page, it POSTs and creates a new user.
That only happens when I try to access the URL directly. When I use the "List of users" link on the app, it works as expected.
Why is that?

that's just how FB does it. They post data with each query as well.

Facebook sends everything as POST which brakes RESTful routes. There is a way to fix it though. If incoming POST request contains signed_request parameter you can assume it was converted from GET to POST by Facebook.
Rack::Facebook::MethodFix middleware fixes the problem automagically. You can use it with something like:
# Basic usage
use Rack::Facebook::MethodFix
# Also validate signed_request
use Rack::Facebook::MethodFix, :secret_id => "c561df165eacdd6e32672c9eaee10318"
# Do not apply request method fix to admin urls.
use Rack::Facebook::MethodFix, :exclude => proc { |env| env["PATH_INFO"].match(/^\/admin/) }
or if you are using Rails then
config.middleware.use Rack::Facebook::MethodFix

Related

What is Facebook callback_url and how to use it in rails?

I am using the Facebook Graph API in my rails projects, no matter I use oauth2 gem or koala, It need callback_url
Oauth2
token = client.auth_code.get_token('code_value', :redirect_uri => 'http://localhost:8080/oauth/callback')
Koala
#oauth = Koala::Facebook::OAuth.new(app_id, app_secret, callback_url)
I try to use http://localhost:3000/callback in my project, but it's not working.
Should I develop a routes for that?
like: get 'callback' => 'oauth#callback'?
What should I write in the callback method in OauthController, what does it use for? Thanks
Yes, you should.
Basically, OAuth uses callback data to provide tokens for authenticating users.
For example
user clicks on "sign in" (or whatever) link and your app redirects they to the OAuth provider (or open it in the iframe).
user permits to your app to use they profile details
OAuth provider send callback to your app with unique code
app uses that code to get secure access token for API communications
That's just a basic example.
In your case you need to implement controller that will parse callback data.
Here is the code example
#oauth = Koala::Facebook::OAuth.new(api_key, app_secret, callback_url)
=> #<Koala::Facebook::OAuth:0x007fc919d014e0 #app_id=1234567890, #app_secret="FaKeAppSecretKey", #oauth_callback_url="http://localhost:3000/callback">
#oauth.url_for_oauth_code
=> "https://www.facebook.com/dialog/oauth?client_id=893637180663238&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback"
And when you go to https://www.facebook.com/dialog/oauth?client_id=893637180663238&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback FB will redirect you to
http://localhost:3000/callback?code=CODE_FROM_CALLBACK
Then you should use implement controller that uses code to get access token
access_token = #oauth.get_access_token(params[:code])
=> "ACCESS_TOKEN"
#graph = Koala::Facebook::API.new(access_token)
=> #<Koala::Facebook::API:0x007fc91a903ae0 #access_token="ACCESS_TOKEN", #app_secret=nil>
profile = #graph.get_object("me")
=> {"id"=>"4492344324865", "email"=>"my_fake_email_address#gmail.com", "first_name"=>"Roman", "gender"=>"male", "last_name"=>"Sotnikov", "link"=>"https://www.facebook.com/app_scoped_user_id/4492344324865/", "locale"=>"en_US", "name"=>"Roman Sotnikov", "timezone"=>6, "updated_time"=>"2015-05-18T05:19:54+0000", "verified"=>true}
Please check https://github.com/arsduo/koala/wiki/OAuth for additional info.
Callback Url is yours applications url -- a GET route -- you want the third party application to redirect to, after its done its work.
So in your routes.rb file simply create a get route
get 'facebook_graph_callback', to: 'controller_name#action'
#A get route which is connected to a controller action
Usually the third party will give you some sort of information back. Quite often its some sort of code. In your controller action you can use find them in params hash.

dynamic callback with omniauth

I use omniauth for Facebook Authentification in my app. All works great. Now I try to implement a callback to a dynamic page which is the ID of an article.
The URL follows the pattern myapp.com/ads/:id/
On this page I use the auth gateway to /auth/facebook
The callback should come back exactly to this page (e.g. /ads/4711/).
I tried to accomplish this in routes:
match "/auth/facebook/callback" => "/ads/:id/"
which is obv not valid.
Also tried to work out this solution OmniAuth dynamic callback url to authenticate particular objects instead of current_user with no success.
Any help is appreciated.
Found the solution via Devise + Omniauth - How to pass extra parameters along?
I can easily pass the ID as a param to the auth page and get it back via request.env['omniauth.params']

Storing user_likes with the Omniauth-Facebook gem

Does anyone know where in the Auth Hash the user_likes are stored?
I've tried different combos:
auth.extra.raw_info.user_likes
auth.extra.raw_info.likes
I've got the permission, I just don't know how to get to the user_likes array.
Omniauth-Facebook gem
After some time (edit)
Alternatively I could do something like this using the HTTParty gem:
url = "https://graph.facebook.com/#{auth.uid}/likes&access_token=#{auth.credentials.token}"
#likes = HTTParty.get(url)
But I really want to know if its possible through the omniauth authentication process...
The omniauth-facebook gem uses the /me call to fill the raw_info hash. Per the facebook docs http://developers.facebook.com/docs/reference/api/user/, likes are not included in the user object but are a connection that can be accessed by calling https://graph.facebook.com/me/likes.
Hope this response though late, helps someone who is trying to figure out what is inside the different hashes part of auth hash object (schema reference: https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema)
Instead of trying different combinations (as posted in the question), one could simply render the object as an yaml to browser. Now page source of the browser output would give a clear picture of what is returned part of the auth object. Other option is to put a debug break and inspect the auth object in the callback controller (an ide would be very helpful in this case).
routes.rb
...
match '/auth/facebook/callback' => 'authentications#create'
...
authentications_controller.rb (or your controller that receives the call back)
class AuthenticationsController < ApplicationController
...
def create
# a simple code to understand the content of the auth object
render :text => request.env["omniauth.auth"].to_yaml
end
end

Passing parameters through OmniAuth

I need to pass some parameters to callback action. Judging from the source code, OmniAuth should add query string to callback URL but strangely it does not. When I open
/auth/facebook?from=partner
...and get redirected to Facebook, return_url is just
/auth/facebook/callback
...without any parameters.
After struggling with all the above answers, I figured out what to do regarding Facebook, which by default does not display the params in request.env["omniauth.auth"].
So -- If you are using a query string for the callback, similar to something like this:
"/auth/facebook?website_id=#{#website.id}"
The only way to get that website_id param is by using request.env["omniauth.params"]. NOTE: MAKE SURE YOU USE omniauth.params and not omniauth.auth -- this one tripped me up for a while.
Then, to test this out, you can inspect it within your controller action (notice the RAISE line...):
def create
raise request.env["omniauth.params"].to_yaml
# the rest of your create action code...
end
You should see your parameter there. Great. Now, go back to your controller and remove that RAISE line. Then, you can access the param as follows in your controller action:
params = request.env["omniauth.params"]
website_id = params["website_id"]
NOTE: in params["website_id"] you need to use quotes and NOT a symbol.
I guess the cookie thing works but why do all that when you can use the state variable as documented here: https://github.com/mkdynamic/omniauth-facebook
This is how I used it:
when creating the url you can just add state in the Query String and it will be available in the callback url as well.
user_omniauth_authorize_path(:facebook, :display => 'page', :state=>'123') %>
now the callback url will be
http://localhost:3000/users/auth/facebook/callback?state=123&code=ReallyLongCode#_=_
Now in the callback handler you can process the state
You can use the :params options, as in
omniauth_authorize_path(:user, :facebook, var: 'value', var2: 'value2' )
and later in the callback you can access request.env['omniauth.params'] to get the hash! :)
(copied from this answer)
What you want to do is dynamically set your callback to include the partner name in the url (not the url parameters), on a per authentication transaction basis, depending on which partner was involved. This means setting the callback url dynamically, for each authentication request. See this blog post to get started. The callback url automatically drops the url parameters, as you've noticed, so doing this with parameters won't work.
So, if instead of trying to pass the partner name/id in as a parameter (which is dropped), you structured your routes so that the partner_id and OmniAuth provider were part of the callback url, then you'd have something like:
/auth/:omniauth_provider/callback/:partner_id
...where a valid callback would be something like
/auth/facebook/callback/123456
...then you would know that a given callback came in from facebook, with partner id 123456
OmniAuth already has a built-in way to know where the user was, it's called "origin" as documented here:
https://github.com/intridea/omniauth/wiki/Saving-User-Location
You know, I think I might be trying to solve this the hard way.
Cookies might be the answer. I think you can solve this by having your login action store a cookie, and then redirecting to the proper /auth/:provider path for authentication, and when the callback is triggered (in SessionsController#create), you just read the cookie back to know where to redirect them to.
So, right now, your "login with facebook" link (or whatever you have you in your app) probably goes to /auth/facebook. Instead if you created a custom action like
POST /partner_auth
...and called it with the url...
POST example.com/partner_auth?from=partner&provider=facebook
Then you might have a controller like:
class PartnerAuth < ApplicationController
def create
cookies[:from] = params[:from] # creates a cookie storing the "from" value
redirect_to "auth/#{params[:provider]"
end
end
Then in the SessionsController#create action, you would have...
def create
...
destination = cookies[:from]
cookies[:from].delete
redirect_to destination # or whatever the appropriate thing is for your
# app to do with the "from" information
end
I tried to build a demo app to accomplish what I'd outlined in the other answer, but you're right - it was too complicated to try to dynamically inject a custom callback into the OmniAuth code. There is a configuration option to override the default callback, but it doesn't appear to be easy to set it dynamically.
So, it dawned on me that cookies would be way simpler, user-specific, and since you theoretically only need to store this from information for a very short time (between when the user tries to authenticate, and when the callback is triggered), it's no big deal to create a cookie, and then delete it when the callback gets hit.
Use the 'state' Variable. Facebook allows the user to set a STATE variable.
Here is how I did it, I appended the AUTH URL with ?state=providername
http://localhost/users/auth/facebook?state=providername
This param is returned to me at Callback as params['providername']
I devised the solution from the original Omniauth Path Method
user_omniauth_authorize_path(:facebook, :display => 'page', :state=>'123') %>

Utilizing A Devise Session Through Flex With Rails3-AMF

I have a Flex front end and a Rails 3 back-end that I would like to establish a session to. Before you can access the page embedded with the flex front-end, you are prompted with a html login page through Devise.
I am able to login fine through the html page but cannot get access to the session with Flex using amf requests.
I have the rails session token in flex but cannot pass them into rails correctly. I am attempting to pass the sessiontokin in through a "send" service call like
somethingService.new.send(session_id: '###', _csrf_token: '###' )
and rails is receiving the session param in a hash as like
{0=>{"session_id"=>'###')}}
instead of like
{"session_id"=>'###')}.
Any suggestions on how to fix this issue or to utilize a session with Flex/RubyAmf/Rails are welcomed.
Thx.
It's been a while since I've done anything with integrating flex & rails, but I have a vague memory that the params come through as {0 => params} if not explicitly mapped in the configuration. The rails3-amf readme on github uses this example:
config.rails3amf.map_params :controller => 'UserController', :action => 'getUser', :params => [:session_id]
If your not already, perhaps explicitly defining the session_id in the :params would make the difference?

Resources