omniauth_openid_connect 0.3.5 → 0.5.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.
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../../../test_helper'
2
4
 
3
5
  module OmniAuth
4
6
  module Strategies
5
- class OpenIDConnectTest < StrategyTestCase
7
+ class OpenIDConnectTest < StrategyTestCase # rubocop:disable Metrics/ClassLength
6
8
  def test_client_options_defaults
7
9
  assert_equal 'https', strategy.options.client_options.scheme
8
10
  assert_equal 443, strategy.options.client_options.port
@@ -11,7 +13,7 @@ module OmniAuth
11
13
  end
12
14
 
13
15
  def test_request_phase
14
- expected_redirect = /^https:\/\/example\.com\/authorize\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$/
16
+ expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$}
15
17
  strategy.options.issuer = 'example.com'
16
18
  strategy.options.client_options.host = 'example.com'
17
19
  strategy.expects(:redirect).with(regexp_matches(expected_redirect))
@@ -19,7 +21,7 @@ module OmniAuth
19
21
  end
20
22
 
21
23
  def test_logout_phase_with_discovery
22
- expected_redirect = %r{^https:\/\/example\.com\/logout$}
24
+ expected_redirect = %r{^https://example\.com/logout$}
23
25
  strategy.options.client_options.host = 'example.com'
24
26
  strategy.options.discovery = true
25
27
 
@@ -36,6 +38,7 @@ module OmniAuth
36
38
  ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
37
39
 
38
40
  request.stubs(:path_info).returns('/auth/openid_connect/logout')
41
+ request.stubs(:path).returns('/auth/openid_connect/logout')
39
42
 
40
43
  strategy.expects(:redirect).with(regexp_matches(expected_redirect))
41
44
  strategy.other_phase
@@ -60,6 +63,7 @@ module OmniAuth
60
63
  ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
61
64
 
62
65
  request.stubs(:path_info).returns('/auth/openid_connect/logout')
66
+ request.stubs(:path).returns('/auth/openid_connect/logout')
63
67
 
64
68
  strategy.expects(:redirect).with(expected_redirect)
65
69
  strategy.other_phase
@@ -69,14 +73,14 @@ module OmniAuth
69
73
  strategy.options.issuer = 'example.com'
70
74
  strategy.options.client_options.host = 'example.com'
71
75
 
72
- request.stubs(:path_info).returns('/auth/openid_connect/logout')
76
+ request.stubs(:path).returns('/auth/openid_connect/logout')
73
77
 
74
78
  strategy.expects(:call_app!)
75
79
  strategy.other_phase
76
80
  end
77
81
 
78
82
  def test_request_phase_with_params
79
- expected_redirect = /^https:\/\/example\.com\/authorize\?claims_locales=es&client_id=1234&login_hint=john.doe%40example.com&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}&ui_locales=en$/
83
+ expected_redirect = %r{^https://example\.com/authorize\?claims_locales=es&client_id=1234&login_hint=john.doe%40example.com&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}&ui_locales=en$}
80
84
  strategy.options.issuer = 'example.com'
81
85
  strategy.options.client_options.host = 'example.com'
82
86
  request.stubs(:params).returns('login_hint' => 'john.doe@example.com', 'ui_locales' => 'en', 'claims_locales' => 'es')
@@ -86,7 +90,7 @@ module OmniAuth
86
90
  end
87
91
 
88
92
  def test_request_phase_with_discovery
89
- expected_redirect = /^https:\/\/example\.com\/authorization\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$/
93
+ expected_redirect = %r{^https://example\.com/authorization\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$}
90
94
  strategy.options.client_options.host = 'example.com'
91
95
  strategy.options.discovery = true
92
96
 
@@ -113,7 +117,7 @@ module OmniAuth
113
117
  end
114
118
 
115
119
  def test_request_phase_with_response_mode
116
- expected_redirect = /^https:\/\/example\.com\/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$/
120
+ expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$}
117
121
  strategy.options.issuer = 'example.com'
118
122
  strategy.options.response_mode = 'form_post'
119
123
  strategy.options.response_type = 'id_token'
@@ -124,7 +128,7 @@ module OmniAuth
124
128
  end
125
129
 
126
130
  def test_request_phase_with_response_mode_symbol
127
- expected_redirect = /^https:\/\/example\.com\/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$/
131
+ expected_redirect = %r{^https://example\.com/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$}
128
132
  strategy.options.issuer = 'example.com'
129
133
  strategy.options.response_mode = 'form_post'
130
134
  strategy.options.response_type = :id_token
@@ -137,19 +141,33 @@ module OmniAuth
137
141
  def test_option_acr_values
138
142
  strategy.options.client_options[:host] = 'foobar.com'
139
143
 
140
- assert(!(strategy.authorize_uri =~ /acr_values=/), 'URI must not contain acr_values')
144
+ refute_match(/acr_values=/, strategy.authorize_uri, 'URI must not contain acr_values')
141
145
 
142
146
  strategy.options.acr_values = 'urn:some:acr:values:value'
143
- assert(strategy.authorize_uri =~ /acr_values=/, 'URI must contain acr_values')
147
+ assert_match(/acr_values=/, strategy.authorize_uri, 'URI must contain acr_values')
144
148
  end
145
149
 
146
150
  def test_option_custom_attributes
147
151
  strategy.options.client_options[:host] = 'foobar.com'
148
- strategy.options.extra_authorize_params = {resource: 'xyz'}
152
+ strategy.options.extra_authorize_params = { resource: 'xyz' }
149
153
 
150
154
  assert(strategy.authorize_uri =~ /resource=xyz/, 'URI must contain custom params')
151
155
  end
152
156
 
157
+ def test_request_phase_with_allowed_params
158
+ strategy.options.issuer = 'example.com'
159
+ strategy.options.allow_authorize_params = %i[name logo resource]
160
+ strategy.options.extra_authorize_params = { resource: 'xyz' }
161
+ strategy.options.client_options.host = 'example.com'
162
+ request.stubs(:params).returns('name' => 'example', 'logo' => 'example_logo', 'resource' => 'abc',
163
+ 'not_allowed' => 'filter_me')
164
+
165
+ assert(strategy.authorize_uri =~ /resource=xyz/, 'URI must contain fixed param resource')
166
+ assert(strategy.authorize_uri =~ /name=example/, 'URI must contain dynamic param name')
167
+ assert(strategy.authorize_uri =~ /logo=example_logo/, 'URI must contain dynamic param logo')
168
+ refute(strategy.authorize_uri =~ /not_allowed=filter_me/, 'URI must filter not allowed param')
169
+ end
170
+
153
171
  def test_uid
154
172
  assert_equal user_info.sub, strategy.uid
155
173
 
@@ -160,16 +178,15 @@ module OmniAuth
160
178
  assert_equal user_info.sub, strategy.uid
161
179
  end
162
180
 
163
- def test_callback_phase(session = {}, params = {})
181
+ def test_callback_phase(_session = {}, _params = {}) # rubocop:disable Metrics/AbcSize
164
182
  code = SecureRandom.hex(16)
165
183
  state = SecureRandom.hex(16)
166
- nonce = SecureRandom.hex(16)
167
184
  request.stubs(:params).returns('code' => code, 'state' => state)
168
- request.stubs(:path_info).returns('')
185
+ request.stubs(:path).returns('')
169
186
 
170
187
  strategy.options.issuer = 'example.com'
171
188
  strategy.options.client_signing_alg = :RS256
172
- strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json')
189
+ strategy.options.client_jwk_signing_key = jwks.to_s
173
190
  strategy.options.response_type = 'code'
174
191
 
175
192
  strategy.unstub(:user_info)
@@ -178,7 +195,7 @@ module OmniAuth
178
195
  access_token.stubs(:refresh_token)
179
196
  access_token.stubs(:expires_in)
180
197
  access_token.stubs(:scope)
181
- access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt'))
198
+ access_token.stubs(:id_token).returns(jwt.to_s)
182
199
  client.expects(:access_token!).at_least_once.returns(access_token)
183
200
  access_token.expects(:userinfo!).returns(user_info)
184
201
 
@@ -193,15 +210,13 @@ module OmniAuth
193
210
  end
194
211
 
195
212
  def test_callback_phase_with_id_token
196
- code = SecureRandom.hex(16)
197
213
  state = SecureRandom.hex(16)
198
- nonce = SecureRandom.hex(16)
199
- request.stubs(:params).returns('id_token' => code, 'state' => state)
200
- request.stubs(:path_info).returns('')
214
+ request.stubs(:params).returns('id_token' => jwt.to_s, 'state' => state)
215
+ request.stubs(:path).returns('')
201
216
 
202
217
  strategy.options.issuer = 'example.com'
203
218
  strategy.options.client_signing_alg = :RS256
204
- strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json')
219
+ strategy.options.client_jwk_signing_key = jwks.to_json
205
220
  strategy.options.response_type = 'id_token'
206
221
 
207
222
  strategy.unstub(:user_info)
@@ -210,7 +225,7 @@ module OmniAuth
210
225
  access_token.stubs(:refresh_token)
211
226
  access_token.stubs(:expires_in)
212
227
  access_token.stubs(:scope)
213
- access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt'))
228
+ access_token.stubs(:id_token).returns(jwt.to_s)
214
229
 
215
230
  id_token = stub('OpenIDConnect::ResponseObject::IdToken')
216
231
  id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
@@ -222,14 +237,86 @@ module OmniAuth
222
237
  strategy.callback_phase
223
238
  end
224
239
 
225
- def test_callback_phase_with_discovery
240
+ def test_callback_phase_with_id_token_and_param_provided_nonce # rubocop:disable Metrics/AbcSize
226
241
  code = SecureRandom.hex(16)
227
242
  state = SecureRandom.hex(16)
228
243
  nonce = SecureRandom.hex(16)
229
- jwks = JSON::JWK::Set.new(JSON.parse(File.read('test/fixtures/jwks.json'))['keys'])
244
+ request.stubs(:params).returns('code' => code, 'state' => state, 'nonce' => nonce)
245
+ request.stubs(:path).returns('')
230
246
 
231
- request.stubs(:params).returns('code' => code, 'state' => state)
232
- request.stubs(:path_info).returns('')
247
+ strategy.options.issuer = 'example.com'
248
+ strategy.options.client_signing_alg = :RS256
249
+ strategy.options.client_jwk_signing_key = jwks.to_s
250
+ strategy.options.response_type = 'code'
251
+
252
+ strategy.unstub(:user_info)
253
+ access_token = stub('OpenIDConnect::AccessToken')
254
+ access_token.stubs(:access_token)
255
+ access_token.stubs(:refresh_token)
256
+ access_token.stubs(:expires_in)
257
+ access_token.stubs(:scope)
258
+ access_token.stubs(:id_token).returns(jwt.to_s)
259
+ client.expects(:access_token!).at_least_once.returns(access_token)
260
+ access_token.expects(:userinfo!).returns(user_info)
261
+
262
+ id_token = stub('OpenIDConnect::ResponseObject::IdToken')
263
+ id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
264
+ id_token.stubs(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, nonce: nonce).returns(true)
265
+ id_token.expects(:verify!)
266
+
267
+ strategy.expects(:decode_id_token).twice.with(access_token.id_token).returns(id_token)
268
+ strategy.call!('rack.session' => { 'omniauth.state' => state })
269
+ strategy.callback_phase
270
+ end
271
+
272
+ def test_callback_phase_with_discovery # rubocop:disable Metrics/AbcSize
273
+ state = SecureRandom.hex(16)
274
+
275
+ request.stubs(:params).returns('code' => jwt.to_s, 'state' => state)
276
+ request.stubs(:path).returns('')
277
+
278
+ strategy.options.client_options.host = 'example.com'
279
+ strategy.options.discovery = true
280
+
281
+ issuer = stub('OpenIDConnect::Discovery::Issuer')
282
+ issuer.stubs(:issuer).returns('https://example.com/')
283
+ ::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer)
284
+
285
+ config = stub('OpenIDConnect::Discovery::Provder::Config')
286
+ config.stubs(:authorization_endpoint).returns('https://example.com/authorization')
287
+ config.stubs(:token_endpoint).returns('https://example.com/token')
288
+ config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo')
289
+ config.stubs(:jwks_uri).returns('https://example.com/jwks')
290
+ config.stubs(:jwks).returns(JSON::JWK::Set.new(jwks['keys']))
291
+
292
+ ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
293
+
294
+ id_token = stub('OpenIDConnect::ResponseObject::IdToken')
295
+ id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
296
+ id_token.stubs(:verify!).with(issuer: 'https://example.com/', client_id: @identifier, nonce: nonce).returns(true)
297
+ ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token)
298
+
299
+ strategy.unstub(:user_info)
300
+ access_token = stub('OpenIDConnect::AccessToken')
301
+ access_token.stubs(:access_token)
302
+ access_token.stubs(:refresh_token)
303
+ access_token.stubs(:expires_in)
304
+ access_token.stubs(:scope)
305
+ access_token.stubs(:id_token).returns(jwt.to_s)
306
+ client.expects(:access_token!).at_least_once.returns(access_token)
307
+ access_token.expects(:userinfo!).returns(user_info)
308
+
309
+ strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
310
+ strategy.callback_phase
311
+ end
312
+
313
+ def test_callback_phase_with_no_state_without_state_verification # rubocop:disable Metrics/AbcSize
314
+ code = SecureRandom.hex(16)
315
+
316
+ strategy.options.require_state = false
317
+
318
+ request.stubs(:params).returns('code' => code)
319
+ request.stubs(:path).returns('')
233
320
 
