Closed medinarodel closed 12 years ago
I guess you are missing to pass the same params when exchanging the given authorization code with an access token. At token endpoint (= when exchanging code with token), you need to put exact the same URL which you put in authorization URL. Below is a sample code to enable it.
def client
FbGraph::Auth.new(FACEBOOK[:id], FACEBOOK[:secret], :redirect_uri => enter_url(todo: todo))
end
def authz_url
client.authorization_uri(:scope => [:email, :offline_access, :publish_stream, :user_location, :user_interests])
end
def exchange_code(code)
client.authorization_code = code
client.access_token! :basic_auth
end
Wow, thanks for the reply. I understand that I need to put exact the same URL which I put on the authorization URL.
Is there a difference between this:
def client fb_auth = FbGraph::Auth.new(FACEBOOK[:id], FACEBOOK[:secret]) client = fb_auth.client client.redirect_uri = enter_url(todo: todo) end
and this:
def client FbGraph::Auth.new(FACEBOOK[:id], FACEBOOK[:secret], :redirect_uri => enter_url(todo: todo)) end
And also, should I use :basic_auth instead of the one your provided on your docs :client_auth_body
Both are the same, if you're touching FbGraph::Auth#client
(= Rack::OAuth2::Client
) directly.
And sorry, please use :client_auth_body
or anything other than :basic
. It's my mistake.
FB doesn't support putting client credentials in basic authentication header.
def client(todo) FbGraph::Auth.new(FACEBOOK[:id], FACEBOOK[:secret], :redirect_uri => enter_url(todo: todo)) end
def authz_url(todo) client(todo).authorization_uri(:scope => [:email, :offline_access, :publish_stream, :user_location, :user_interests]) end
Let us say enter_url = "/enter"
Based from the code above, I am redirecting my Login via Facebook using authz_url(users_path). Facebook will then redirect it back to my callback URL which should be: "/enter?todo=[encoded users_path]&code=[FROM_FACEBOOK]"
Here is the code for my callback which is still causing the error:
@client = client(params[:todo]) #I also tried URI.decode(params[:todo]) @client.authorization_code = code @client.access_token! :client_auth_body
I assume params[:todo]
is nil
when you get the authorization code via redirect, since it's not the same HTTP request anymore.
To do so, you'll need to store the params[:todo]
value in session or cookie before redirecting to FB's authorization endpoint.
BTW, OAuth 2.0 defines state
parameter to identify additional session info, and FB also support it.
https://developers.facebook.com/docs/reference/dialogs/oauth/
Using state
parameter, the code could be
class FBOAuthController
def start
state = SecureRandom.hex(8)
session[state] = params[:todo]
redirect_to client.authorization_uri(:state => state, :scope => YOU_DEFINE)
end
def callback
client.authorization_code = params[:code]
todo = session[params[:state]]
token = client.access_token! :client_auth_body
# TODO: More work here
end
private
def client
@client ||= FbGraph::Auth.new(
FACEBOOK[:id],
FACEBOOK[:secret],
:redirect_uri => callback_url # no query params needed here.
).client
end
end
Of course, you can put params[:todo]
value itself in the redirect_uri query as you are doing now though.
ps.
Main purpose of state
is avoiding CSRF attack on your callback endpoint.
So if you use state
correctly, you'll get a security advantage too.
ref.) http://tools.ietf.org/html/draft-ietf-oauth-v2-30#section-10.12
BTW, the most simplest & secure way would be using FB JS SDK & XFBML Login Button.
ref.) https://developers.facebook.com/docs/reference/plugins/login/ http://facebook.stackoverflow.com/questions/5200167/how-to-let-facebook-login-button-redirect-to-a-particular-url https://github.com/nov/fb_graph/wiki/Authentication
the params[:todo] is being passed together with the params[:code] and it is not nil BTW, my original implementation was Javascript SDK but I encountered reoccurring issue for cookie not found so I changed it to OAUTH which I found more stable but I just encountered this reported problem if I added another params on redirect
Ah, you are right. I was confusing.
Then we probably need to check the exact redirect_uri
value.
Can you put this file in your rails project?
Then you'll see raw HTTP request & response in your rails log.
https://github.com/nov/fb_graph_sample/blob/master/config/initializers/debugger.rb
ps. I modified my fb_graph_sample app to use query parameter in redirect_uri as below, and it's working fine. https://github.com/nov/fb_graph_sample/
class FacebooksController < ApplicationController
:
# handle Normal OAuth flow: start
def new
client = Facebook.auth(callback_facebook_url(hoge: "http://example.com")).client
redirect_to client.authorization_uri(
:scope => Facebook.config[:scope]
)
end
# handle Normal OAuth flow: callback
def create
client = Facebook.auth(callback_facebook_url(hoge: "http://example.com")).client
client.authorization_code = params[:code]
access_token = client.access_token! :client_auth_body
user = FbGraph::User.me(access_token).fetch
authenticate Facebook.identify(user)
redirect_to dashboard_url
end
:
end
Is this issue still open?
Hi! Sorry I was not able to verify this since I changed my implementation.
ok, then I close this issue for now.
Thanks for all your amazing work on fb_graph. Sorry to open this issue again, but I'm using fb_graph on a production server with 100s of signups a day and a clear (and seemingly) random 5% of our users are getting this error.
I save the redirect_uri in the session, so it's always exactly the same. I'll really appreciate your help with this.
Gem versions: fb_graph v2.5.2 rack v1.1.0
Backtrace: "Rack::OAuth2::Client::Error"
/usr/local/lib/ruby/gems/1.8/gems/rack-oauth2-0.14.4/lib/rack/oauth2/client.rb:110:in handle_error_response' /usr/local/lib/ruby/gems/1.8/gems/rack-oauth2-0.14.4/lib/rack/oauth2/client.rb:87:in
handle_response'
/usr/local/lib/ruby/gems/1.8/gems/rack-oauth2-0.14.4/lib/rack/oauth2/client.rb:61:in access_token!' [RAILS_ROOT]/app/controllers/facebook_controller.rb:43:in
create'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1333:in send' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1333:in
perform_action_without_filters'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:617:in call_filters' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:610:in
perform_action_without_benchmark'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:68:in perform_action_without_rescue' /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in
ms'
/usr/local/lib/ruby/1.8/benchmark.rb:308:in realtime' /usr/local/lib/ruby/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in
ms'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:68:in perform_action_without_rescue' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/rescue.rb:160:in
perform_action_without_flash'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/flash.rb:151:in perform_action' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:532:in
send'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:532:in process_without_filters' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:606:in
process'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:391:in process' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:386:in
call'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/routing/route_set.rb:438:in call' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:87:in
dispatch'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:121:in _call' /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:130:in
build_middleware_stack'
/usr/local/lib/ruby/gems/1.8/gems/omniauth-1.1.1/lib/omniauth/strategy.rb:177:in call' /usr/local/lib/ruby/gems/1.8/gems/omniauth-1.1.1/lib/omniauth/strategy.rb:177:in
call!'
/usr/local/lib/ruby/gems/1.8/gems/omniauth-1.1.1/lib/omniauth/strategy.rb:157:in call' /usr/local/lib/ruby/gems/1.8/gems/omniauth-1.1.1/lib/omniauth/strategy.rb:177:in
call!'
/usr/local/lib/ruby/gems/1.8/gems/omniauth-1.1.1/lib/omniauth/strategy.rb:157:in call' /usr/local/lib/ruby/gems/1.8/gems/omniauth-1.1.1/lib/omniauth/builder.rb:48:in
call'
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/string_coercion.rb:25:in call' /usr/local/lib/ruby/gems/1.8/gems/rack-1.1.0/lib/rack/head.rb:9:in
call'
/usr/local/lib/ruby/gems/1.8/gems/rack-1.1.0/lib/rack/methodoverride.rb:24:in `call'
class FacebookController < ApplicationController
before_filter :create_client
# Start the facebook authentication process.
#
def new
@target_url = root_path(:src => HomeShowSource::LoginError)
session[:fb_redirect_uri] = fb_reply_url(
:src => @source,
:target => params[:target],
:follow_user_id => params[:follow_user_id],
:usage => params[:usage])
@client.redirect_uri = session[:fb_redirect_uri]
@target_url = @client.authorization_uri(
:scope => [:email,
:user_likes,
:user_birthday,
:publish_actions])
redirect_to @target_url
end
# Handle reply from facebook oauth.
#
def create
@client.redirect_uri = session[:fb_redirect_uri]
@client.authorization_code = params[:code]
access_token = @client.access_token!(:client_auth_body)
end
private
# Create facebook client variable.
#
def create_client
fb_auth = FbGraph::Auth.new(
CONFIG[:fb_app_id],
CONFIG[:fb_app_secret])
@client = fb_auth.client
end
end
Hi
I am getting this error
Rack::OAuth2::Client::Error (Rack::OAuth2::Client::Error):
when my redirect_uri has additional parameters like this:
def facebook_oauth_link(todo) fb_auth = FbGraph::Auth.new(FACEBOOK[:id], FACEBOOK[:secret]) client = fb_auth.client client.redirect_uri = enter_url(todo: todo) client.authorization_uri(:scope => [:email, :offline_access, :publish_stream, :user_location, :user_interests]) end
But if this is my code
def facebook_oauth_link fb_auth = FbGraph::Auth.new(FACEBOOK[:id], FACEBOOK[:secret]) client = fb_auth.client client.redirect_uri = enter_url client.authorization_uri(:scope => [:email, :offline_access, :publish_stream, :user_location, :user_interests]) end
The purpose of my params(todo) is for me to determine where to redirect the user after the callback.