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.
- 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
|