234
321
  strategy.options.client_options.host = 'example.com'
235
322
  strategy.options.discovery = true
@@ -243,7 +330,7 @@ module OmniAuth
243
330
  config.stubs(:token_endpoint).returns('https://example.com/token')
244
331
  config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo')
245
332
  config.stubs(:jwks_uri).returns('https://example.com/jwks')
246
- config.stubs(:jwks).returns(jwks)
333
+ config.stubs(:jwks).returns(JSON::JWK::Set.new(jwks['keys']))
247
334
 
248
335
  ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config)
249
336
 
@@ -258,21 +345,67 @@ module OmniAuth
258
345
  access_token.stubs(:refresh_token)
259
346
  access_token.stubs(:expires_in)
260
347
  access_token.stubs(:scope)
261
- access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt'))
348
+ access_token.stubs(:id_token).returns(jwt.to_s)
262
349
  client.expects(:access_token!).at_least_once.returns(access_token)
263
350
  access_token.expects(:userinfo!).returns(user_info)
264
351
 
352
+ strategy.call!('rack.session' => { 'omniauth.nonce' => nonce })
353
+ strategy.callback_phase
354
+ end
355
+
356
+ def test_callback_phase_with_invalid_state_without_state_verification
357
+ code = SecureRandom.hex(16)
358
+ state = SecureRandom.hex(16)
359
+
360
+ strategy.options.require_state = false
361
+
362
+ request.stubs(:params).returns('code' => code, 'state' => 'foobar')
363
+ request.stubs(:path).returns('')
364
+
365
+ strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
366
+ strategy.expects(:fail!)
367
+ strategy.callback_phase
368
+ end
369
+
370
+ def test_callback_phase_with_jwks_uri
371
+ id_token = jwt.to_s
372
+ state = SecureRandom.hex(16)
373
+ request.stubs(:params).returns('id_token' => id_token, 'state' => state)
374
+ request.stubs(:path_info).returns('')
375
+
376
+ strategy.options.issuer = 'example.com'
377
+ strategy.options.client_options.jwks_uri = 'https://jwks.example.com'
378
+ strategy.options.response_type = 'id_token'
379
+
380
+ HTTPClient
381
+ .any_instance.stubs(:get_content)
382
+ .with(strategy.options.client_options.jwks_uri)
383
+ .returns(jwks.to_json)
384
+
385
+ strategy.unstub(:user_info)
386
+ access_token = stub('OpenIDConnect::AccessToken')
387
+ access_token.stubs(:access_token)
388
+ access_token.stubs(:refresh_token)
389
+ access_token.stubs(:expires_in)
390
+ access_token.stubs(:scope)
391
+ access_token.stubs(:id_token).returns(id_token)
392
+
393
+ id_token = stub('OpenIDConnect::ResponseObject::IdToken')
394
+ id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email')
395
+ id_token.stubs(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, nonce: nonce).returns(true)
396
+ ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token)
397
+ id_token.expects(:verify!)
398
+
265
399
  strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
