oauth2 1.4.0 → 1.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,530 @@
1
+ # coding: utf-8
2
+
3
+ require 'helper'
4
+ require 'nkf'
5
+
6
+ describe OAuth2::Client do
7
+ subject do
8
+ described_class.new('abc', 'def', :site => 'https://api.example.com') do |builder|
9
+ builder.adapter :test do |stub|
10
+ stub.get('/success') { |env| [200, {'Content-Type' => 'text/awesome'}, 'yay'] }
11
+ stub.get('/reflect') { |env| [200, {}, env[:body]] }
12
+ stub.post('/reflect') { |env| [200, {}, env[:body]] }
13
+ stub.get('/unauthorized') { |env| [401, {'Content-Type' => 'application/json'}, MultiJson.encode(:error => error_value, :error_description => error_description_value)] }
14
+ stub.get('/conflict') { |env| [409, {'Content-Type' => 'text/plain'}, 'not authorized'] }
15
+ stub.get('/redirect') { |env| [302, {'Content-Type' => 'text/plain', 'location' => '/success'}, ''] }
16
+ stub.post('/redirect') { |env| [303, {'Content-Type' => 'text/plain', 'location' => '/reflect'}, ''] }
17
+ stub.get('/error') { |env| [500, {'Content-Type' => 'text/plain'}, 'unknown error'] }
18
+ stub.get('/empty_get') { |env| [204, {}, nil] }
19
+ stub.get('/different_encoding') { |env| [500, {'Content-Type' => 'application/json'}, NKF.nkf('-We', MultiJson.encode(:error => error_value, :error_description => '∞'))] }
20
+ stub.get('/ascii_8bit_encoding') { |env| [500, {'Content-Type' => 'application/json'}, MultiJson.encode(:error => 'invalid_request', :error_description => 'é').force_encoding('ASCII-8BIT')] }
21
+ end
22
+ end
23
+ end
24
+
25
+ let!(:error_value) { 'invalid_token' }
26
+ let!(:error_description_value) { 'bad bad token' }
27
+
28
+ describe '#initialize' do
29
+ it 'assigns id and secret' do
30
+ expect(subject.id).to eq('abc')
31
+ expect(subject.secret).to eq('def')
32
+ end
33
+
34
+ it 'assigns site from the options hash' do
35
+ expect(subject.site).to eq('https://api.example.com')
36
+ end
37
+
38
+ it 'assigns Faraday::Connection#host' do
39
+ expect(subject.connection.host).to eq('api.example.com')
40
+ end
41
+
42
+ it 'leaves Faraday::Connection#ssl unset' do
43
+ expect(subject.connection.ssl).to be_empty
44
+ end
45
+
46
+ it 'is able to pass a block to configure the connection' do
47
+ connection = double('connection')
48
+ builder = double('builder')
49
+ allow(Faraday).to receive(:new).and_yield(builder)
50
+ allow(Faraday::Connection).to receive(:new).and_return(connection)
51
+
52
+ expect(builder).to receive(:adapter).with(:test)
53
+
54
+ described_class.new('abc', 'def') do |client|
55
+ client.adapter :test
56
+ end.connection
57
+ end
58
+
59
+ it 'defaults raise_errors to true' do
60
+ expect(subject.options[:raise_errors]).to be true
61
+ end
62
+
63
+ it 'allows true/false for raise_errors option' do
64
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => false)
65
+ expect(client.options[:raise_errors]).to be false
66
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => true)
67
+ expect(client.options[:raise_errors]).to be true
68
+ end
69
+
70
+ it 'allows override of raise_errors option' do
71
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com', :raise_errors => true) do |builder|
72
+ builder.adapter :test do |stub|
73
+ stub.get('/notfound') { |env| [404, {}, nil] }
74
+ end
75
+ end
76
+ expect(client.options[:raise_errors]).to be true
77
+ expect { client.request(:get, '/notfound') }.to raise_error(OAuth2::Error)
78
+ response = client.request(:get, '/notfound', :raise_errors => false)
79
+ expect(response.status).to eq(404)
80
+ end
81
+
82
+ it 'allows get/post for access_token_method option' do
83
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :get)
84
+ expect(client.options[:access_token_method]).to eq(:get)
85
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com', :access_token_method => :post)
86
+ expect(client.options[:access_token_method]).to eq(:post)
87
+ end
88
+
89
+ it 'does not mutate the opts hash argument' do
90
+ opts = {:site => 'http://example.com/'}
91
+ opts2 = opts.dup
92
+ described_class.new 'abc', 'def', opts
93
+ expect(opts).to eq(opts2)
94
+ end
95
+ end
96
+
97
+ %w[authorize token].each do |url_type|
98
+ describe ":#{url_type}_url option" do
99
+ it "defaults to a path of /oauth/#{url_type}" do
100
+ expect(subject.send("#{url_type}_url")).to eq("https://api.example.com/oauth/#{url_type}")
101
+ end
102
+
103
+ it "is settable via the :#{url_type}_url option" do
104
+ subject.options[:"#{url_type}_url"] = '/oauth/custom'
105
+ expect(subject.send("#{url_type}_url")).to eq('https://api.example.com/oauth/custom')
106
+ end
107
+
108
+ it 'allows a different host than the site' do
109
+ subject.options[:"#{url_type}_url"] = 'https://api.foo.com/oauth/custom'
110
+ expect(subject.send("#{url_type}_url")).to eq('https://api.foo.com/oauth/custom')
111
+ end
112
+ end
113
+ end
114
+
115
+ describe ':redirect_uri option' do
116
+ let(:auth_code_params) do
117
+ {
118
+ 'client_id' => 'abc',
119
+ 'client_secret' => 'def',
120
+ 'code' => 'code',
121
+ 'grant_type' => 'authorization_code',
122
+ }
123
+ end
124
+
125
+ context 'when blank' do
126
+ it 'there is no redirect_uri param added to authorization URL' do
127
+ expect(subject.authorize_url('a' => 'b')).to eq('https://api.example.com/oauth/authorize?a=b')
128
+ end
129
+
130
+ it 'does not add the redirect_uri param to the auth_code token exchange request' do
131
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com') do |builder|
132
+ builder.adapter :test do |stub|
133
+ stub.post('/oauth/token', auth_code_params) do
134
+ [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
135
+ end
136
+ end
137
+ end
138
+ client.auth_code.get_token('code')
139
+ end
140
+ end
141
+
142
+ context 'when set' do
143
+ before { subject.options[:redirect_uri] = 'https://site.com/oauth/callback' }
144
+
145
+ it 'adds the redirect_uri param to authorization URL' do
146
+ expect(subject.authorize_url('a' => 'b')).to eq('https://api.example.com/oauth/authorize?a=b&redirect_uri=https%3A%2F%2Fsite.com%2Foauth%2Fcallback')
147
+ end
148
+
149
+ it 'adds the redirect_uri param to the auth_code token exchange request' do
150
+ client = described_class.new('abc', 'def', :redirect_uri => 'https://site.com/oauth/callback', :site => 'https://api.example.com') do |builder|
151
+ builder.adapter :test do |stub|
152
+ stub.post('/oauth/token', auth_code_params.merge('redirect_uri' => 'https://site.com/oauth/callback')) do
153
+ [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
154
+ end
155
+ end
156
+ end
157
+ client.auth_code.get_token('code')
158
+ end
159
+ end
160
+
161
+ describe 'custom headers' do
162
+ context 'string key headers' do
163
+ it 'adds the custom headers to request' do
164
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com', :auth_scheme => :request_body) do |builder|
165
+ builder.adapter :test do |stub|
166
+ stub.post('/oauth/token') do |env|
167
+ expect(env.request_headers).to include({'CustomHeader' => 'CustomHeader'})
168
+ [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
169
+ end
170
+ end
171
+ end
172
+ header_params = {'headers' => {'CustomHeader' => 'CustomHeader'}}
173
+ client.auth_code.get_token('code', header_params)
174
+ end
175
+ end
176
+
177
+ context 'symbol key headers' do
178
+ it 'adds the custom headers to request' do
179
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com', :auth_scheme => :request_body) do |builder|
180
+ builder.adapter :test do |stub|
181
+ stub.post('/oauth/token') do |env|
182
+ expect(env.request_headers).to include({'CustomHeader' => 'CustomHeader'})
183
+ [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
184
+ end
185
+ end
186
+ end
187
+ header_params = {:headers => {'CustomHeader' => 'CustomHeader'}}
188
+ client.auth_code.get_token('code', header_params)
189
+ end
190
+ end
191
+
192
+ context 'string key custom headers with basic auth' do
193
+ it 'adds the custom headers to request' do
194
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com') do |builder|
195
+ builder.adapter :test do |stub|
196
+ stub.post('/oauth/token') do |env|
197
+ expect(env.request_headers).to include({'CustomHeader' => 'CustomHeader'})
198
+ [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
199
+ end
200
+ end
201
+ end
202
+ header_params = {'headers' => {'CustomHeader' => 'CustomHeader'}}
203
+ client.auth_code.get_token('code', header_params)
204
+ end
205
+ end
206
+
207
+ context 'symbol key custom headers with basic auth' do
208
+ it 'adds the custom headers to request' do
209
+ client = described_class.new('abc', 'def', :site => 'https://api.example.com') do |builder|
210
+ builder.adapter :test do |stub|
211
+ stub.post('/oauth/token') do |env|
212
+ expect(env.request_headers).to include({'CustomHeader' => 'CustomHeader'})
213
+ [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}']
214
+ end
215
+ end
216
+ end
217
+ header_params = {:headers => {'CustomHeader' => 'CustomHeader'}}
218
+ client.auth_code.get_token('code', header_params)
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ describe '#request' do
225
+ it 'works with a null response body' do
226
+ expect(subject.request(:get, 'empty_get').body).to eq('')
227
+ end
228
+
229
+ it 'returns on a successful response' do
230
+ response = subject.request(:get, '/success')
231
+ expect(response.body).to eq('yay')
232
+ expect(response.status).to eq(200)
233
+ expect(response.headers).to eq('Content-Type' => 'text/awesome')
234
+ end
235
+
236
+ it 'posts a body' do
237
+ response = subject.request(:post, '/reflect', :body => 'foo=bar')
238
+ expect(response.body).to eq('foo=bar')
239
+ end
240
+
241
+ it 'follows redirects properly' do
242
+ response = subject.request(:get, '/redirect')
243
+ expect(response.body).to eq('yay')
244
+ expect(response.status).to eq(200)
245
+ expect(response.headers).to eq('Content-Type' => 'text/awesome')
246
+ end
247
+
248
+ it 'redirects using GET on a 303' do
249
+ response = subject.request(:post, '/redirect', :body => 'foo=bar')
250
+ expect(response.body).to be_empty
251
+ expect(response.status).to eq(200)
252
+ end
253
+
254
+ it 'obeys the :max_redirects option' do
255
+ max_redirects = subject.options[:max_redirects]
256
+ subject.options[:max_redirects] = 0
257
+ response = subject.request(:get, '/redirect')
258
+ expect(response.status).to eq(302)
259
+ subject.options[:max_redirects] = max_redirects
260
+ end
261
+
262
+ it 'returns if raise_errors is false' do
263
+ subject.options[:raise_errors] = false
264
+ response = subject.request(:get, '/unauthorized')
265
+
266
+ expect(response.status).to eq(401)
267
+ expect(response.headers).to eq('Content-Type' => 'application/json')
268
+ expect(response.error).not_to be_nil
269
+ end
270
+
271
+ %w[/unauthorized /conflict /error /different_encoding /ascii_8bit_encoding].each do |error_path|
272
+ it "raises OAuth2::Error on error response to path #{error_path}" do
273
+ expect { subject.request(:get, error_path) }.to raise_error(OAuth2::Error)
274
+ end
275
+ end
276
+
277
+ # rubocop:disable Style/RedundantBegin
278
+ it 're-encodes response body in the error message' do
279
+ begin
280
+ subject.request(:get, '/ascii_8bit_encoding')
281
+ rescue StandardError => e
282
+ expect(e.message.encoding.name).to eq('UTF-8')
283
+ expect(e.message).to eq("invalid_request: é\n{\"error\":\"invalid_request\",\"error_description\":\"��\"}")
284
+ end
285
+ end
286
+
287
+ it 'parses OAuth2 standard error response' do
288
+ begin
289
+ subject.request(:get, '/unauthorized')
290
+ rescue StandardError => e
291
+ expect(e.code).to eq(error_value)
292
+ expect(e.description).to eq(error_description_value)
293
+ expect(e.to_s).to match(/#{error_value}/)
294
+ expect(e.to_s).to match(/#{error_description_value}/)
295
+ end
296
+ end
297
+
298
+ it 'provides the response in the Exception' do
299
+ begin
300
+ subject.request(:get, '/error')
301
+ rescue StandardError => e
302
+ expect(e.response).not_to be_nil
303
+ expect(e.to_s).to match(/unknown error/)
304
+ end
305
+ end
306
+ # rubocop:enable Style/RedundantBegin
307
+
308
+ context 'with ENV' do
309
+ include_context 'with stubbed env'
310
+ before do
311
+ stub_env('OAUTH_DEBUG' => 'true')
312
+ end
313
+
314
+ it 'outputs to $stdout when OAUTH_DEBUG=true' do
315
+ output = capture(:stdout) do
316
+ subject.request(:get, '/success')
317
+ end
318
+ logs = [
319
+ '-- request: GET https://api.example.com/success',
320
+ '-- response: Status 200',
321
+ '-- response: Content-Type: "text/awesome"',
322
+ ]
323
+ expect(output).to include(*logs)
324
+ end
325
+ end
326
+ end
327
+
328
+ describe '#get_token' do
329
+ it 'returns a configured AccessToken' do
330
+ client = stubbed_client do |stub|
331
+ stub.post('/oauth/token') do
332
+ [200, {'Content-Type' => 'application/json'}, MultiJson.encode('access_token' => 'the-token')]
333
+ end
334
+ end
335
+
336
+ token = client.get_token({})
337
+ expect(token).to be_a OAuth2::AccessToken
338
+ expect(token.token).to eq('the-token')
339
+ end
340
+
341
+ it 'authenticates with request parameters' do
342
+ client = stubbed_client(:auth_scheme => :request_body) do |stub|
343
+ stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def') do |env|
344
+ [200, {'Content-Type' => 'application/json'}, MultiJson.encode('access_token' => 'the-token')]
345
+ end
346
+ end
347
+ client.get_token({})
348
+ end
349
+
350
+ it 'authenticates with Basic auth' do
351
+ client = stubbed_client(:auth_scheme => :basic_auth) do |stub|
352
+ stub.post('/oauth/token') do |env|
353
+ raise Faraday::Adapter::Test::Stubs::NotFound unless env[:request_headers]['Authorization'] == OAuth2::Authenticator.encode_basic_auth('abc', 'def')
354
+
355
+ [200, {'Content-Type' => 'application/json'}, MultiJson.encode('access_token' => 'the-token')]
356
+ end
357
+ end
358
+ client.get_token({})
359
+ end
360
+
361
+ describe 'extract_access_token option' do
362
+ let(:client) do
363
+ client = stubbed_client(:extract_access_token => extract_access_token) do |stub|
364
+ stub.post('/oauth/token') do
365
+ [200, {'Content-Type' => 'application/json'}, MultiJson.encode('data' => {'access_token' => 'the-token'})]
366
+ end
367
+ end
368
+ end
369
+
370
+ context 'with proc extract_access_token' do
371
+ let(:extract_access_token) do
372
+ proc do |client, hash|
373
+ token = hash['data']['access_token']
374
+ OAuth2::AccessToken.new(client, token, hash)
375
+ end
376
+ end
377
+
378
+ it 'returns a configured AccessToken' do
379
+ token = client.get_token({})
380
+ expect(token).to be_a OAuth2::AccessToken
381
+ expect(token.token).to eq('the-token')
382
+ end
383
+ end
384
+
385
+ context 'with depracted Class.from_hash option' do
386
+ let(:extract_access_token) do
387
+ CustomAccessToken = Class.new(OAuth2::AccessToken)
388
+ CustomAccessToken.define_singleton_method(:from_hash) do |client, hash|
389
+ token = hash['data']['access_token']
390
+ OAuth2::AccessToken.new(client, token, hash)
391
+ end
392
+ CustomAccessToken
393
+ end
394
+
395
+ it 'returns a configured AccessToken' do
396
+ token = client.get_token({})
397
+ expect(token).to be_a OAuth2::AccessToken
398
+ expect(token.token).to eq('the-token')
399
+ end
400
+ end
401
+ end
402
+
403
+ describe ':raise_errors flag' do
404
+ let(:options) { {} }
405
+ let(:token_response) { nil }
406
+ let(:post_args) { [] }
407
+
408
+ let(:client) do
409
+ stubbed_client(options.merge(:raise_errors => raise_errors)) do |stub|
410
+ stub.post('/oauth/token', *post_args) do
411
+ # stub 200 response so that we're testing the get_token handling of :raise_errors flag not request
412
+ [200, {'Content-Type' => 'application/json'}, token_response]
413
+ end
414
+ end
415
+ end
416
+
417
+ context 'when set to false' do
418
+ let(:raise_errors) { false }
419
+
420
+ context 'when the request body is nil' do
421
+ it 'returns a nil :access_token' do
422
+ expect(client.get_token({})).to eq(nil)
423
+ end
424
+ end
425
+
426
+ context 'when the request body is missing the access_token' do
427
+ let(:token_response) { MultiJson.encode('unexpected_access_token' => 'the-token') }
428
+
429
+ it 'returns a nil :access_token' do
430
+ expect(client.get_token({})).to eq(nil)
431
+ end
432
+ end
433
+
434
+ context 'when the request body has an access token' do
435
+ let(:token_response) { MultiJson.encode('access_token' => 'the-token') }
436
+
437
+ it 'returns the parsed :access_token from body' do
438
+ token = client.get_token({})
439
+ expect(token).to be_a OAuth2::AccessToken
440
+ expect(token.token).to eq('the-token')
441
+ end
442
+
443
+ context 'when :auth_scheme => :request_body' do
444
+ context 'when arbitrary params are present' do
445
+ let(:post_args) { ['arbitrary' => 'parameter', 'client_id' => 'abc', 'client_secret' => 'def'] }
446
+ let(:options) { {:auth_scheme => :request_body} }
447
+
448
+ it 'does not affect access token' do
449
+ token = client.get_token(*post_args)
450
+ expect(token).to be_a OAuth2::AccessToken
451
+ expect(token.token).to eq('the-token')
452
+ end
453
+ end
454
+ end
455
+ end
456
+
457
+ context 'when extract_access_token raises an exception' do
458
+ let(:options) do
459
+ {
460
+ :extract_access_token => proc { |client, hash| raise ArgumentError },
461
+ }
462
+ end
463
+
464
+ it 'returns a nil :access_token' do
465
+ expect(client.get_token({})).to eq(nil)
466
+ end
467
+ end
468
+ end
469
+
470
+ context 'when set to true' do
471
+ let(:raise_errors) { true }
472
+
473
+ context 'when the request body is nil' do
474
+ it 'raises an error' do
475
+ expect { client.get_token({}) }.to raise_error OAuth2::Error
476
+ end
477
+ end
478
+
479
+ context 'when the request body is missing the access_token' do
480
+ let(:token_response) { MultiJson.encode('unexpected_access_token' => 'the-token') }
481
+
482
+ it 'raises an error' do
483
+ expect { client.get_token({}) }.to raise_error OAuth2::Error
484
+ end
485
+ end
486
+
487
+ context 'when extract_access_token raises an exception' do
488
+ let(:options) do
489
+ {
490
+ :extract_access_token => proc { |client, hash| raise ArgumentError },
491
+ }
492
+ end
493
+
494
+ it 'raises an error' do
495
+ expect { client.get_token({}) }.to raise_error OAuth2::Error
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ def stubbed_client(params = {}, &stubs)
502
+ params = {:site => 'https://api.example.com'}.merge(params)
503
+ OAuth2::Client.new('abc', 'def', params) do |builder|
504
+ builder.adapter :test, &stubs
505
+ end
506
+ end
507
+ end
508
+
509
+ it 'instantiates an AuthCode strategy with this client' do
510
+ expect(subject.auth_code).to be_kind_of(OAuth2::Strategy::AuthCode)
511
+ end
512
+
513
+ it 'instantiates an Implicit strategy with this client' do
514
+ expect(subject.implicit).to be_kind_of(OAuth2::Strategy::Implicit)
515
+ end
516
+
517
+ context 'with SSL options' do
518
+ subject do
519
+ cli = described_class.new('abc', 'def', :site => 'https://api.example.com', :ssl => {:ca_file => 'foo.pem'})
520
+ cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b|
521
+ b.adapter :test
522
+ end
523
+ cli
524
+ end
525
+
526
+ it 'passes the SSL options along to Faraday::Connection#ssl' do
527
+ expect(subject.connection.ssl.fetch(:ca_file)).to eq('foo.pem')
528
+ end
529
+ end
530
+ end
@@ -0,0 +1,120 @@
1
+ describe OAuth2::MACToken do
2
+ subject { described_class.new(client, token, 'abc123') }
3
+
4
+ let(:token) { 'monkey' }
5
+ let(:client) do
6
+ OAuth2::Client.new('abc', 'def', :site => 'https://api.example.com') do |builder|
7
+ builder.request :url_encoded
8
+ builder.adapter :test do |stub|
9
+ VERBS.each do |verb|
10
+ stub.send(verb, '/token/header') { |env| [200, {}, env[:request_headers]['Authorization']] }
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ describe '#initialize' do
17
+ it 'assigns client and token' do
18
+ expect(subject.client).to eq(client)
19
+ expect(subject.token).to eq(token)
20
+ end
21
+
22
+ it 'assigns secret' do
23
+ expect(subject.secret).to eq('abc123')
24
+ end
25
+
26
+ it 'defaults algorithm to hmac-sha-256' do
27
+ pending_for(:engine => 'ruby', :versions => '1.9.3', :reason => "Ruby 1.9's OpenSSL uses instance of OpenSSL::Digest")
28
+ expect(subject.algorithm).to be_instance_of(OpenSSL::Digest::SHA256)
29
+ end
30
+
31
+ it 'handles hmac-sha-256' do
32
+ pending_for(:engine => 'ruby', :versions => '1.9.3', :reason => "Ruby 1.9's OpenSSL uses instance of OpenSSL::Digest")
33
+ mac = described_class.new(client, token, 'abc123', :algorithm => 'hmac-sha-256')
34
+ expect(mac.algorithm).to be_instance_of(OpenSSL::Digest::SHA256)
35
+ end
36
+
37
+ it 'handles hmac-sha-1' do
38
+ pending_for(:engine => 'ruby', :versions => '1.9.3', :reason => "Ruby 1.9's OpenSSL uses instance of OpenSSL::Digest")
39
+ mac = described_class.new(client, token, 'abc123', :algorithm => 'hmac-sha-1')
40
+ expect(mac.algorithm).to be_instance_of(OpenSSL::Digest::SHA1)
41
+ end
42
+
43
+ it 'raises on improper algorithm' do
44
+ expect { described_class.new(client, token, 'abc123', :algorithm => 'invalid-sha') }.to raise_error(ArgumentError)
45
+ end
46
+ end
47
+
48
+ describe '#request' do
49
+ VERBS.each do |verb|
50
+ it "sends the token in the Authorization header for a #{verb.to_s.upcase} request" do
51
+ expect(subject.post('/token/header').body).to include("MAC id=\"#{token}\"")
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#header' do
57
+ it 'does not generate the same header twice' do
58
+ header = subject.header('get', 'https://www.example.com/hello')
59
+ duplicate_header = subject.header('get', 'https://www.example.com/hello')
60
+
61
+ expect(header).not_to eq(duplicate_header)
62
+ end
63
+
64
+ it 'generates the proper format' do
65
+ header = subject.header('get', 'https://www.example.com/hello?a=1')
66
+ expect(header).to match(/MAC id="#{token}", ts="[0-9]+", nonce="[^"]+", mac="[^"]+"/)
67
+ end
68
+
69
+ it 'passes ArgumentError with an invalid url' do
70
+ expect { subject.header('get', 'this-is-not-valid') }.to raise_error(ArgumentError)
71
+ end
72
+
73
+ it 'passes URI::InvalidURIError through' do
74
+ expect { subject.header('get', nil) }.to raise_error(URI::InvalidURIError)
75
+ end
76
+ end
77
+
78
+ describe '#signature' do
79
+ it 'generates properly' do
80
+ signature = subject.signature(0, 'random-string', 'get', URI('https://www.google.com'))
81
+ expect(signature).to eq('rMDjVA3VJj3v1OmxM29QQljKia6msl5rjN83x3bZmi8=')
82
+ end
83
+ end
84
+
85
+ describe '#headers' do
86
+ it 'is an empty hash' do
87
+ expect(subject.headers).to eq({})
88
+ end
89
+ end
90
+
91
+ describe '.from_access_token' do
92
+ subject { described_class.from_access_token(access_token, 'hello') }
93
+
94
+ let(:access_token) do
95
+ OAuth2::AccessToken.new(
96
+ client, token,
97
+ :expires_at => 1,
98
+ :expires_in => 1,
99
+ :refresh_token => 'abc',
100
+ :random => 1
101
+ )
102
+ end
103
+
104
+ it 'initializes client, token, and secret properly' do
105
+ expect(subject.client).to eq(client)
106
+ expect(subject.token).to eq(token)
107
+ expect(subject.secret).to eq('hello')
108
+ end
109
+
110
+ it 'initializes configuration options' do
111
+ expect(subject.expires_at).to eq(1)
112
+ expect(subject.expires_in).to eq(1)
113
+ expect(subject.refresh_token).to eq('abc')
114
+ end
115
+
116
+ it 'initializes params' do
117
+ expect(subject.params).to eq(:random => 1)
118
+ end
119
+ end
120
+ end