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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/UDAP_RunServerAgainstClient.json.erb +4 -4
  3. data/lib/udap_security_test_kit/client_suite/access_ac_group.rb +25 -0
  4. data/lib/udap_security_test_kit/client_suite/access_ac_interaction_test.rb +59 -0
  5. data/lib/udap_security_test_kit/client_suite/access_cc_group.rb +23 -0
  6. data/lib/udap_security_test_kit/client_suite/access_cc_interaction_test.rb +49 -0
  7. data/lib/udap_security_test_kit/client_suite/authorization_request_verification_test.rb +83 -0
  8. data/lib/udap_security_test_kit/client_suite/client_descriptions.rb +70 -0
  9. data/lib/udap_security_test_kit/client_suite/client_options.rb +20 -0
  10. data/lib/udap_security_test_kit/client_suite/oidc_jwks.json +32 -0
  11. data/lib/udap_security_test_kit/client_suite/oidc_jwks.rb +27 -0
  12. data/lib/udap_security_test_kit/client_suite/registration_ac_group.rb +18 -0
  13. data/lib/udap_security_test_kit/client_suite/registration_ac_verification_test.rb +38 -0
  14. data/lib/udap_security_test_kit/client_suite/registration_cc_group.rb +18 -0
  15. data/lib/udap_security_test_kit/client_suite/registration_cc_verification_test.rb +38 -0
  16. data/lib/udap_security_test_kit/client_suite/{client_registration_interaction_test.rb → registration_interaction_test.rb} +11 -4
  17. data/lib/udap_security_test_kit/client_suite/{client_registration_verification_test.rb → registration_request_verification.rb} +38 -40
  18. data/lib/udap_security_test_kit/client_suite/token_request_ac_verification_test.rb +49 -0
  19. data/lib/udap_security_test_kit/client_suite/token_request_cc_verification_test.rb +49 -0
  20. data/lib/udap_security_test_kit/client_suite/{client_token_request_verification_test.rb → token_request_verification.rb} +91 -46
  21. data/lib/udap_security_test_kit/client_suite/{client_token_use_verification_test.rb → token_use_verification_test.rb} +0 -3
  22. data/lib/udap_security_test_kit/client_suite.rb +46 -17
  23. data/lib/udap_security_test_kit/docs/udap_client_suite_description.md +74 -31
  24. data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder_endpoint.rb +96 -0
  25. data/lib/udap_security_test_kit/endpoints/mock_udap_server/authorization_endpoint.rb +28 -0
  26. data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration_endpoint.rb +31 -0
  27. data/lib/udap_security_test_kit/endpoints/mock_udap_server/token_endpoint.rb +56 -0
  28. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_authorization_response_creation.rb +63 -0
  29. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_registration_response_creation.rb +28 -0
  30. data/lib/udap_security_test_kit/endpoints/mock_udap_server/udap_token_response_creation.rb +218 -0
  31. data/lib/udap_security_test_kit/endpoints/mock_udap_server.rb +112 -31
  32. data/lib/udap_security_test_kit/metadata.rb +1 -1
  33. data/lib/udap_security_test_kit/tags.rb +4 -0
  34. data/lib/udap_security_test_kit/urls.rb +15 -8
  35. data/lib/udap_security_test_kit/version.rb +2 -2
  36. metadata +28 -12
  37. data/lib/udap_security_test_kit/client_suite/client_access_group.rb +0 -22
  38. data/lib/udap_security_test_kit/client_suite/client_access_interaction_test.rb +0 -53
  39. data/lib/udap_security_test_kit/client_suite/client_registration_group.rb +0 -26
  40. data/lib/udap_security_test_kit/endpoints/echoing_fhir_responder.rb +0 -52
  41. data/lib/udap_security_test_kit/endpoints/mock_udap_server/registration.rb +0 -57
  42. 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
- class UDAPClientRegistrationVerification < Inferno::Test
7
- include URLs
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
- skip_if requests.empty?, 'No UDAP Registration Requests made.'
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
- verified_request = requests.last
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.decond(certification_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'].each do |redirect_uri|
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
- 'it is not a valid https uri.')
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: it is not a valid https uri.')
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['.'].last.downcase)
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
- class UDAPClientTokenRequestVerification < Inferno::Test
7
- include URLs
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(registration_assertion)
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
- check_request_params(request_params, index + 1)
41
- check_client_assertion(request_params['client_assertion'], index + 1, jti_list, registration_token,
42
- registered_client_id, token_request.created_at)
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'] != 'client_credentials'
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 'client_credentials', " \
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
- return unless params['udap'].to_s != '1'
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
- add_message('error',
68
- "Token request #{request_num} had an incorrect `udap`: " \
69
- "expected '1', " \
70
- "but got '#{params['udap']}'")
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, request_time)
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/registration'
2
- require_relative 'endpoints/mock_udap_server/token'
3
- require_relative 'endpoints/echoing_fhir_responder'
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/client_registration_group'
6
- require_relative 'client_suite/client_access_group'
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 do
66
- title 'UDAP Client Credentials Flow'
67
- description %(
68
- During these tests, the client will use the UDAP Client Credentials
69
- flow as specified in the [B2B section of the IG](https://hl7.org/fhir/us/udap-security/STU1/b2b.html)
70
- to access a FHIR API. Clients will register, obtain an access token,
71
- and use the access token when making a request to a FHIR API.
72
- )
73
-
74
- group from: :udap_client_registration
75
- group from: :udap_client_access
76
- end
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