266
400
  strategy.callback_phase
267
401
  end
268
402
 
269
403
  def test_callback_phase_with_error
270
404
  state = SecureRandom.hex(16)
271
- nonce = SecureRandom.hex(16)
272
405
  request.stubs(:params).returns('error' => 'invalid_request')
273
- request.stubs(:path_info).returns('')
406
+ request.stubs(:path).returns('')
274
407
 
275
- strategy.call!({'rack.session' => {'omniauth.state' => state, 'omniauth.nonce' => nonce}})
408
+ strategy.call!({ 'rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce } })
276
409
  strategy.expects(:fail!)
277
410
  strategy.callback_phase
278
411
  end
@@ -280,9 +413,8 @@ module OmniAuth
280
413
  def test_callback_phase_with_invalid_state
281
414
  code = SecureRandom.hex(16)
282
415
  state = SecureRandom.hex(16)
283
- nonce = SecureRandom.hex(16)
284
416
  request.stubs(:params).returns('code' => code, 'state' => 'foobar')
285
- request.stubs(:path_info).returns('')
417
+ request.stubs(:path).returns('')
286
418
 
287
419
  strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
288
420
  strategy.expects(:fail!)
@@ -291,9 +423,8 @@ module OmniAuth
291
423
 
292
424
  def test_callback_phase_without_code
293
425
  state = SecureRandom.hex(16)
294
- nonce = SecureRandom.hex(16)
295
426
  request.stubs(:params).returns('state' => state)
296
- request.stubs(:path_info).returns('')
427
+ request.stubs(:path).returns('')
297
428
 
298
429
  strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
299
430
 
@@ -303,9 +434,8 @@ module OmniAuth
303
434
 
304
435
  def test_callback_phase_without_id_token
305
436
  state = SecureRandom.hex(16)
306
- nonce = SecureRandom.hex(16)
307
437
  request.stubs(:params).returns('state' => state)
308
- request.stubs(:path_info).returns('')
438
+ request.stubs(:path).returns('')
309
439
  strategy.options.response_type = 'id_token'
310
440
 
311
441
  strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
@@ -316,9 +446,8 @@ module OmniAuth
316
446
 
317
447
  def test_callback_phase_without_id_token_symbol
318
448
  state = SecureRandom.hex(16)
319
- nonce = SecureRandom.hex(16)
320
449
  request.stubs(:params).returns('state' => state)
321
- request.stubs(:path_info).returns('')
450
+ request.stubs(:path).returns('')
322
451
  strategy.options.response_type = :id_token
323
452
 
324
453
  strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
@@ -330,9 +459,8 @@ module OmniAuth
330
459
  def test_callback_phase_with_timeout
331
460
  code = SecureRandom.hex(16)
332
461
  state = SecureRandom.hex(16)
333
- nonce = SecureRandom.hex(16)
334
462
  request.stubs(:params).returns('code' => code, 'state' => state)
335
- request.stubs(:path_info).returns('')
463
+ request.stubs(:path).returns('')
336
464
 
337
465
  strategy.options.issuer = 'example.com'
338
466
 
@@ -350,9 +478,8 @@ module OmniAuth
350
478
  def test_callback_phase_with_etimeout
351
479
  code = SecureRandom.hex(16)
352
480
  state = SecureRandom.hex(16)
353
- nonce = SecureRandom.hex(16)
354
481
  request.stubs(:params).returns('code' => code, 'state' => state)
355
- request.stubs(:path_info).returns('')
482
+ request.stubs(:path).returns('')
356
483
 
357
484
  strategy.options.issuer = 'example.com'
358
485
 
@@ -370,9 +497,8 @@ module OmniAuth
370
497
  def test_callback_phase_with_socket_error
371
498
  code = SecureRandom.hex(16)
372
499
  state = SecureRandom.hex(16)
373
- nonce = SecureRandom.hex(16)
374
500
  request.stubs(:params).returns('code' => code, 'state' => state)
375
- request.stubs(:path_info).returns('')
501
+ request.stubs(:path).returns('')
376
502
 
377
503
  strategy.options.issuer = 'example.com'
378
504
 
@@ -390,9 +516,8 @@ module OmniAuth
390
516
  def test_callback_phase_with_rack_oauth2_client_error
391
517
  code = SecureRandom.hex(16)
392
518
  state = SecureRandom.hex(16)
393
- nonce = SecureRandom.hex(16)
394
519
  request.stubs(:params).returns('code' => code, 'state' => state)
395
- request.stubs(:path_info).returns('')
520
+ request.stubs(:path).returns('')
396
521
 
397
522
  strategy.options.issuer = 'example.com'
398
523
 
@@ -411,6 +536,7 @@ module OmniAuth
411
536
  info = strategy.info
412
537
  assert_equal user_info.name, info[:name]
413
538
  assert_equal user_info.email, info[:email]
539
+ assert_equal user_info.email_verified, info[:email_verified]
414
540
  assert_equal user_info.preferred_username, info[:nickname]
415
541
  assert_equal user_info.given_name, info[:first_name]
416
542
  assert_equal user_info.family_name, info[:last_name]
@@ -427,7 +553,7 @@ module OmniAuth
427
553
  def test_credentials
428
554
  strategy.options.issuer = 'example.com'
429
555
  strategy.options.client_signing_alg = :RS256
430
- strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json')
556
+ strategy.options.client_jwk_signing_key = jwks.to_json
431
557
 
432
558
  id_token = stub('OpenIDConnect::ResponseObject::IdToken')
433
559
  id_token.stubs(:verify!).returns(true)
@@ -438,7 +564,7 @@ module OmniAuth
438
564
  access_token.stubs(:refresh_token).returns(SecureRandom.hex(16))
439
565
  access_token.stubs(:expires_in).returns(Time.now)
440
566
  access_token.stubs(:scope).returns('openidconnect')
441
- access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt'))
567
+ access_token.stubs(:id_token).returns(jwt.to_s)
442
568
 
443
569
  client.expects(:access_token!).returns(access_token)
444
570
  access_token.expects(:refresh_token).returns(access_token.refresh_token)
@@ -450,7 +576,7 @@ module OmniAuth
450
576
  token: access_token.access_token,
451
577
  refresh_token: access_token.refresh_token,
452
578
  expires_in: access_token.expires_in,
453
- scope: access_token.scope
579
+ scope: access_token.scope,
454
580
  },
455
581
  strategy.credentials
456
582
  )
