Codebase list ruby-omniauth-facebook / 765ed9a
remove support for canvas app flow Mark Dodwell authored 10 years ago Josef Šimánek committed 9 years ago
2 changed file(s) with 35 addition(s) and 169 deletion(s). Raw diff Collapse all Expand all
6767 end
6868
6969 def callback_phase
70 super
71 rescue NoAuthorizationCodeError => e
72 fail!(:no_authorization_code, e)
73 rescue UnknownSignatureAlgorithmError => e
74 fail!(:unknown_signature_algoruthm, e)
75 end
76
77 def request_phase
78 if signed_request_contains_access_token?
79 # If we already have an access token, we can just hit the callback URL directly and pass the signed request.
80 params = { :signed_request => raw_signed_request }
81 query = Rack::Utils.build_query(params)
82
83 url = callback_url
84 url << "?" unless url.match(/\?/)
85 url << "&" unless url.match(/[\&\?]$/)
86 url << query
87
88 redirect url
89 else
90 super
70 with_authorization_code! do
71 begin
72 super
73 rescue NoAuthorizationCodeError => e
74 fail!(:no_authorization_code, e)
75 rescue UnknownSignatureAlgorithmError => e
76 fail!(:unknown_signature_algoruthm, e)
77 end
9178 end
9279 end
9380
9481 # NOTE If we're using code from the signed request then FB sets the redirect_uri to '' during the authorize
9582 # phase and it must match during the access_token phase:
96 # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php#L348
83 # https://github.com/facebook/facebook-php-sdk/blob/master/src/base_facebook.php#L477
9784 def callback_url
98 if @authorization_code_from_signed_request
85 if @authorization_code_from_signed_request_in_cookie
9986 ''
10087 else
10188 options[:callback_url] || super
10996 # You can pass +display+, +scope+, or +auth_type+ params to the auth request, if you need to set them dynamically.
11097 # You can also set these options in the OmniAuth config :authorize_params option.
11198 #
112 # /auth/facebook?display=popup
99 # For example: /auth/facebook?display=popup
113100 def authorize_params
114101 super.tap do |params|
115102 %w[display scope auth_type].each do |v|
122109 end
123110 end
124111
125 # Parse signed request in order, from:
126 #
127 # 1. The request 'signed_request' param (server-side flow from canvas pages) or
128 # 2. A cookie (client-side flow via JS SDK)
129 def signed_request
130 @signed_request ||= raw_signed_request && parse_signed_request(raw_signed_request)
131 end
132
133112 protected
134113
135114 def build_access_token
136 if signed_request_contains_access_token?
137 hash = signed_request.clone
138 ::OAuth2::AccessToken.new(
139 client,
140 hash.delete('oauth_token'),
141 hash.merge!(access_token_options.merge(:expires_at => hash.delete('expires')))
142 )
143 else
144 with_authorization_code! { super }.tap do |token|
145 token.options.merge!(access_token_options)
146 end
115 super.tap do |token|
116 token.options.merge!(access_token_options)
147117 end
148118 end
149119
150120 private
151121
152 def raw_signed_request
153 request.params['signed_request'] || request.cookies["fbsr_#{client.id}"]
154 end
155
156 # If the signed_request comes from a FB canvas page and the user has already authorized your application, the JSON
157 # object will be contain the access token.
158 #
159 # https://developers.facebook.com/docs/authentication/canvas/
160 def signed_request_contains_access_token?
161 signed_request && signed_request['oauth_token']
122 def signed_request_from_cookie
123 @signed_request_from_cookie ||= raw_signed_request_from_cookie && parse_signed_request(raw_signed_request_from_cookie)
124 end
125
126 def raw_signed_request_from_cookie
127 request.cookies["fbsr_#{client.id}"]
162128 end
163129
164130 # Picks the authorization code in order, from:
165131 #
166132 # 1. The request 'code' param (manual callback from standard server-side flow)
167 # 2. A signed request (see #signed_request for more)
133 # 2. A signed request from cookie (passed from the client during the client-side flow)
168134 def with_authorization_code!
169135 if request.params.key?('code')
170136 yield
171 elsif code_from_signed_request = signed_request && signed_request['code']
137 elsif code_from_signed_request = signed_request_from_cookie && signed_request_from_cookie['code']
172138 request.params['code'] = code_from_signed_request
173 @authorization_code_from_signed_request = true
139 @authorization_code_from_signed_request_in_cookie = true
140 original_provider_ignores_state = options.provider_ignores_state
141 options.provider_ignores_state = true
174142 begin
175143 yield
176144 ensure
177145 request.params.delete('code')
178 @authorization_code_from_signed_request = false
146 @authorization_code_from_signed_request_in_cookie = false
147 options.provider_ignores_state = original_provider_ignores_state
179148 end
180149 else
181 raise NoAuthorizationCodeError, 'must pass either a `code` parameter or a signed request (via `signed_request` parameter or a `fbsr_XXX` cookie)'
150 raise NoAuthorizationCodeError, 'must pass either a `code` (via URL or by an `fbsr_XXX` signed request cookie)'
182151 end
183152 end
184153
393393
394394 class CookieAndParamNotPresentTest < TestCase
395395 test 'is nil' do
396 assert_nil strategy.send(:signed_request)
396 assert_nil strategy.send(:signed_request_from_cookie)
397397 end
398398
399399 test 'throws an error on calling build_access_token' do
400 assert_equal 'must pass either a `code` parameter or a signed request (via `signed_request` parameter or a `fbsr_XXX` cookie)',
401 assert_raises(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError) { strategy.send(:build_access_token) }.message
400 assert_raises(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError) { strategy.send(:with_authorization_code!) {} }
402401 end
403402 end
404403
416415 end
417416
418417 test 'parses the access code out from the cookie' do
419 assert_equal @payload, strategy.send(:signed_request)
418 assert_equal @payload, strategy.send(:signed_request_from_cookie)
420419 end
421420
422421 test 'throws an error if the algorithm is unknown' do
423422 setup('UNKNOWN-ALGO')
424 assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError) { strategy.send(:signed_request) }.message
425 end
426 end
427
428 class ParamPresentTest < TestCase
429 def setup(algo = nil)
430 super()
431 @payload = {
432 'algorithm' => algo || 'HMAC-SHA256',
433 'oauth_token' => 'XXX',
434 'issued_at' => Time.now.to_i,
435 'user_id' => '123456'
436 }
437
438 @request.stubs(:params).returns({'signed_request' => signed_request(@payload, @client_secret)})
439 end
440
441 test 'parses the access code out from the param' do
442 assert_equal @payload, strategy.send(:signed_request)
443 end
444
445 test 'throws an error if the algorithm is unknown' do
446 setup('UNKNOWN-ALGO')
447 assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError) { strategy.send(:signed_request) }.message
448 end
449 end
450
451 class CookieAndParamPresentTest < TestCase
452 def setup
453 super
454 @payload_from_cookie = {
455 'algorithm' => 'HMAC-SHA256',
456 'from' => 'cookie'
457 }
458
459 @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload_from_cookie, @client_secret)})
460
461 @payload_from_param = {
462 'algorithm' => 'HMAC-SHA256',
463 'from' => 'param'
464 }
465
466 @request.stubs(:params).returns({'signed_request' => signed_request(@payload_from_param, @client_secret)})
467 end
468
469 test 'picks param over cookie' do
470 assert_equal @payload_from_param, strategy.send(:signed_request)
423 assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError) { strategy.send(:signed_request_from_cookie) }.message
471424 end
472425 end
473426
478431 end
479432
480433 test 'empty param' do
481 assert_equal nil, strategy.send(:signed_request)
482 end
483 end
484
485 end
486
487 class RequestPhaseWithSignedRequestTest < StrategyTestCase
488 include SignedRequestHelpers
489
490 def setup
491 super
492
493 payload = {
494 'algorithm' => 'HMAC-SHA256',
495 'oauth_token' => 'm4c0d3z'
496 }
497 @raw_signed_request = signed_request(payload, @client_secret)
498 @request.stubs(:params).returns("signed_request" => @raw_signed_request)
499
500 strategy.stubs(:callback_url).returns('/')
501 end
502
503 test 'redirects to callback passing along signed request' do
504 strategy.expects(:redirect).with("/?signed_request=#{Rack::Utils.escape(@raw_signed_request)}").once
505 strategy.request_phase
506 end
507 end
508
509 module BuildAccessTokenTests
510 class TestCase < StrategyTestCase
511 include SignedRequestHelpers
512 end
513
514 class ParamsContainSignedRequestWithAccessTokenTest < TestCase
515 def setup
516 super
517
518 @payload = {
519 'algorithm' => 'HMAC-SHA256',
520 'oauth_token' => 'm4c0d3z',
521 'expires' => Time.now.to_i
522 }
523 @raw_signed_request = signed_request(@payload, @client_secret)
524 @request.stubs(:params).returns({"signed_request" => @raw_signed_request})
525
526 strategy.stubs(:callback_url).returns('/')
527 end
528
529 test 'returns a new access token from the signed request' do
530 result = strategy.send(:build_access_token)
531 assert_kind_of ::OAuth2::AccessToken, result
532 assert_equal @payload['oauth_token'], result.token
533 end
534
535 test 'returns an access token with the correct expiry time' do
536 result = strategy.send(:build_access_token)
537 assert_equal @payload['expires'], result.expires_at
538 end
539 end
540 end
434 assert_equal nil, strategy.send(:signed_request_from_cookie)
435 end
436 end
437 end