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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +63 -0
- data/.rubocop.yml +8 -5
- data/CHANGELOG.md +16 -0
- data/Gemfile +6 -0
- data/Guardfile +1 -1
- data/README.md +32 -20
- data/Rakefile +2 -0
- data/lib/omniauth/openid_connect/errors.rb +2 -0
- data/lib/omniauth/openid_connect/version.rb +1 -1
- data/lib/omniauth/strategies/openid_connect.rb +66 -18
- data/omniauth_openid_connect.gemspec +13 -6
- data/test/lib/omniauth/strategies/openid_connect_test.rb +226 -79
- data/test/strategy_test_case.rb +37 -3
- data/test/test_helper.rb +17 -7
- metadata +41 -50
- data/.github/config/rubocop_linter_action.yml +0 -59
- data/.github/workflows/rubocop.yml +0 -22
- data/.travis.yml +0 -8
- data/test/fixtures/id_token.txt +0 -1
- data/test/fixtures/jwks.json +0 -8
@@ -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 =
|
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
|
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(:
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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(
|
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(:
|
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 =
|
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(
|
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
|
-
|
199
|
-
request.stubs(:
|
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 =
|
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(
|
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
|
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
|
-
|
244
|
+
request.stubs(:params).returns('code' => code, 'state' => state, 'nonce' => nonce)
|
245
|
+
request.stubs(:path).returns('')
|
230
246
|
|
231
|
-
|
232
|
-
|
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(
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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 =
|
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(
|
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
|
-
|
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?
|
600
|
+
assert(result.is_a?(Array))
|
476
601
|
assert(result[0] == 302, 'Redirect')
|
477
|
-
assert(result[1][
|
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(:
|
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(:
|
506
|
-
strategy.call!('rack.session' => {
|
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 =
|
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:
|
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(:
|
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
|
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 =
|
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
|
-
|
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(:
|
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
|