@@ -458,11 +584,10 @@ module OmniAuth
458
584
 
459
585
  def test_option_send_nonce
460
586
  strategy.options.client_options[:host] = 'foobar.com'
461
-
462
- assert(strategy.authorize_uri =~ /nonce=/, 'URI must contain nonce')
587
+ assert_match(/nonce/, strategy.authorize_uri, 'URI must contain nonce')
463
588
 
464
589
  strategy.options.send_nonce = false
465
- assert(!(strategy.authorize_uri =~ /nonce=/), 'URI must not contain nonce')
590
+ refute_match(/nonce/, strategy.authorize_uri, 'URI must not contain nonce')
466
591
  end
467
592
 
468
593
  def test_failure_endpoint_redirect
@@ -472,9 +597,9 @@ module OmniAuth
472
597
 
473
598
  result = strategy.callback_phase
474
599
 
475
- assert(result.is_a? Array)
600
+ assert(result.is_a?(Array))
476
601
  assert(result[0] == 302, 'Redirect')
477
- assert(result[1]["Location"] =~ /\/auth\/failure/)
602
+ assert(result[1]['Location'] =~ %r{/auth/failure})
478
603
  end
479
604
 
480
605
  def test_state
@@ -493,7 +618,7 @@ module OmniAuth
493
618
  # the following should fail because the wrong state is passed to the callback
