omniauth_openid_connect 0.3.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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