omniauth-microsoft_graph 0.2.1 → 2.2.0
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.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/ruby.yml +32 -0
- data/.travis.yml +2 -1
- data/README.md +46 -1
- data/Rakefile +5 -7
- data/lib/omniauth/microsoft_graph/domain_verifier.rb +99 -0
- data/lib/omniauth/microsoft_graph/version.rb +2 -2
- data/lib/omniauth/microsoft_graph.rb +1 -0
- data/lib/omniauth/strategies/microsoft_graph.rb +114 -26
- data/omniauth-microsoft_graph.gemspec +8 -7
- data/spec/omniauth/microsoft_graph/domain_verifier_spec.rb +120 -0
- data/spec/omniauth/strategies/microsoft_graph_oauth2_spec.rb +538 -0
- data/spec/spec_helper.rb +4 -0
- metadata +71 -27
- data/test/strategy_test.rb +0 -26
- data/test/test_helper.rb +0 -31
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'omniauth_microsoft_graph'
|
|
6
|
+
require 'stringio'
|
|
7
|
+
|
|
8
|
+
describe OmniAuth::Strategies::MicrosoftGraph do
|
|
9
|
+
let(:request) { double('Request', params: {}, cookies: {}, env: {}) }
|
|
10
|
+
let(:app) do
|
|
11
|
+
lambda do
|
|
12
|
+
[200, {}, ['Hello.']]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
subject do
|
|
17
|
+
OmniAuth::Strategies::MicrosoftGraph.new(app, 'appid', 'secret', @options || {}).tap do |strategy|
|
|
18
|
+
allow(strategy).to receive(:request) do
|
|
19
|
+
request
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
before do
|
|
25
|
+
OmniAuth.config.test_mode = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
after do
|
|
29
|
+
OmniAuth.config.test_mode = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#client_options' do
|
|
33
|
+
it 'has correct site' do
|
|
34
|
+
expect(subject.client.site).to eq('https://login.microsoftonline.com/')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'has correct authorize_url' do
|
|
38
|
+
expect(subject.client.options[:authorize_url]).to eq('common/oauth2/v2.0/authorize')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'has correct token_url' do
|
|
42
|
+
expect(subject.client.options[:token_url]).to eq('common/oauth2/v2.0/token')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe 'overrides' do
|
|
46
|
+
context 'as strings' do
|
|
47
|
+
it 'should allow overriding the site' do
|
|
48
|
+
@options = { client_options: { 'site' => 'https://example.com' } }
|
|
49
|
+
expect(subject.client.site).to eq('https://example.com')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'should allow overriding the authorize_url' do
|
|
53
|
+
@options = { client_options: { 'authorize_url' => 'https://example.com' } }
|
|
54
|
+
expect(subject.client.options[:authorize_url]).to eq('https://example.com')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'should allow overriding the token_url' do
|
|
58
|
+
@options = { client_options: { 'token_url' => 'https://example.com' } }
|
|
59
|
+
expect(subject.client.options[:token_url]).to eq('https://example.com')
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'as symbols' do
|
|
64
|
+
it 'should allow overriding the site' do
|
|
65
|
+
@options = { client_options: { site: 'https://example.com' } }
|
|
66
|
+
expect(subject.client.site).to eq('https://example.com')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'should allow overriding the authorize_url' do
|
|
70
|
+
@options = { client_options: { authorize_url: 'https://example.com' } }
|
|
71
|
+
expect(subject.client.options[:authorize_url]).to eq('https://example.com')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'should allow overriding the token_url' do
|
|
75
|
+
@options = { client_options: { token_url: 'https://example.com' } }
|
|
76
|
+
expect(subject.client.options[:token_url]).to eq('https://example.com')
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe '#authorize_options' do
|
|
83
|
+
%i[display score auth_type scope prompt login_hint domain_hint response_mode].each do |k|
|
|
84
|
+
it "should support #{k}" do
|
|
85
|
+
@options = { k => 'http://someval' }
|
|
86
|
+
expect(subject.authorize_params[k.to_s]).to eq('http://someval')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe 'callback_url' do
|
|
91
|
+
it 'should default to nil' do
|
|
92
|
+
@options = {}
|
|
93
|
+
expect(subject.authorize_params['callback_url']).to eq(nil)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'should set the callback_url parameter if present' do
|
|
97
|
+
@options = { callback_url: 'https://example.com' }
|
|
98
|
+
expect(subject.authorize_params['callback_url']).to eq('https://example.com')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe 'access_type' do
|
|
103
|
+
it 'should default to "offline"' do
|
|
104
|
+
@options = {}
|
|
105
|
+
expect(subject.authorize_params['access_type']).to eq('offline')
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it 'should set the access_type parameter if present' do
|
|
109
|
+
@options = { access_type: 'online' }
|
|
110
|
+
expect(subject.authorize_params['access_type']).to eq('online')
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe 'login_hint' do
|
|
115
|
+
it 'should default to nil' do
|
|
116
|
+
expect(subject.authorize_params['login_hint']).to eq(nil)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'should set the login_hint parameter if present' do
|
|
120
|
+
@options = { login_hint: 'john@example.com' }
|
|
121
|
+
expect(subject.authorize_params['login_hint']).to eq('john@example.com')
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
describe 'prompt' do
|
|
126
|
+
it 'should default to nil' do
|
|
127
|
+
expect(subject.authorize_params['prompt']).to eq(nil)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'should set the prompt parameter if present' do
|
|
131
|
+
@options = { prompt: 'consent select_account' }
|
|
132
|
+
expect(subject.authorize_params['prompt']).to eq('consent select_account')
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
describe 'scope' do
|
|
137
|
+
|
|
138
|
+
it 'should leave base scopes as is' do
|
|
139
|
+
@options = { scope: 'profile' }
|
|
140
|
+
expect(subject.authorize_params['scope']).to eq('profile')
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'should join scopes' do
|
|
144
|
+
@options = { scope: 'profile,email' }
|
|
145
|
+
expect(subject.authorize_params['scope']).to eq('profile email')
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'should deal with whitespace when joining scopes' do
|
|
149
|
+
@options = { scope: 'profile, email' }
|
|
150
|
+
expect(subject.authorize_params['scope']).to eq('profile email')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'should set default scope to email,profile' do
|
|
154
|
+
expect(subject.authorize_params['scope']).to eq('offline_access openid email profile https://graph.microsoft.com/User.Read')
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'should support space delimited scopes' do
|
|
158
|
+
@options = { scope: 'profile email' }
|
|
159
|
+
expect(subject.authorize_params['scope']).to eq('profile email')
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'should support extremely badly formed scopes' do
|
|
163
|
+
@options = { scope: 'profile email,foo,steve yeah http://example.com' }
|
|
164
|
+
expect(subject.authorize_params['scope']).to eq('profile email https://graph.microsoft.com/foo https://graph.microsoft.com/steve https://graph.microsoft.com/yeah http://example.com')
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe 'state' do
|
|
169
|
+
it 'should set the state parameter' do
|
|
170
|
+
@options = { state: 'some_state' }
|
|
171
|
+
expect(subject.authorize_params['state']).to eq('some_state')
|
|
172
|
+
expect(subject.authorize_params[:state]).to eq('some_state')
|
|
173
|
+
expect(subject.session['omniauth.state']).to eq('some_state')
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'should set the omniauth.state dynamically' do
|
|
177
|
+
allow(subject).to receive(:request) { double('Request', params: { 'state' => 'some_state' }, env: {}) }
|
|
178
|
+
expect(subject.authorize_params['state']).to eq('some_state')
|
|
179
|
+
expect(subject.authorize_params[:state]).to eq('some_state')
|
|
180
|
+
expect(subject.session['omniauth.state']).to eq('some_state')
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
describe 'overrides' do
|
|
185
|
+
it 'should include top-level options that are marked as :authorize_options' do
|
|
186
|
+
@options = { authorize_options: %i[scope foo request_visible_actions], scope: 'http://bar', foo: 'baz', hd: 'wow', request_visible_actions: 'something' }
|
|
187
|
+
expect(subject.authorize_params['scope']).to eq('http://bar')
|
|
188
|
+
expect(subject.authorize_params['foo']).to eq('baz')
|
|
189
|
+
expect(subject.authorize_params['hd']).to eq(nil)
|
|
190
|
+
expect(subject.authorize_params['request_visible_actions']).to eq('something')
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
describe 'request overrides' do
|
|
194
|
+
%i[access_type login_hint prompt scope state].each do |k|
|
|
195
|
+
context "authorize option #{k}" do
|
|
196
|
+
let(:request) { double('Request', params: { k.to_s => 'http://example.com' }, cookies: {}, env: {}) }
|
|
197
|
+
|
|
198
|
+
it "should set the #{k} authorize option dynamically in the request" do
|
|
199
|
+
@options = { k: '' }
|
|
200
|
+
expect(subject.authorize_params[k.to_s]).to eq('http://example.com')
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
describe 'custom authorize_options' do
|
|
206
|
+
let(:request) { double('Request', params: { 'foo' => 'something' }, cookies: {}, env: {}) }
|
|
207
|
+
|
|
208
|
+
it 'should support request overrides from custom authorize_options' do
|
|
209
|
+
@options = { authorize_options: [:foo], foo: '' }
|
|
210
|
+
expect(subject.authorize_params['foo']).to eq('something')
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
describe '#authorize_params' do
|
|
218
|
+
it 'should include any authorize params passed in the :authorize_params option' do
|
|
219
|
+
@options = { authorize_params: { request_visible_actions: 'something', foo: 'bar', baz: 'zip' }, hd: 'wow', bad: 'not_included' }
|
|
220
|
+
expect(subject.authorize_params['request_visible_actions']).to eq('something')
|
|
221
|
+
expect(subject.authorize_params['foo']).to eq('bar')
|
|
222
|
+
expect(subject.authorize_params['baz']).to eq('zip')
|
|
223
|
+
expect(subject.authorize_params['bad']).to eq(nil)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
describe '#token_params' do
|
|
228
|
+
it 'should include any token params passed in the :token_params option' do
|
|
229
|
+
@options = { token_params: { foo: 'bar', baz: 'zip' } }
|
|
230
|
+
expect(subject.token_params['foo']).to eq('bar')
|
|
231
|
+
expect(subject.token_params['baz']).to eq('zip')
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
describe '#token_options' do
|
|
236
|
+
it 'should include top-level options that are marked as :token_options' do
|
|
237
|
+
@options = { token_options: %i[scope foo], scope: 'bar', foo: 'baz', bad: 'not_included' }
|
|
238
|
+
expect(subject.token_params['scope']).to eq('bar')
|
|
239
|
+
expect(subject.token_params['foo']).to eq('baz')
|
|
240
|
+
expect(subject.token_params['bad']).to eq(nil)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
describe '#callback_path' do
|
|
245
|
+
it 'has the correct default callback path' do
|
|
246
|
+
allow(subject).to receive(:script_name).and_return('')
|
|
247
|
+
expect(subject.callback_path).to eq('/auth/microsoft_graph/callback')
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it 'should set the callback_path parameter if present' do
|
|
251
|
+
@options = { callback_path: '/auth/foo/callback' }
|
|
252
|
+
expect(subject.callback_path).to eq('/auth/foo/callback')
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it 'should set the callback_path with script_name if present' do
|
|
256
|
+
allow(subject).to receive(:script_name).and_return('/api/v1')
|
|
257
|
+
expect(subject.callback_path).to eq('/api/v1/auth/microsoft_graph/callback')
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
describe '#info' do
|
|
262
|
+
let(:client) do
|
|
263
|
+
OAuth2::Client.new('abc', 'def') do |builder|
|
|
264
|
+
builder.request :url_encoded
|
|
265
|
+
builder.adapter :test do |stub|
|
|
266
|
+
stub.get('/v1.0/me') { [200, { 'content-type' => 'application/json' }, response_hash.to_json] }
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
let(:access_token) { OAuth2::AccessToken.from_hash(client, { 'access_token' => 'a' }) }
|
|
271
|
+
before { allow(subject).to receive(:access_token).and_return(access_token) }
|
|
272
|
+
|
|
273
|
+
context 'with verified email' do
|
|
274
|
+
let(:response_hash) do
|
|
275
|
+
{ mail: 'something@domain.invalid' }
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it 'should return equal email ' do
|
|
279
|
+
expect(subject.info['email']).to eq('something@domain.invalid')
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
context 'when email verification fails' do
|
|
284
|
+
let(:response_hash) { { mail: 'something@domain.invalid' } }
|
|
285
|
+
let(:error) { OmniAuth::MicrosoftGraph::DomainVerificationError.new }
|
|
286
|
+
|
|
287
|
+
before do
|
|
288
|
+
allow(OmniAuth::MicrosoftGraph::DomainVerifier).to receive(:verify!).and_raise(error)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it 'raises an error' do
|
|
292
|
+
expect { subject.auth_hash }.to raise_error error
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
describe '#extra' do
|
|
298
|
+
let(:client) do
|
|
299
|
+
OAuth2::Client.new('abc', 'def') do |builder|
|
|
300
|
+
builder.request :url_encoded
|
|
301
|
+
builder.adapter :test do |stub|
|
|
302
|
+
stub.get('/v1.0/me') { [200, { 'content-type' => 'application/json' }, '{"id": "12345"}'] }
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
let(:access_token) { OAuth2::AccessToken.from_hash(client, { 'access_token' => 'a' }) }
|
|
307
|
+
|
|
308
|
+
before { allow(subject).to receive(:access_token).and_return(access_token) }
|
|
309
|
+
|
|
310
|
+
describe 'raw_info' do
|
|
311
|
+
it 'should include raw_info' do
|
|
312
|
+
expect(subject.extra['raw_info']).to eq('id' => '12345')
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
describe 'build_access_token' do
|
|
318
|
+
it 'should use a hybrid authorization request_uri if this is an AJAX request with a code parameter' do
|
|
319
|
+
allow(request).to receive(:scheme).and_return('https')
|
|
320
|
+
allow(request).to receive(:url).and_return('https://example.com')
|
|
321
|
+
allow(request).to receive(:xhr?).and_return(true)
|
|
322
|
+
allow(request).to receive(:params).and_return('code' => 'valid_code')
|
|
323
|
+
|
|
324
|
+
client = double(:client)
|
|
325
|
+
auth_code = double(:auth_code)
|
|
326
|
+
allow(client).to receive(:auth_code).and_return(auth_code)
|
|
327
|
+
expect(subject).to receive(:client).and_return(client)
|
|
328
|
+
expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: '/auth/microsoft_graph/callback' }, {})
|
|
329
|
+
|
|
330
|
+
expect(subject).not_to receive(:orig_build_access_token)
|
|
331
|
+
subject.instance_variable_set("@env", {})
|
|
332
|
+
subject.send(:build_access_token)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it 'should use a hybrid authorization request_uri if this is an AJAX request (mobile) with a code parameter' do
|
|
336
|
+
allow(request).to receive(:scheme).and_return('https')
|
|
337
|
+
allow(request).to receive(:url).and_return('https://example.com')
|
|
338
|
+
allow(request).to receive(:xhr?).and_return(true)
|
|
339
|
+
allow(request).to receive(:params).and_return('code' => 'valid_code', 'callback_url' => 'localhost')
|
|
340
|
+
|
|
341
|
+
client = double(:client)
|
|
342
|
+
auth_code = double(:auth_code)
|
|
343
|
+
allow(client).to receive(:auth_code).and_return(auth_code)
|
|
344
|
+
expect(subject).to receive(:client).and_return(client)
|
|
345
|
+
expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: 'localhost' }, {})
|
|
346
|
+
|
|
347
|
+
expect(subject).not_to receive(:orig_build_access_token)
|
|
348
|
+
subject.instance_variable_set("@env", {})
|
|
349
|
+
subject.send(:build_access_token)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it 'should use the request_uri from params if this not an AJAX request (request from installed app) with a code parameter' do
|
|
353
|
+
allow(request).to receive(:scheme).and_return('https')
|
|
354
|
+
allow(request).to receive(:url).and_return('https://example.com')
|
|
355
|
+
allow(request).to receive(:xhr?).and_return(false)
|
|
356
|
+
allow(request).to receive(:params).and_return('code' => 'valid_code', 'callback_url' => 'callback_url')
|
|
357
|
+
|
|
358
|
+
client = double(:client)
|
|
359
|
+
auth_code = double(:auth_code)
|
|
360
|
+
allow(client).to receive(:auth_code).and_return(auth_code)
|
|
361
|
+
expect(subject).to receive(:client).and_return(client)
|
|
362
|
+
expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: 'callback_url' }, {})
|
|
363
|
+
|
|
364
|
+
expect(subject).not_to receive(:orig_build_access_token)
|
|
365
|
+
subject.send(:build_access_token)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
it 'should read access_token from hash if this is not an AJAX request with a code parameter' do
|
|
369
|
+
allow(request).to receive(:scheme).and_return('https')
|
|
370
|
+
allow(request).to receive(:url).and_return('https://example.com')
|
|
371
|
+
allow(request).to receive(:xhr?).and_return(false)
|
|
372
|
+
allow(request).to receive(:params).and_return('access_token' => 'valid_access_token')
|
|
373
|
+
expect(subject).to receive(:verify_token).with('valid_access_token').and_return true
|
|
374
|
+
expect(subject).to receive(:client).and_return(:client)
|
|
375
|
+
|
|
376
|
+
token = subject.send(:build_access_token)
|
|
377
|
+
expect(token).to be_instance_of(::OAuth2::AccessToken)
|
|
378
|
+
expect(token.token).to eq('valid_access_token')
|
|
379
|
+
expect(token.client).to eq(:client)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it 'reads the code from a json request body' do
|
|
383
|
+
body = StringIO.new(%({"code":"json_access_token"}))
|
|
384
|
+
client = double(:client)
|
|
385
|
+
auth_code = double(:auth_code)
|
|
386
|
+
|
|
387
|
+
allow(request).to receive(:scheme).and_return('https')
|
|
388
|
+
allow(request).to receive(:url).and_return('https://example.com')
|
|
389
|
+
allow(request).to receive(:xhr?).and_return(false)
|
|
390
|
+
allow(request).to receive(:content_type).and_return('application/json')
|
|
391
|
+
allow(request).to receive(:body).and_return(body)
|
|
392
|
+
allow(client).to receive(:auth_code).and_return(auth_code)
|
|
393
|
+
expect(subject).to receive(:client).and_return(client)
|
|
394
|
+
|
|
395
|
+
expect(auth_code).to receive(:get_token).with('json_access_token', { redirect_uri: '/auth/microsoft_graph/callback' }, {})
|
|
396
|
+
|
|
397
|
+
subject.send(:build_access_token)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
it 'should use callback_url without query_string if this is not an AJAX request' do
|
|
401
|
+
allow(request).to receive(:scheme).and_return('https')
|
|
402
|
+
allow(request).to receive(:url).and_return('https://example.com')
|
|
403
|
+
allow(request).to receive(:xhr?).and_return(false)
|
|
404
|
+
allow(request).to receive(:params).and_return('code' => 'valid_code')
|
|
405
|
+
allow(request).to receive(:content_type).and_return('application/x-www-form-urlencoded')
|
|
406
|
+
|
|
407
|
+
client = double(:client)
|
|
408
|
+
auth_code = double(:auth_code)
|
|
409
|
+
allow(client).to receive(:auth_code).and_return(auth_code)
|
|
410
|
+
allow(subject).to receive(:callback_url).and_return('callback_url_without_query_string')
|
|
411
|
+
|
|
412
|
+
expect(subject).to receive(:client).and_return(client)
|
|
413
|
+
expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: 'callback_url_without_query_string' }, {})
|
|
414
|
+
subject.send(:build_access_token)
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
describe 'verify_token' do
|
|
419
|
+
before(:each) do
|
|
420
|
+
subject.options.client_options[:connection_build] = proc do |builder|
|
|
421
|
+
builder.request :url_encoded
|
|
422
|
+
builder.adapter :test do |stub|
|
|
423
|
+
stub.get('/v1.0/me?access_token=valid_access_token') do
|
|
424
|
+
[200, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(
|
|
425
|
+
aud: '000000000000.apps.googleusercontent.com',
|
|
426
|
+
id: '123456789',
|
|
427
|
+
email: 'example@example.com',
|
|
428
|
+
access_type: 'offline',
|
|
429
|
+
scope: 'profile email',
|
|
430
|
+
expires_in: 436
|
|
431
|
+
)]
|
|
432
|
+
end
|
|
433
|
+
stub.get('/v1.0/me?access_token=invalid_access_token') do
|
|
434
|
+
[400, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(error_description: 'Invalid Value')]
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
it 'should verify token if access_token is valid and app_id equals' do
|
|
441
|
+
subject.options.client_id = '000000000000.apps.googleusercontent.com'
|
|
442
|
+
expect(subject.send(:verify_token, 'valid_access_token')).to eq(true)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it 'should verify token if access_token is valid and app_id authorized' do
|
|
446
|
+
subject.options.authorized_client_ids = ['000000000000.apps.googleusercontent.com']
|
|
447
|
+
expect(subject.send(:verify_token, 'valid_access_token')).to eq(true)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
it 'should not verify token if access_token is valid but app_id is false' do
|
|
451
|
+
expect(subject.send(:verify_token, 'valid_access_token')).to eq(false)
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
it 'should raise error if access_token is invalid' do
|
|
455
|
+
expect do
|
|
456
|
+
subject.send(:verify_token, 'invalid_access_token')
|
|
457
|
+
end.to raise_error(OAuth2::Error)
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
describe 'Yammer profile endpoint support' do
|
|
462
|
+
describe '#profile_endpoint' do
|
|
463
|
+
context 'when no profile endpoint is determined' do
|
|
464
|
+
it 'defaults to Microsoft Graph profile URL' do
|
|
465
|
+
expect(subject.profile_endpoint).to eq('https://graph.microsoft.com/v1.0/me')
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
context 'when profile endpoint is already set' do
|
|
470
|
+
before { subject.instance_variable_set(:@profile_endpoint, 'https://custom.endpoint.com') }
|
|
471
|
+
|
|
472
|
+
it 'returns the previously set endpoint' do
|
|
473
|
+
expect(subject.profile_endpoint).to eq('https://custom.endpoint.com')
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
describe '#determine_profile_endpoint' do
|
|
479
|
+
let(:request) { double('Request', env: request_env) }
|
|
480
|
+
|
|
481
|
+
context 'when scope includes Yammer access_as_user scope' do
|
|
482
|
+
let(:request_env) { { 'omniauth.params' => { 'scope' => 'https://api.yammer.com/access_as_user' } } }
|
|
483
|
+
|
|
484
|
+
it 'returns Yammer profile URL' do
|
|
485
|
+
expect(subject.determine_profile_endpoint(request)).to eq('https://www.yammer.com/api/v1/users/current.json')
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
context 'when scope includes Yammer user_impersonation scope' do
|
|
490
|
+
let(:request_env) { { 'omniauth.params' => { 'scope' => 'openid profile https://api.yammer.com/user_impersonation' } } }
|
|
491
|
+
|
|
492
|
+
it 'returns Yammer profile URL' do
|
|
493
|
+
expect(subject.determine_profile_endpoint(request)).to eq('https://www.yammer.com/api/v1/users/current.json')
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
context 'when scope includes Yammer scope among other scopes' do
|
|
498
|
+
let(:request_env) { { 'omniauth.params' => { 'scope' => 'offline_access openid email profile https://api.yammer.com/access_as_user User.Read' } } }
|
|
499
|
+
|
|
500
|
+
it 'returns Yammer profile URL' do
|
|
501
|
+
expect(subject.determine_profile_endpoint(request)).to eq('https://www.yammer.com/api/v1/users/current.json')
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
context 'when scope includes multiple Yammer scopes' do
|
|
506
|
+
let(:request_env) { { 'omniauth.params' => { 'scope' => 'openid profile https://api.yammer.com/access_as_user https://api.yammer.com/user_impersonation' } } }
|
|
507
|
+
|
|
508
|
+
it 'returns Yammer profile URL' do
|
|
509
|
+
expect(subject.determine_profile_endpoint(request)).to eq('https://www.yammer.com/api/v1/users/current.json')
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
context 'when scope does not include any Yammer scopes' do
|
|
514
|
+
let(:request_env) { { 'omniauth.params' => { 'scope' => 'openid profile User.Read' } } }
|
|
515
|
+
|
|
516
|
+
it 'returns Microsoft Graph profile URL' do
|
|
517
|
+
expect(subject.determine_profile_endpoint(request)).to eq('https://graph.microsoft.com/v1.0/me')
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
context 'when scope is nil' do
|
|
522
|
+
let(:request_env) { { 'omniauth.params' => { 'scope' => nil } } }
|
|
523
|
+
|
|
524
|
+
it 'returns Microsoft Graph profile URL' do
|
|
525
|
+
expect(subject.determine_profile_endpoint(request)).to eq('https://graph.microsoft.com/v1.0/me')
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
context 'when omniauth.params is nil' do
|
|
530
|
+
let(:request_env) { { 'omniauth.params' => nil } }
|
|
531
|
+
|
|
532
|
+
it 'returns Microsoft Graph profile URL' do
|
|
533
|
+
expect(subject.determine_profile_endpoint(request)).to eq('https://graph.microsoft.com/v1.0/me')
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
data/spec/spec_helper.rb
ADDED