494
619
  code = SecureRandom.hex(16)
495
620
  request.stubs(:params).returns('code' => code, 'state' => 43)
496
- request.stubs(:path_info).returns('')
621
+ request.stubs(:path).returns('')
497
622
 
498
623
  strategy.call!('rack.session' => session)
499
624
  strategy.expects(:fail!)
@@ -502,8 +627,8 @@ module OmniAuth
502
627
 
503
628
  def test_dynamic_state
504
629
  # Stub request parameters
505
- request.stubs(:path_info).returns('')
506
- strategy.call!('rack.session' => { }, QUERY_STRING: { state: 'abc', client_id: '123' } )
630
+ request.stubs(:path).returns('')
631
+ strategy.call!('rack.session' => {}, QUERY_STRING: { state: 'abc', client_id: '123' })
507
632
 
508
633
  strategy.options.state = lambda { |env|
509
634
  # Get params from request, e.g. CGI.parse(env['QUERY_STRING'])
@@ -519,23 +644,22 @@ module OmniAuth
519
644
 
520
645
  def test_option_client_auth_method
521
646
  state = SecureRandom.hex(16)
522
- nonce = SecureRandom.hex(16)
523
647
 
524
648
  opts = strategy.options.client_options
525
649
  opts[:host] = 'foobar.com'
526
650
  strategy.options.issuer = 'foobar.com'
527
651
  strategy.options.client_auth_method = :not_basic
528
652
  strategy.options.client_signing_alg = :RS256
529
- strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json')
653
+ strategy.options.client_jwk_signing_key = jwks.to_json
530
654
 
531
655
  json_response = {
532
656
  access_token: 'test_access_token',
533
- id_token: File.read('test/fixtures/id_token.txt'),
657
+ id_token: jwt.to_s,
534
658
  token_type: 'Bearer',
535
659
  }.to_json
536
660
  success = Struct.new(:status, :body).new(200, json_response)
537
661
 
538
- request.stubs(:path_info).returns('')
662
+ request.stubs(:path).returns('')
539
663
  strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce })
