udap_security_test_kit 0.11.3 → 0.11.4
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/config/presets/UDAP_RunServerAgainstClient.json.erb +4 -4
- data/lib/udap_security_test_kit/client_suite/access_ac_group.rb +25 -0
- data/lib/udap_security_test_kit/client_suite/access_ac_interaction_test.rb +59 -0
- data/lib/udap_security_test_kit/client_suite/access_cc_group.rb +23 -0
- data/lib/udap_security_test_kit/client_suite/access_cc_interaction_test.rb +49 -0
- data/lib/udap_security_test_kit/client_suite/authorization_request_verification_test.rb +83 -0
- data/lib/udap_security_test_kit/client_suite/client_descriptions.rb +70 -0
- data/lib/udap_security_test_kit/client_suite/client_options.rb +20 -0
- data/lib/udap_security_test_kit/client_suite/oidc_jwks.json +32 -0
- data/lib/udap_security_test_kit/client_suite/oidc_jwks.rb +27 -0
- data/lib/udap_security_test_kit/client_suite/registration_ac_group.rb +18 -0
- data/lib/udap_security_test_kit/client_suite/registration_ac_verification_test.rb +38 -0
- data/lib/udap_security_test_kit/client_suite/registration_cc_group.rb +18 -0
- data/lib/udap_security_test_kit/client_suite/registration_cc_verification_test.rb +38 -0
- data/lib/udap_security_test_kit/client_suite/{client_registration_interaction_test.rb → registration_interaction_test.rb} +11 -4
- data/lib/udap_security_test_kit/client_suite/{client_registration_verification_test.rb → registration_request_verification.rb} +38 -40
- data/lib/udap_security_test_kit/client_suite/token_request_ac_verification_test.rb +49 -0
- data/lib/udap_security_test_kit/client_suite/token_request_cc_verification_test.rb +49 -0
- data/lib/udap_security_test_kit/client_suite/{client_token_request_verification_test.rb → token_request_verification.rb} +91 -46
- data/lib/udap_security_test_kit/client_suite/{client_token_use_verification_test.rb → token_use_verification_test.rb} +0 -3
- data/lib/udap_security_test_kit/client_suite.rb +46 -17
- data/lib/udap_security_test_kit/docs/udap_client_suite_description.md +74 -31
- data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder_endpoint.rb +96 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/authorization_endpoint.rb +28 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration_endpoint.rb +31 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/token_endpoint.rb +56 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_authorization_response_creation.rb +63 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_registration_response_creation.rb +28 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_token_response_creation.rb +218 -0
- data/lib/udap_security_test_kit/endpoints/mock_udap_server.rb +112 -31
- data/lib/udap_security_test_kit/metadata.rb +1 -1
- data/lib/udap_security_test_kit/tags.rb +4 -0
- data/lib/udap_security_test_kit/urls.rb +15 -8
- data/lib/udap_security_test_kit/version.rb +2 -2
- metadata +28 -12
- data/lib/udap_security_test_kit/client_suite/client_access_group.rb +0 -22
- data/lib/udap_security_test_kit/client_suite/client_access_interaction_test.rb +0 -53
- data/lib/udap_security_test_kit/client_suite/client_registration_group.rb +0 -26
- data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder.rb +0 -52
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration.rb +0 -57
- data/lib/udap_security_test_kit/endpoints/mock_udap_server/token.rb +0 -27
@@ -3,35 +3,24 @@ require_relative '../urls'
|
|
3
3
|
require_relative '../endpoints/mock_udap_server'
|
4
4
|
|
5
5
|
module UDAPSecurityTestKit
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
id :udap_client_registration_verification
|
10
|
-
title 'Verify UDAP Registration'
|
11
|
-
description %(
|
12
|
-
During this test, Inferno will verify that the client's UDAP
|
13
|
-
registration request is conformant.
|
14
|
-
)
|
15
|
-
input :udap_client_uri,
|
16
|
-
optional: false
|
17
|
-
|
18
|
-
run do
|
19
|
-
omit_if udap_client_uri.blank?, # for re-use: mark the udap_client_uri input as optional when importing to enable
|
20
|
-
'Not configured for UDAP authentication.'
|
21
|
-
|
6
|
+
module RegistrationRequestVerification
|
7
|
+
def load_registration_requests_for_client_uri(client_uri)
|
22
8
|
load_tagged_requests(UDAP_TAG, REGISTRATION_TAG)
|
23
|
-
|
9
|
+
requests.select do |reg_request|
|
10
|
+
registered_uri = MockUDAPServer.udap_client_uri_from_registration_payload(
|
11
|
+
MockUDAPServer.parsed_request_body(reg_request)
|
12
|
+
)
|
13
|
+
client_uri == registered_uri
|
14
|
+
end
|
15
|
+
end
|
24
16
|
|
25
|
-
|
17
|
+
def verify_registration_request(oauth_flow, verified_request)
|
26
18
|
parsed_body = MockUDAPServer.parsed_request_body(verified_request)
|
27
19
|
assert parsed_body.present?, 'Registration request body is not valid JSON.'
|
28
20
|
|
29
21
|
check_request_body(parsed_body)
|
30
|
-
check_software_statement(parsed_body['software_statement'], verified_request.created_at)
|
31
|
-
|
32
|
-
assert messages.none? { |msg|
|
33
|
-
msg[:type] == 'error'
|
34
|
-
}, 'Invalid registration request. See messages for details.'
|
22
|
+
check_software_statement(oauth_flow, parsed_body['software_statement'], verified_request.created_at)
|
23
|
+
output udap_registration_jwt: parsed_body['software_statement']
|
35
24
|
end
|
36
25
|
|
37
26
|
def check_request_body(request_body)
|
@@ -46,14 +35,14 @@ module UDAPSecurityTestKit
|
|
46
35
|
return unless request_body['certifications'].present?
|
47
36
|
|
48
37
|
request_body['certifications'].each_with_index do |certification_jwt, index|
|
49
|
-
JWT.
|
38
|
+
JWT.decode(certification_jwt, nil, false)
|
50
39
|
rescue StandardError => e
|
51
40
|
add_message('error',
|
52
41
|
"Certification #{index + 1} in the registration request is not a valid signed jwt: #{e}")
|
53
42
|
end
|
54
43
|
end
|
55
44
|
|
56
|
-
def check_software_statement(software_statement_jwt, request_time)
|
45
|
+
def check_software_statement(oauth_flow, software_statement_jwt, request_time)
|
57
46
|
unless software_statement_jwt.present?
|
58
47
|
add_message('error',
|
59
48
|
'Registration is missing a `software_statement` key')
|
@@ -69,11 +58,11 @@ module UDAPSecurityTestKit
|
|
69
58
|
end
|
70
59
|
|
71
60
|
# headers checked with signature
|
72
|
-
check_software_statement_claims(claims, request_time)
|
61
|
+
check_software_statement_claims(oauth_flow, claims, request_time)
|
73
62
|
check_jwt_signature(software_statement_jwt)
|
74
63
|
end
|
75
64
|
|
76
|
-
def check_software_statement_claims(claims, request_time) # rubocop:disable Metrics/CyclomaticComplexity
|
65
|
+
def check_software_statement_claims(oauth_flow, claims, request_time) # rubocop:disable Metrics/CyclomaticComplexity
|
77
66
|
unless claims['iss'] == udap_client_uri
|
78
67
|
add_message('error',
|
79
68
|
'Registration software statement `iss` claim is incorrect: ' \
|
@@ -90,7 +79,7 @@ module UDAPSecurityTestKit
|
|
90
79
|
"expected '#{client_registration_url}', got '#{claims['aud']}'")
|
91
80
|
end
|
92
81
|
|
93
|
-
check_software_statement_grant_types(claims)
|
82
|
+
check_software_statement_grant_types(oauth_flow, claims)
|
94
83
|
MockUDAPServer.check_jwt_timing(claims['iat'], claims['exp'], request_time)
|
95
84
|
|
96
85
|
add_message('error', 'Registration software statement `jti` claim is missing.') unless claims['jti'].present?
|
@@ -124,7 +113,7 @@ module UDAPSecurityTestKit
|
|
124
113
|
nil
|
125
114
|
end
|
126
115
|
|
127
|
-
def check_software_statement_grant_types(claims) # rubocop:disable Metrics/CyclomaticComplexity
|
116
|
+
def check_software_statement_grant_types(oauth_flow, claims) # rubocop:disable Metrics/CyclomaticComplexity
|
128
117
|
unless claims['grant_types'].present?
|
129
118
|
add_message('error', 'Registration software statement `grant_types` claim is missing')
|
130
119
|
return
|
@@ -157,6 +146,14 @@ module UDAPSecurityTestKit
|
|
157
146
|
"'authorization_code', 'client_credentials', and 'refresh_token")
|
158
147
|
end
|
159
148
|
|
149
|
+
if oauth_flow == CLIENT_CREDENTIALS_TAG && !has_client_credentials
|
150
|
+
add_message('error', 'Registration software statement `grant_types` must contain ' \
|
151
|
+
"''client_credentials' when testing the client credentials flow.")
|
152
|
+
end
|
153
|
+
if oauth_flow == AUTHORIZATION_CODE_TAG && !has_authorization_code
|
154
|
+
add_message('error', 'Registration software statement `grant_types` must contain ' \
|
155
|
+
"''authorization_code' when testing the authorization code flow.")
|
156
|
+
end
|
160
157
|
check_client_credentials_software_statement(claims) if has_client_credentials
|
161
158
|
check_authorization_code_software_statement(claims) if has_authorization_code
|
162
159
|
|
@@ -165,35 +162,36 @@ module UDAPSecurityTestKit
|
|
165
162
|
|
166
163
|
def check_authorization_code_software_statement(claims) # rubocop:disable Metrics/CyclomaticComplexity
|
167
164
|
if claims['redirect_uris'].blank?
|
168
|
-
add_message('error', 'Registration software statement `redirect_uris` must be present when' \
|
165
|
+
add_message('error', 'Registration software statement `redirect_uris` must be present when ' \
|
169
166
|
"the 'authorization_code' `grant_type` is requested.")
|
170
167
|
elsif !claims['redirect_uris'].is_a?(Array)
|
171
|
-
add_message('error', 'Registration software statement `redirect_uris` must be a list when' \
|
168
|
+
add_message('error', 'Registration software statement `redirect_uris` must be a list when ' \
|
172
169
|
"the 'authorization_code' `grant_type` is requested.")
|
173
170
|
else
|
174
|
-
claims['redirect_uris'].
|
171
|
+
claims['redirect_uris'].each_with_index do |redirect_uri, index|
|
175
172
|
unless valid_uri?(redirect_uri, required_scheme: 'https')
|
176
173
|
add_message('error', "Registration software statement `redirect_uris` entry #{index + 1} is invalid: " \
|
177
|
-
'
|
174
|
+
"'#{redirect_uri}' is not a valid https uri.")
|
178
175
|
end
|
179
176
|
end
|
180
177
|
end
|
181
178
|
|
182
179
|
if claims['logo_uri'].blank?
|
183
|
-
add_message('error', 'Registration software statement `logo_uri` must be present when' \
|
180
|
+
add_message('error', 'Registration software statement `logo_uri` must be present when ' \
|
184
181
|
"the 'authorization_code' `grant_type` is requested.")
|
185
182
|
else
|
186
183
|
unless valid_uri?(claims['logo_uri'], required_scheme: 'https')
|
187
|
-
add_message('error', 'Registration software statement `logo_uri` is invalid:
|
184
|
+
add_message('error', 'Registration software statement `logo_uri` is invalid: ' \
|
185
|
+
"'#{claims['logo_uri']}' is not a valid https uri.")
|
188
186
|
end
|
189
|
-
unless ['gif', 'jpg', 'jpeg', 'png'].include?(claims['logo_uri'].split
|
187
|
+
unless ['gif', 'jpg', 'jpeg', 'png'].include?(claims['logo_uri'].split('.').last.downcase)
|
190
188
|
add_message('error', 'Registration software statement `logo_uri` is invalid: it must point to a ' \
|
191
189
|
'PNG, JPG, or GIF file.')
|
192
190
|
end
|
193
191
|
end
|
194
192
|
|
195
193
|
if claims['response_types'].blank?
|
196
|
-
add_message('error', 'Registration software statement `response_types` must be present when' \
|
194
|
+
add_message('error', 'Registration software statement `response_types` must be present when ' \
|
197
195
|
"the 'authorization_code' `grant_type` is requested.")
|
198
196
|
else
|
199
197
|
unless claims['response_types'].is_a?(Array) &&
|
@@ -209,17 +207,17 @@ module UDAPSecurityTestKit
|
|
209
207
|
|
210
208
|
def check_client_credentials_software_statement(claims)
|
211
209
|
unless claims['redirect_uris'].nil?
|
212
|
-
add_message('error', 'Registration software statement `redirect_uris` must not be present when' \
|
210
|
+
add_message('error', 'Registration software statement `redirect_uris` must not be present when ' \
|
213
211
|
"the 'client_credentials' `grant_type` is requested.")
|
214
212
|
end
|
215
213
|
|
216
214
|
unless claims['response_types'].nil?
|
217
|
-
add_message('error', 'Registration software statement `response_types` must not be present when' \
|
215
|
+
add_message('error', 'Registration software statement `response_types` must not be present when ' \
|
218
216
|
"the 'client_credentials' `grant_type` is requested.")
|
219
217
|
end
|
220
218
|
|
221
219
|
if claims['grant_types'].include?('refresh_token')
|
222
|
-
add_message('error', "Registration software statement `response_types` cannot contain 'refresh_token' when" \
|
220
|
+
add_message('error', "Registration software statement `response_types` cannot contain 'refresh_token' when " \
|
223
221
|
"the 'client_credentials' `grant_type` is requested.")
|
224
222
|
end
|
225
223
|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative '../tags'
|
2
|
+
require_relative '../urls'
|
3
|
+
require_relative '../endpoints/mock_udap_server'
|
4
|
+
require_relative 'client_descriptions'
|
5
|
+
require_relative 'client_options'
|
6
|
+
require_relative 'token_request_verification'
|
7
|
+
|
8
|
+
module UDAPSecurityTestKit
|
9
|
+
class UDAPClientTokenRequestAuthorizationCodeVerification < Inferno::Test
|
10
|
+
include URLs
|
11
|
+
include TokenRequestVerification
|
12
|
+
|
13
|
+
id :udap_client_token_request_ac_verification
|
14
|
+
title 'Verify UDAP Authorization Code Token Requests'
|
15
|
+
description %(
|
16
|
+
Check that UDAP token requests are conformant.
|
17
|
+
)
|
18
|
+
|
19
|
+
input :client_id,
|
20
|
+
title: 'Client Id',
|
21
|
+
type: 'text',
|
22
|
+
locked: true,
|
23
|
+
description: INPUT_CLIENT_ID_DESCRIPTION_LOCKED
|
24
|
+
input :udap_registration_jwt,
|
25
|
+
title: 'Registered UDAP Software Statement',
|
26
|
+
type: 'textarea',
|
27
|
+
locked: 'true',
|
28
|
+
description: INPUT_UDAP_REGISTRATION_JWT_DESCRIPTION_LOCKED
|
29
|
+
output :udap_tokens
|
30
|
+
|
31
|
+
def client_suite_id
|
32
|
+
return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
|
33
|
+
|
34
|
+
UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
|
35
|
+
end
|
36
|
+
|
37
|
+
run do
|
38
|
+
load_tagged_requests(TOKEN_TAG, UDAP_TAG, AUTHORIZATION_CODE_TAG)
|
39
|
+
skip_if requests.blank?, 'No UDAP token requests made.'
|
40
|
+
load_tagged_requests(TOKEN_TAG, UDAP_TAG, REFRESH_TOKEN_TAG) # verify refresh_requests as well
|
41
|
+
|
42
|
+
verify_token_requests(AUTHORIZATION_CODE_TAG)
|
43
|
+
|
44
|
+
assert messages.none? { |msg|
|
45
|
+
msg[:type] == 'error'
|
46
|
+
}, 'Invalid token requests received. See messages for details.'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative '../tags'
|
2
|
+
require_relative '../urls'
|
3
|
+
require_relative '../endpoints/mock_udap_server'
|
4
|
+
require_relative 'client_descriptions'
|
5
|
+
require_relative 'client_options'
|
6
|
+
require_relative 'token_request_verification'
|
7
|
+
|
8
|
+
module UDAPSecurityTestKit
|
9
|
+
class UDAPClientTokenRequestClientCredentialsVerification < Inferno::Test
|
10
|
+
include URLs
|
11
|
+
include TokenRequestVerification
|
12
|
+
|
13
|
+
id :udap_client_token_request_cc_verification
|
14
|
+
title 'Verify UDAP Client Credentials Token Requests'
|
15
|
+
description %(
|
16
|
+
Check that UDAP token requests are conformant.
|
17
|
+
)
|
18
|
+
|
19
|
+
input :client_id,
|
20
|
+
title: 'Client Id',
|
21
|
+
type: 'text',
|
22
|
+
locked: true,
|
23
|
+
description: INPUT_CLIENT_ID_DESCRIPTION_LOCKED
|
24
|
+
input :udap_registration_jwt,
|
25
|
+
title: 'Registered UDAP Software Statement',
|
26
|
+
type: 'textarea',
|
27
|
+
locked: 'true',
|
28
|
+
description: INPUT_UDAP_REGISTRATION_JWT_DESCRIPTION_LOCKED
|
29
|
+
output :udap_tokens
|
30
|
+
|
31
|
+
def client_suite_id
|
32
|
+
return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
|
33
|
+
|
34
|
+
UDAPSecurityTestKit::UDAPSecurityClientTestSuite.id
|
35
|
+
end
|
36
|
+
|
37
|
+
run do
|
38
|
+
load_tagged_requests(TOKEN_TAG, UDAP_TAG, CLIENT_CREDENTIALS_TAG)
|
39
|
+
skip_if requests.blank?, 'No UDAP token requests made.'
|
40
|
+
load_tagged_requests(TOKEN_TAG, UDAP_TAG, REFRESH_TOKEN_TAG) # verify refresh_requests as well (shouldn't be any)
|
41
|
+
|
42
|
+
verify_token_requests(CLIENT_CREDENTIALS_TAG)
|
43
|
+
|
44
|
+
assert messages.none? { |msg|
|
45
|
+
msg[:type] == 'error'
|
46
|
+
}, 'Invalid token requests received. See messages for details.'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,59 +1,34 @@
|
|
1
|
-
require_relative '../tags'
|
2
|
-
require_relative '../urls'
|
3
|
-
require_relative '../endpoints/mock_udap_server'
|
4
|
-
|
5
1
|
module UDAPSecurityTestKit
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
id :udap_client_token_request_verification
|
10
|
-
title 'Verify UDAP Token Requests'
|
11
|
-
description %(
|
12
|
-
Check that UDAP token requests are conformant.
|
13
|
-
)
|
14
|
-
|
15
|
-
output :udap_demonstrated
|
16
|
-
output :udap_tokens
|
17
|
-
|
18
|
-
run do
|
19
|
-
load_tagged_requests(REGISTRATION_TAG, UDAP_TAG)
|
20
|
-
output udap_demonstrated: requests.present? ? 'Yes' : 'No'
|
21
|
-
omit_if requests.blank?, 'UDAP Authentication not demonstrated as a part of this test session.'
|
22
|
-
registration_request = requests.last
|
23
|
-
registration_assertion = MockUDAPServer.parsed_request_body(registration_request)['software_statement']
|
2
|
+
module TokenRequestVerification
|
3
|
+
def verify_token_requests(oauth_flow)
|
24
4
|
registration_token =
|
25
5
|
begin
|
26
|
-
JWT::EncodedToken.new(
|
6
|
+
JWT::EncodedToken.new(udap_registration_jwt)
|
27
7
|
rescue StandardError => e
|
28
8
|
assert false, "Registration request parsing failed: #{e}"
|
29
9
|
end
|
30
|
-
registered_client_id = JSON.parse(registration_request.response_body)['client_id']
|
31
|
-
|
32
|
-
requests.clear
|
33
|
-
load_tagged_requests(TOKEN_TAG, UDAP_TAG)
|
34
|
-
skip_if requests.blank?, 'No UDAP token requests made.'
|
35
10
|
|
36
11
|
jti_list = []
|
37
12
|
token_list = []
|
38
13
|
requests.each_with_index do |token_request, index|
|
39
14
|
request_params = URI.decode_www_form(token_request.request_body).to_h
|
40
|
-
|
41
|
-
|
42
|
-
|
15
|
+
if request_params['grant_type'] == 'refresh_token'
|
16
|
+
check_refresh_request_params(oauth_flow, request_params, index + 1)
|
17
|
+
else
|
18
|
+
check_request_params(oauth_flow, request_params, index + 1)
|
19
|
+
end
|
20
|
+
check_client_assertion(oauth_flow, request_params['client_assertion'], index + 1, jti_list, registration_token,
|
21
|
+
client_id, token_request.created_at)
|
43
22
|
token_list << extract_token_from_response(token_request)
|
44
23
|
end
|
45
24
|
|
46
25
|
output udap_tokens: token_list.compact.join("\n")
|
47
|
-
|
48
|
-
assert messages.none? { |msg|
|
49
|
-
msg[:type] == 'error'
|
50
|
-
}, 'Invalid token requests detected. See messages for details.'
|
51
26
|
end
|
52
27
|
|
53
|
-
def check_request_params(params, request_num)
|
54
|
-
if params['grant_type'] !=
|
28
|
+
def check_request_params(oauth_flow, params, request_num)
|
29
|
+
if params['grant_type'] != oauth_flow
|
55
30
|
add_message('error',
|
56
|
-
"Token request #{request_num} had an incorrect `grant_type`: expected '
|
31
|
+
"Token request #{request_num} had an incorrect `grant_type`: expected '#{oauth_flow}', " \
|
57
32
|
"but got '#{params['grant_type']}'")
|
58
33
|
end
|
59
34
|
if params['client_assertion_type'] != 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
@@ -62,15 +37,52 @@ module UDAPSecurityTestKit
|
|
62
37
|
"expected 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', " \
|
63
38
|
"but got '#{params['client_assertion_type']}'")
|
64
39
|
end
|
65
|
-
|
40
|
+
unless params['udap'].to_s == '1'
|
41
|
+
add_message('error',
|
42
|
+
"Token request #{request_num} had an incorrect `udap`: " \
|
43
|
+
"expected '1', " \
|
44
|
+
"but got '#{params['udap']}'")
|
45
|
+
end
|
46
|
+
|
47
|
+
check_authorization_code_request_params(params, request_num) if oauth_flow == AUTHORIZATION_CODE_TAG
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_authorization_code_request_params(params, request_num)
|
53
|
+
if params['code'].present?
|
54
|
+
|
55
|
+
authorization_request = MockUDAPServer.authorization_request_for_code(params['code'], test_session_id)
|
56
|
+
|
57
|
+
if authorization_request.present?
|
58
|
+
authorization_body = MockUDAPServer.authorization_code_request_details(authorization_request)
|
59
|
+
|
60
|
+
if params['redirect_uri'] != authorization_body['redirect_uri']
|
61
|
+
add_message('error', "Authorization code token request #{request_num} included an incorrect " \
|
62
|
+
"`redirect_uri` value: expected '#{authorization_body['redirect_uri']} " \
|
63
|
+
"but got '#{params['redirect_uri']}'")
|
64
|
+
end
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
return unless params['code_verifier'].present? # optional in UDAP
|
67
|
+
|
68
|
+
pkce_error = MockUDAPServer.pkce_error(params['code_verifier'],
|
69
|
+
authorization_body['code_challenge'],
|
70
|
+
authorization_body['code_challenge_method'])
|
71
|
+
if pkce_error.present?
|
72
|
+
add_message('error', 'Error performing pkce verification on the `code_verifier` value in ' \
|
73
|
+
"authorization code token request #{request_num}: #{pkce_error}")
|
74
|
+
end
|
75
|
+
else
|
76
|
+
add_message('error', "Authorization code token request #{request_num} included a code not " \
|
77
|
+
"issued during this test session: '#{params['code']}'")
|
78
|
+
end
|
79
|
+
else
|
80
|
+
add_message('error', "Authorization code token request #{request_num} missing a `code`")
|
81
|
+
end
|
71
82
|
end
|
72
83
|
|
73
|
-
def check_client_assertion(assertion, request_num, jti_list, registration_token, registered_client_id,
|
84
|
+
def check_client_assertion(oauth_flow, assertion, request_num, jti_list, registration_token, registered_client_id,
|
85
|
+
request_time)
|
74
86
|
decoded_token =
|
75
87
|
begin
|
76
88
|
JWT::EncodedToken.new(assertion)
|
@@ -82,11 +94,11 @@ module UDAPSecurityTestKit
|
|
82
94
|
return unless decoded_token.present?
|
83
95
|
|
84
96
|
# header checked with signature
|
85
|
-
check_jwt_payload(decoded_token.payload, request_num, jti_list, registered_client_id, request_time)
|
97
|
+
check_jwt_payload(oauth_flow, decoded_token.payload, request_num, jti_list, registered_client_id, request_time)
|
86
98
|
check_jwt_signature(decoded_token, registration_token, request_num)
|
87
99
|
end
|
88
100
|
|
89
|
-
def check_jwt_payload(claims, request_num, jti_list, registered_client_id, request_time) # rubocop:disable Metrics/CyclomaticComplexity
|
101
|
+
def check_jwt_payload(oauth_flow, claims, request_num, jti_list, registered_client_id, request_time) # rubocop:disable Metrics/CyclomaticComplexity
|
90
102
|
if claims['iss'] != registered_client_id
|
91
103
|
add_message('error', "client assertion jwt on token request #{request_num} has an incorrect `iss` claim: " \
|
92
104
|
"expected '#{registered_client_id}', got '#{claims['iss']}'")
|
@@ -113,6 +125,8 @@ module UDAPSecurityTestKit
|
|
113
125
|
jti_list << claims['jti']
|
114
126
|
end
|
115
127
|
|
128
|
+
return unless oauth_flow == CLIENT_CREDENTIALS_TAG
|
129
|
+
|
116
130
|
if claims['extensions'].present?
|
117
131
|
if claims['extensions'].is_a?(Hash)
|
118
132
|
check_b2b_auth_extension(claims.dig('extensions', 'hl7-b2b'), request_num)
|
@@ -174,5 +188,36 @@ module UDAPSecurityTestKit
|
|
174
188
|
rescue StandardError
|
175
189
|
nil
|
176
190
|
end
|
191
|
+
|
192
|
+
def check_refresh_request_params(oauth_flow, params, request_num)
|
193
|
+
if oauth_flow == CLIENT_CREDENTIALS_TAG
|
194
|
+
add_message('error',
|
195
|
+
"Invalid refresh request #{request_num} found during client_credentials flow.")
|
196
|
+
return
|
197
|
+
end
|
198
|
+
|
199
|
+
if params['grant_type'] != 'refresh_token'
|
200
|
+
add_message('error',
|
201
|
+
"Refresh request #{request_num} had an incorrect `grant_type`: expected 'refresh_token', " \
|
202
|
+
"but got '#{params['grant_type']}'")
|
203
|
+
end
|
204
|
+
if params['client_assertion_type'] != 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
205
|
+
add_message('error',
|
206
|
+
"Token refresh request #{request_num} had an incorrect " \
|
207
|
+
"`client_assertion_type`: expected 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', " \
|
208
|
+
"but got '#{params['client_assertion_type']}'")
|
209
|
+
end
|
210
|
+
|
211
|
+
authorization_code = MockUDAPServer.refresh_token_to_authorization_code(params['refresh_token'])
|
212
|
+
authorization_request = MockUDAPServer.authorization_request_for_code(authorization_code, test_session_id)
|
213
|
+
if authorization_request.present?
|
214
|
+
# TODO: - check that the scope is a subset of the original authorization code request
|
215
|
+
else
|
216
|
+
add_message('error', "Authorization code token refresh request #{request_num} included a refresh token not " \
|
217
|
+
"issued during this test session: '#{params['refresh_token']}'")
|
218
|
+
end
|
219
|
+
|
220
|
+
nil
|
221
|
+
end
|
177
222
|
end
|
178
223
|
end
|
@@ -10,7 +10,6 @@ module UDAPSecurityTestKit
|
|
10
10
|
authentication.
|
11
11
|
)
|
12
12
|
|
13
|
-
input :udap_demonstrated # from test :udap_client_token_request_verification based on registrations
|
14
13
|
input :udap_tokens,
|
15
14
|
optional: true
|
16
15
|
|
@@ -21,8 +20,6 @@ module UDAPSecurityTestKit
|
|
21
20
|
end
|
22
21
|
|
23
22
|
run do
|
24
|
-
omit_if udap_demonstrated == 'No', 'UDAP Authentication not demonstrated as a part of this test session.'
|
25
|
-
|
26
23
|
access_requests = access_request_tags.map do |access_request_tag|
|
27
24
|
load_tagged_requests(access_request_tag).reject { |access| access.status == 401 }
|
28
25
|
end.flatten
|
@@ -1,9 +1,12 @@
|
|
1
|
-
require_relative 'endpoints/mock_udap_server/
|
2
|
-
require_relative 'endpoints/mock_udap_server/
|
3
|
-
require_relative 'endpoints/
|
1
|
+
require_relative 'endpoints/mock_udap_server/registration_endpoint'
|
2
|
+
require_relative 'endpoints/mock_udap_server/authorization_endpoint'
|
3
|
+
require_relative 'endpoints/mock_udap_server/token_endpoint'
|
4
|
+
require_relative 'endpoints/echoing_fhir_responder_endpoint'
|
4
5
|
require_relative 'urls'
|
5
|
-
require_relative 'client_suite/
|
6
|
-
require_relative 'client_suite/
|
6
|
+
require_relative 'client_suite/registration_ac_group'
|
7
|
+
require_relative 'client_suite/registration_cc_group'
|
8
|
+
require_relative 'client_suite/access_ac_group'
|
9
|
+
require_relative 'client_suite/access_cc_group'
|
7
10
|
|
8
11
|
module UDAPSecurityTestKit
|
9
12
|
class UDAPSecurityClientTestSuite < Inferno::TestSuite
|
@@ -34,8 +37,30 @@ module UDAPSecurityTestKit
|
|
34
37
|
}
|
35
38
|
]
|
36
39
|
|
40
|
+
suite_option :client_type,
|
41
|
+
title: 'UDAP Client Type',
|
42
|
+
list_options: [
|
43
|
+
{
|
44
|
+
label: 'UDAP Authorization Code Client',
|
45
|
+
value: UDAPClientOptions::UDAP_AUTHORIZATION_CODE
|
46
|
+
},
|
47
|
+
{
|
48
|
+
label: 'UDAP Client Credentials Client',
|
49
|
+
value: UDAPClientOptions::UDAP_CLIENT_CREDENTIALS
|
50
|
+
}
|
51
|
+
]
|
52
|
+
|
37
53
|
route(:get, UDAP_DISCOVERY_PATH, ->(_env) { MockUDAPServer.udap_server_metadata(id) })
|
54
|
+
route(:get, OIDC_DISCOVERY_PATH, ->(_env) { MockUDAPServer.openid_connect_metadata(id) })
|
55
|
+
route(
|
56
|
+
:get,
|
57
|
+
OIDC_JWKS_PATH,
|
58
|
+
->(_env) { [200, { 'Content-Type' => 'application/json' }, [OIDCJWKS.jwks_json]] }
|
59
|
+
)
|
60
|
+
|
38
61
|
suite_endpoint :post, REGISTRATION_PATH, MockUDAPServer::RegistrationEndpoint
|
62
|
+
suite_endpoint :get, AUTHORIZATION_PATH, MockUDAPServer::AuthorizationEndpoint
|
63
|
+
suite_endpoint :post, AUTHORIZATION_PATH, MockUDAPServer::AuthorizationEndpoint
|
39
64
|
suite_endpoint :post, TOKEN_PATH, MockUDAPServer::TokenEndpoint
|
40
65
|
suite_endpoint :get, FHIR_PATH, EchoingFHIRResponderEndpoint
|
41
66
|
suite_endpoint :post, FHIR_PATH, EchoingFHIRResponderEndpoint
|
@@ -62,17 +87,21 @@ module UDAPSecurityTestKit
|
|
62
87
|
request.query_parameters['token']
|
63
88
|
end
|
64
89
|
|
65
|
-
group
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
90
|
+
group from: :udap_client_registration_ac,
|
91
|
+
required_suite_options: {
|
92
|
+
client_type: UDAPClientOptions::UDAP_AUTHORIZATION_CODE
|
93
|
+
}
|
94
|
+
group from: :udap_client_registration_cc,
|
95
|
+
required_suite_options: {
|
96
|
+
client_type: UDAPClientOptions::UDAP_CLIENT_CREDENTIALS
|
97
|
+
}
|
98
|
+
group from: :udap_client_access_ac,
|
99
|
+
required_suite_options: {
|
100
|
+
client_type: UDAPClientOptions::UDAP_AUTHORIZATION_CODE
|
101
|
+
}
|
102
|
+
group from: :udap_client_access_cc,
|
103
|
+
required_suite_options: {
|
104
|
+
client_type: UDAPClientOptions::UDAP_CLIENT_CREDENTIALS
|
105
|
+
}
|
77
106
|
end
|
78
107
|
end
|