Codebase list ruby-omniauth-facebook / 8086931
support for parsing code from signed request cookie Mark Dodwell 12 years ago
2 changed file(s) with 96 addition(s) and 3 deletion(s). Raw diff Collapse all Expand all
6565 end
6666
6767 def build_access_token
68 super.tap do |token|
69 token.options.merge!(access_token_options)
68 with_code(request.params['code'] || signed_request && signed_request['code']) do
69 super.tap do |token|
70 token.options.merge!(access_token_options)
71 end
7072 end
7173 end
7274
8082 params[:scope] ||= DEFAULT_SCOPE
8183 end
8284 end
85
86 def signed_request
87 @signed_request ||= begin
88 cookie = request.cookies["fbsr_#{client.id}"] and
89 parse_signed_request(cookie)
90 end
91 end
8392
8493 private
94
95 def with_code(code)
96 original_code = request.params['code']
97 begin
98 request.params['code'] = code
99 yield
100 ensure
101 request.params['code'] = original_code
102 end
103 end
85104
86105 def prune!(hash)
87106 hash.delete_if do |_, value|
89108 value.nil? || (value.respond_to?(:empty?) && value.empty?)
90109 end
91110 end
111
112 def parse_signed_request(value)
113 signature, encoded_payload = value.split('.')
114
115 decoded_hex_signature = base64_decode_url(signature)#.unpack('H*')
116 decoded_payload = MultiJson.decode(base64_decode_url(encoded_payload))
117
118 unless decoded_payload['algorithm'] == 'HMAC-SHA256'
119 raise NotImplementedError, "unkown algorithm: #{decoded_payload['algorithm']}"
120 end
121
122 if valid_signature?(client.secret, decoded_hex_signature, encoded_payload)
123 decoded_payload
124 end
125 end
126
127 def valid_signature?(secret, signature, payload, algorithm = OpenSSL::Digest::SHA256.new)
128 OpenSSL::HMAC.digest(algorithm, secret, payload) == signature
129 end
130
131 def base64_decode_url(value)
132 value += '=' * (4 - value.size.modulo(4))
133 Base64.decode64(value.tr('-_', '+/'))
134 end
92135 end
93136 end
94137 end
00 require 'spec_helper'
11 require 'omniauth-facebook'
2 require 'openssl'
3 require 'base64'
24
35 describe OmniAuth::Strategies::Facebook do
46 before :each do
57 @request = double('Request')
68 @request.stub(:params) { {} }
9 @request.stub(:cookies) { {} }
10
11 @client_id = '123'
12 @client_secret = '53cr3tz'
713 end
814
915 subject do
10 OmniAuth::Strategies::Facebook.new(nil, @options || {}).tap do |strategy|
16 args = [@client_id, @client_secret, @options].compact
17 OmniAuth::Strategies::Facebook.new(nil, *args).tap do |strategy|
1118 strategy.stub(:request) { @request }
1219 end
1320 end
270277 subject.extra.should eq({ 'raw_info' => @raw_info })
271278 end
272279 end
280
281 describe '#signed_request' do
282 context 'cookie not present' do
283 it 'is nil' do
284 subject.send(:signed_request).should be_nil
285 end
286 end
287
288 context 'cookie present' do
289 before :each do
290 @payload = {
291 'algorithm' => 'HMAC-SHA256',
292 'code' => 'm4c0d3z',
293 'issued_at' => Time.now.to_i,
294 'user_id' => '123456'
295 }
296
297 @request.stub(:cookies) do
298 { "fbsr_#{@client_id}" => signed_request(@payload, @client_secret) }
299 end
300 end
301
302 it 'parses the access code out from the cookie' do
303 subject.send(:signed_request).should eq(@payload)
304 end
305 end
306 end
307
308 private
309
310 def signed_request(payload, secret)
311 encoded_payload = base64_encode_url(MultiJson.encode(payload))
312 encoded_signature = base64_encode_url(signature(encoded_payload, secret))
313 [encoded_signature, encoded_payload].join('.')
314 end
315
316 def base64_encode_url(value)
317 Base64.encode64(value).tr('+/', '-_').gsub(/\n/, '')
318 end
319
320 def signature(payload, secret, algorithm = OpenSSL::Digest::SHA256.new)
321 OpenSSL::HMAC.digest(algorithm, secret, payload)
322 end
273323 end