540
664
 
541
665
  id_token = stub('OpenIDConnect::ResponseObject::IdToken')
@@ -548,21 +672,19 @@ module OmniAuth
548
672
  {}
549
673
  ).returns(success)
550
674
 
551
- assert(strategy.send :access_token)
675
+ assert(strategy.send(:access_token))
552
676
  end
553
677
 
554
678
  def test_public_key_with_jwks
555
679
  strategy.options.client_signing_alg = :RS256
556
- strategy.options.client_jwk_signing_key = File.read('./test/fixtures/jwks.json')
680
+ strategy.options.client_jwk_signing_key = jwks.to_json
557
681
 
558
682
  assert_equal JSON::JWK::Set, strategy.public_key.class
559
683
  end
560
684
 
561
685
  def test_public_key_with_jwk
562
686
  strategy.options.client_signing_alg = :RS256
563
- jwks_str = File.read('./test/fixtures/jwks.json')
564
- jwks = JSON.parse(jwks_str)
565
- jwk = jwks['keys'].first
687
+ jwk = jwks[:keys].first
566
688
  strategy.options.client_jwk_signing_key = jwk.to_json
567
689
 
568
690
  assert_equal JSON::JWK, strategy.public_key.class
@@ -582,25 +704,15 @@ module OmniAuth
582
704
 
583
705
  def test_id_token_auth_hash
584
706
  state = SecureRandom.hex(16)
585
- nonce = SecureRandom.hex(16)
586
707
  strategy.options.response_type = 'id_token'
587
708
  strategy.options.issuer = 'example.com'
588
709
 
589
710
  id_token = stub('OpenIDConnect::ResponseObject::IdToken')
590
711
  id_token.stubs(:verify!).returns(true)
591
- id_token.stubs(:raw_attributes, :to_h).returns(
592
- {
593
- "iss": "http://server.example.com",
594
- "sub": "248289761001",
595
- "aud": "s6BhdRkqt3",
596
- "nonce": "n-0S6_WzA2Mj",
597
- "exp": 1311281970,
598
- "iat": 1311280970,
599
- }
600
- )
712
+ id_token.stubs(:raw_attributes, :to_h).returns(payload)
601
713
 
602
714
  request.stubs(:params).returns('state' => state, 'nounce' => nonce, 'id_token' => id_token)
603
- request.stubs(:path_info).returns('')
715
+ request.stubs(:path).returns('')
604
716
 
605
717
  strategy.stubs(:decode_id_token).returns(id_token)
606
718
  strategy.stubs(:stored_state).returns(state)
@@ -615,6 +727,41 @@ module OmniAuth
615
727
  assert auth_hash.key?('extra')
616
728
  assert auth_hash['extra'].key?('raw_info')
617
729
  end
730
+
731
+ def test_option_pkce
732
+ strategy.options.client_options[:host] = 'example.com'
733
+
734
+ # test pkce disabled
735
+ strategy.options.pkce = false
736
+
737
+ assert((strategy.authorize_uri !~ /code_challenge=/), 'URI must not contain code challenge param')
738
+ assert((strategy.authorize_uri !~ /code_challenge_method=/), 'URI must not contain code challenge method param')
739
+
740
+ # test pkce enabled with default opts
741
+ strategy.options.pkce = true
742
+
743
+ assert(strategy.authorize_uri =~ /code_challenge=/, 'URI must contain code challenge param')
744
+ assert(strategy.authorize_uri =~ /code_challenge_method=/, 'URI must contain code challenge method param')
745
+
746
+ # test pkce with custom verifier code
747
+ strategy.options.pkce_verifier = proc { 'dummy_verifier' }
748
+ code_challenge_value = Base64.urlsafe_encode64(
749
+ Digest::SHA2.digest(strategy.options.pkce_verifier.call),
750
+ padding: false
751
+ )
752
+
753
+ assert(strategy.authorize_uri =~ /#{Regexp.quote(code_challenge_value)}/, 'URI must contain code challenge value')
754
+
755
+ # test pkce with custom options and plain text code
756
+ strategy.options.pkce_options =
757
+ {
758
+ code_challenge: proc { |verifier| verifier },
759
+ code_challenge_method: 'plain',
760
+ }
761
+
762
+ assert(strategy.authorize_uri =~ /#{Regexp.quote(strategy.options.pkce_verifier.call)}/,
763
+ 'URI must contain code challenge value')
764
+ end
618
765
  end
619
766
  end
620
767
  end