onc_certification_g10_test_kit 2.3.0 → 3.0.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/inferno/terminology/expected_manifest.yml +242 -29
  3. data/lib/inferno/terminology/fhir_package_manager.rb +27 -0
  4. data/lib/inferno/terminology/loader.rb +22 -1
  5. data/lib/inferno/terminology/tasks/create_value_set_validators.rb +1 -1
  6. data/lib/inferno/terminology/tasks/download_fhir_terminology.rb +5 -0
  7. data/lib/inferno/terminology/value_set.rb +51 -5
  8. data/lib/onc_certification_g10_test_kit/base_token_refresh_group.rb +5 -4
  9. data/lib/onc_certification_g10_test_kit/bulk_data_group_export_stu1.rb +5 -0
  10. data/lib/onc_certification_g10_test_kit/bulk_data_group_export_stu2.rb +2 -0
  11. data/lib/onc_certification_g10_test_kit/bulk_data_group_export_validation.rb +206 -28
  12. data/lib/onc_certification_g10_test_kit/bulk_export_validation_tester.rb +25 -40
  13. data/lib/onc_certification_g10_test_kit/encounter_context_test.rb +30 -0
  14. data/lib/onc_certification_g10_test_kit/feature.rb +5 -8
  15. data/lib/onc_certification_g10_test_kit/limited_scope_grant_test.rb +18 -5
  16. data/lib/onc_certification_g10_test_kit/profile_selector.rb +175 -0
  17. data/lib/onc_certification_g10_test_kit/restricted_resource_type_access_group.rb +54 -4
  18. data/lib/onc_certification_g10_test_kit/single_patient_us_core_5_api_group.rb +93 -0
  19. data/lib/onc_certification_g10_test_kit/smart_app_launch_invalid_aud_group.rb +50 -5
  20. data/lib/onc_certification_g10_test_kit/smart_ehr_patient_launch_group.rb +94 -0
  21. data/lib/onc_certification_g10_test_kit/smart_ehr_patient_launch_group_stu2.rb +94 -0
  22. data/lib/onc_certification_g10_test_kit/smart_ehr_practitioner_app_group.rb +197 -13
  23. data/lib/onc_certification_g10_test_kit/smart_invalid_pkce_group.rb +310 -0
  24. data/lib/onc_certification_g10_test_kit/smart_invalid_token_group_stu2.rb +211 -0
  25. data/lib/onc_certification_g10_test_kit/smart_limited_app_group.rb +135 -9
  26. data/lib/onc_certification_g10_test_kit/smart_public_standalone_launch_group.rb +16 -4
  27. data/lib/onc_certification_g10_test_kit/smart_public_standalone_launch_group_stu2.rb +130 -0
  28. data/lib/onc_certification_g10_test_kit/smart_scopes_test.rb +134 -67
  29. data/lib/onc_certification_g10_test_kit/smart_standalone_patient_app_group.rb +166 -11
  30. data/lib/onc_certification_g10_test_kit/unrestricted_resource_type_access_group.rb +119 -135
  31. data/lib/onc_certification_g10_test_kit/version.rb +1 -1
  32. data/lib/onc_certification_g10_test_kit/visual_inspection_and_attestations_group.rb +19 -0
  33. data/lib/onc_certification_g10_test_kit/well_known_capabilities_test.rb +7 -1
  34. data/lib/onc_certification_g10_test_kit.rb +115 -74
  35. metadata +19 -11
  36. data/lib/onc_certification_g10_test_kit/profile_guesser.rb +0 -72
@@ -2,6 +2,7 @@ require_relative 'base_token_refresh_group'
2
2
  require_relative 'smart_scopes_test'
3
3
  require_relative 'unauthorized_access_test'
4
4
  require_relative 'well_known_capabilities_test'
5
+ require_relative 'encounter_context_test'
5
6
 
6
7
  module ONCCertificationG10TestKit
7
8
  class SmartEHRPractitionerAppGroup < Inferno::TestGroup
@@ -14,29 +15,35 @@ module ONCCertificationG10TestKit
14
15
  * Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
15
16
 
16
17
  Enter in the appropriate scope to enable user-level access to all relevant
17
- resources. In addition, support for the OpenID Connect (openid fhirUser),
18
- refresh tokens (offline_access), and EHR context (launch) are required. This
19
- test expects that the EHR will launch the application with a patient context.
18
+ resources. If using SMART v2, v2-style scopes must be used. In addition,
19
+ support for the OpenID Connect (openid fhirUser), refresh tokens
20
+ (offline_access), and EHR context (launch) are required. This test expects
21
+ that the EHR will launch the application with a patient context.
20
22
 
21
23
  After submit is pressed, Inferno will wait for the system under test to launch
22
24
  the application.
23
25
  )
24
26
 
25
27
  description %(
26
- Demonstrate the ability to perform an EHR launch to a [SMART on
27
- FHIR](https://hl7.org/fhir/smart-app-launch/1.0.0/) confidential client with
28
- patient context, refresh token, and [OpenID Connect
29
- (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html) identity
30
- token. After launch, a simple Patient resource read is performed on the
31
- patient in context. The access token is then refreshed, and the Patient
32
- resource is read using the new access token to ensure that the refresh was
33
- successful. Finally, the authentication information provided by OpenID
34
- Connect is decoded and validated.
28
+ Demonstrate the ability to perform an EHR launch to a SMART on FHIR
29
+ confidential client with patient context, refresh token, OpenID Connect
30
+ (OIDC) identity token, and (SMART v2 only) use the POST HTTP method for
31
+ code exchange. After launch, a simple Patient resource read is performed
32
+ on the patient in context. The access token is then refreshed, and the
33
+ Patient resource is read using the new access token to ensure that the
34
+ refresh was successful. Finally, the authentication information provided
35
+ by OpenID Connect is decoded and validated.
35
36
 
36
37
  For EHRs that use Internet Explorer 11 to display embedded apps,
37
38
  please review [instructions on how to complete the EHR Practitioner App
38
39
  test](https://github.com/onc-healthit/onc-certification-g10-test-kit/wiki/Completing-EHR-Practitioner-App-test-in-Internet-Explorer/).
39
40
 
41
+ * [SMART on FHIR
42
+ (STU1)](http://www.hl7.org/fhir/smart-app-launch/1.0.0/)
43
+ * [SMART on FHIR
44
+ (STU2)](http://hl7.org/fhir/smart-app-launch/STU2)
45
+ * [OpenID Connect
46
+ (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html)
40
47
  )
41
48
  id :g10_smart_ehr_practitioner_app
42
49
  run_as_group
@@ -52,6 +59,8 @@ module ONCCertificationG10TestKit
52
59
  input_order :url, :ehr_client_id, :ehr_client_secret
53
60
 
54
61
  group from: :smart_discovery do
62
+ required_suite_options(smart_app_launch_version: 'smart_app_launch_1')
63
+
55
64
  test from: 'g10_smart_well_known_capabilities',
56
65
  config: {
57
66
  options: {
@@ -69,7 +78,32 @@ module ONCCertificationG10TestKit
69
78
  }
70
79
  end
71
80
 
81
+ group from: :smart_discovery_stu2 do
82
+ required_suite_options(smart_app_launch_version: 'smart_app_launch_2')
83
+
84
+ test from: 'g10_smart_well_known_capabilities',
85
+ config: {
86
+ options: {
87
+ required_capabilities: [
88
+ 'launch-ehr',
89
+ 'client-confidential-symmetric',
90
+ 'sso-openid-connect',
91
+ 'context-banner',
92
+ 'context-style',
93
+ 'context-ehr-patient',
94
+ 'permission-offline',
95
+ 'permission-user',
96
+ 'authorize-post',
97
+ 'permission-v1',
98
+ 'permission-v2'
99
+ ]
100
+ }
101
+ }
102
+ end
103
+
72
104
  group from: :smart_ehr_launch do
105
+ required_suite_options(smart_app_launch_version: 'smart_app_launch_1')
106
+
73
107
  title 'EHR Launch With Practitioner Scope'
74
108
  input :client_secret,
75
109
  name: :ehr_client_secret,
@@ -101,6 +135,9 @@ module ONCCertificationG10TestKit
101
135
  inputs: {
102
136
  requested_scopes: { name: :ehr_requested_scopes },
103
137
  received_scopes: { name: :ehr_received_scopes }
138
+ },
139
+ options: {
140
+ scope_version: :v1
104
141
  }
105
142
  )
106
143
 
@@ -108,7 +145,7 @@ module ONCCertificationG10TestKit
108
145
  ['openid', 'fhirUser', 'launch', 'offline_access']
109
146
  end
110
147
 
111
- def scope_type
148
+ def required_scope_type
112
149
  'user'
113
150
  end
114
151
  end
@@ -128,6 +165,151 @@ module ONCCertificationG10TestKit
128
165
  }
129
166
  }
130
167
 
168
+ test from: :g10_encounter_context,
169
+ config: {
170
+ inputs: {
171
+ encounter_id: { name: :ehr_encounter_id },
172
+ access_token: { name: :ehr_access_token }
173
+ }
174
+ },
175
+ required_suite_options: { us_core_version: 'us_core_5' }
176
+
177
+ test do
178
+ title 'Launch context contains smart_style_url which links to valid JSON'
179
+ description %(
180
+ In order to mimic the style of the SMART host more closely, SMART apps
181
+ can check for the existence of this launch context parameter and
182
+ download the JSON file referenced by the URL value.
183
+ )
184
+ id :Test13
185
+ uses_request :token
186
+
187
+ run do
188
+ skip_if request.status != 200, 'No token response received'
189
+ assert_valid_json response[:body]
190
+
191
+ body = JSON.parse(response[:body])
192
+
193
+ assert body['smart_style_url'].present?,
194
+ 'Token response did not contain `smart_style_url`'
195
+
196
+ get(body['smart_style_url'])
197
+
198
+ assert_response_status(200)
199
+ assert_valid_json(response[:body])
200
+ end
201
+ end
202
+
203
+ test do
204
+ title 'Launch context contains need_patient_banner'
205
+ description %(
206
+ `need_patient_banner` is a boolean value indicating whether the app
207
+ was launched in a UX context where a patient banner is required (when
208
+ true) or not required (when false).
209
+ )
210
+ id :Test14
211
+ uses_request :token
212
+
213
+ run do
214
+ skip_if request.status != 200, 'No token response received'
215
+ assert_valid_json response[:body]
216
+
217
+ body = JSON.parse(response[:body])
218
+
219
+ assert body.key?('need_patient_banner'),
220
+ 'Token response did not contain `need_patient_banner`'
221
+ end
222
+ end
223
+ end
224
+
225
+ group from: :smart_ehr_launch_stu2,
226
+ config: {
227
+ inputs: {
228
+ use_pkce: {
229
+ default: 'true',
230
+ locked: true
231
+ },
232
+ pkce_code_challenge_method: {
233
+ locked: true
234
+ },
235
+ authorization_method: {
236
+ name: :ehr_authorization_method,
237
+ default: 'post',
238
+ locked: true
239
+ }
240
+ }
241
+ } do
242
+ required_suite_options(smart_app_launch_version: 'smart_app_launch_2')
243
+
244
+ title 'EHR Launch With Practitioner Scope'
245
+ input :client_secret,
246
+ name: :ehr_client_secret,
247
+ title: 'EHR Launch Client Secret',
248
+ description: 'Client Secret provided during registration of Inferno as an EHR launch application',
249
+ optional: false
250
+
251
+ config(
252
+ inputs: {
253
+ requested_scopes: {
254
+ default: %(
255
+ launch openid fhirUser offline_access user/Medication.rs
256
+ user/AllergyIntolerance.rs user/CarePlan.rs user/CareTeam.rs
257
+ user/Condition.rs user/Device.rs user/DiagnosticReport.rs
258
+ user/DocumentReference.rs user/Encounter.rs user/Goal.rs
259
+ user/Immunization.rs user/Location.rs user/MedicationRequest.rs
260
+ user/Observation.rs user/Organization.rs user/Patient.rs
261
+ user/Practitioner.rs user/Procedure.rs user/Provenance.rs
262
+ user/PractitionerRole.rs
263
+ ).gsub(/\s{2,}/, ' ').strip
264
+ }
265
+ }
266
+ )
267
+
268
+ test from: :g10_smart_scopes do
269
+ title 'User-level access with OpenID Connect and Refresh Token scopes used.'
270
+ config(
271
+ inputs: {
272
+ requested_scopes: { name: :ehr_requested_scopes },
273
+ received_scopes: { name: :ehr_received_scopes }
274
+ },
275
+ options: {
276
+ scope_version: :v2
277
+ }
278
+ )
279
+
280
+ def required_scopes
281
+ ['openid', 'fhirUser', 'launch', 'offline_access']
282
+ end
283
+
284
+ def required_scope_type
285
+ 'user'
286
+ end
287
+ end
288
+
289
+ test from: :g10_unauthorized_access,
290
+ config: {
291
+ inputs: {
292
+ patient_id: { name: :ehr_patient_id }
293
+ }
294
+ }
295
+
296
+ test from: :g10_patient_context,
297
+ config: {
298
+ inputs: {
299
+ patient_id: { name: :ehr_patient_id },
300
+ access_token: { name: :ehr_access_token }
301
+ }
302
+ }
303
+
304
+ test from: :g10_encounter_context,
305
+ config: {
306
+ inputs: {
307
+ encounter_id: { name: :ehr_encounter_id },
308
+ access_token: { name: :ehr_access_token }
309
+ }
310
+ },
311
+ required_suite_options: { us_core_version: 'us_core_5' }
312
+
131
313
  test do
132
314
  title 'Launch context contains smart_style_url which links to valid JSON'
133
315
  description %(
@@ -136,6 +318,7 @@ module ONCCertificationG10TestKit
136
318
  download the JSON file referenced by the URL value.
137
319
  )
138
320
  uses_request :token
321
+ id :g10_smart_style_url
139
322
 
140
323
  run do
141
324
  skip_if request.status != 200, 'No token response received'
@@ -161,6 +344,7 @@ module ONCCertificationG10TestKit
161
344
  true) or not required (when false).
162
345
  )
163
346
  uses_request :token
347
+ id :g10_smart_need_patient_banner
164
348
 
165
349
  run do
166
350
  skip_if request.status != 200, 'No token response received'
@@ -0,0 +1,310 @@
1
+ module ONCCertificationG10TestKit
2
+ class InvalidSMARTTokenRequestTest < Inferno::Test
3
+ title 'OAuth token exchange fails when supplied invalid code_verifier'
4
+ description %(
5
+ If the request failed verification or is invalid, the authorization
6
+ server returns an error response.
7
+ )
8
+ uses_request :redirect
9
+ id :invalid_pkce_request
10
+
11
+ input :code, :use_pkce, :pkce_code_verifier, :client_id, :client_secret, :smart_token_url
12
+
13
+ def modify_oauth_params(oauth_params)
14
+ oauth_params
15
+ end
16
+
17
+ run do
18
+ skip_if request.query_parameters['error'].present?, 'Error during authorization request'
19
+
20
+ oauth2_params = {
21
+ grant_type: 'authorization_code',
22
+ code: code,
23
+ redirect_uri: config.options[:redirect_uri]
24
+ }
25
+
26
+ oauth2_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
27
+
28
+ if client_secret.present?
29
+ client_credentials = "#{client_id}:#{client_secret}"
30
+ oauth2_headers['Authorization'] = "Basic #{Base64.strict_encode64(client_credentials)}"
31
+ else
32
+ oauth2_params[:client_id] = client_id
33
+ end
34
+
35
+ modify_oauth_params(oauth2_params)
36
+
37
+ post(smart_token_url, body: oauth2_params, name: :token, headers: oauth2_headers)
38
+
39
+ assert_response_status([400, 401])
40
+ end
41
+ end
42
+
43
+ class SMARTInvalidPKCEGroup < Inferno::TestGroup
44
+ title 'SMART App Launch Error: Invalid PKCE Code Verifier'
45
+ short_title 'SMART Invalid PKCE Code Verifier'
46
+ input_instructions %(
47
+ Register Inferno as a standalone application using the following information:
48
+
49
+ * Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
50
+ )
51
+ description %(
52
+ # Background
53
+
54
+ The #{title} Group verifies that a SMART Launch Sequence, specifically the
55
+ [Standalone
56
+ Launch](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
57
+ Sequence, verifies that servers properly support PKCE. It does this by ensuring the launch fails
58
+ in the case where the client sends an invalid PKCE `code_verifier`.
59
+
60
+ This group performs four launches with various forms of an invalid `code_verifier`
61
+ (e.g. incorrect `code_verifier`, blank `code_identifier`) and verifies that these do
62
+ not result in a successful launch. Testers can expect to be prompted four times
63
+ that a redirect will occur in this test.
64
+
65
+ This test is not included as part of the Single Patient App group
66
+ because there is no way for a client to infer that PKCE is supported on the server
67
+ properly without performing extra launches. Attempting to verify this within the
68
+ same launch cannot be done because some servers may not accept an authorization code
69
+ after it has been used unsuccessfully in this manner.
70
+ )
71
+ id :g10_smart_invalid_pkce_code_verifier_group
72
+ run_as_group
73
+
74
+ input :use_pkce,
75
+ title: 'Proof Key for Code Exchange (PKCE)',
76
+ type: 'radio',
77
+ default: 'true',
78
+ locked: true,
79
+ options: {
80
+ list_options: [
81
+ {
82
+ label: 'Enabled',
83
+ value: 'true'
84
+ },
85
+ {
86
+ label: 'Disabled',
87
+ value: 'false'
88
+ }
89
+ ]
90
+ }
91
+ input :pkce_code_challenge_method,
92
+ optional: true,
93
+ title: 'PKCE Code Challenge Method',
94
+ type: 'radio',
95
+ default: 'S256',
96
+ locked: true,
97
+ options: {
98
+ list_options: [
99
+ {
100
+ label: 'S256',
101
+ value: 'S256'
102
+ },
103
+ {
104
+ label: 'Plain',
105
+ value: 'plain'
106
+ }
107
+ ]
108
+ }
109
+
110
+ input_order :url,
111
+ :standalone_client_id,
112
+ :standalone_client_secret,
113
+ :standalone_requested_scopes,
114
+ :use_pkce,
115
+ :pkce_code_challenge_method,
116
+ :smart_authorization_url,
117
+ :smart_token_url
118
+
119
+ config(
120
+ inputs: {
121
+ client_id: {
122
+ name: :standalone_client_id,
123
+ title: 'Standalone Client ID',
124
+ description: 'Client ID provided during registration of Inferno as a standalone application'
125
+ },
126
+ client_secret: {
127
+ name: :standalone_client_secret,
128
+ title: 'Standalone Client Secret',
129
+ description: 'Client Secret provided during registration of Inferno as a standalone application'
130
+ },
131
+ requested_scopes: {
132
+ name: :standalone_requested_scopes,
133
+ title: 'Standalone Scope',
134
+ description: 'OAuth 2.0 scope provided by system to enable all required functionality',
135
+ type: 'textarea',
136
+ default: %(
137
+ launch/patient openid fhirUser offline_access
138
+ patient/Medication.read patient/AllergyIntolerance.read
139
+ patient/CarePlan.read patient/CareTeam.read patient/Condition.read
140
+ patient/Device.read patient/DiagnosticReport.read
141
+ patient/DocumentReference.read patient/Encounter.read
142
+ patient/Goal.read patient/Immunization.read patient/Location.read
143
+ patient/MedicationRequest.read patient/Observation.read
144
+ patient/Organization.read patient/Patient.read
145
+ patient/Practitioner.read patient/Procedure.read
146
+ patient/Provenance.read patient/PractitionerRole.read
147
+ ).gsub(/\s{2,}/, ' ').strip
148
+ },
149
+ url: {
150
+ title: 'Standalone FHIR Endpoint',
151
+ description: 'URL of the FHIR endpoint used by standalone applications'
152
+ },
153
+ code: {
154
+ name: :invalid_token_code
155
+ },
156
+ state: {
157
+ name: :invalid_token_state
158
+ },
159
+ smart_authorization_url: {
160
+ title: 'OAuth 2.0 Authorize Endpoint',
161
+ description: 'OAuth 2.0 Authorize Endpoint provided during the patient standalone launch'
162
+ },
163
+ smart_token_url: {
164
+ title: 'OAuth 2.0 Token Endpoint',
165
+ description: 'OAuth 2.0 Token Endpoint provided during the patient standalone launch'
166
+ },
167
+ pkce_code_challenge: {
168
+ name: :invalid_token_pkce_code_challenge
169
+ },
170
+ pkce_code_verifier: {
171
+ name: :invalid_token_pkce_code_verifier
172
+ }
173
+ },
174
+ outputs: {
175
+ code: { name: :invalid_token_code },
176
+ state: { name: :invalid_token_state },
177
+ expires_in: { name: :invalid_token_expires_in },
178
+ pkce_code_challenge: { name: :invalid_token_pkce_code_challenge },
179
+ pkce_code_verifier: { name: :invalid_token_pkce_code_verifier }
180
+ },
181
+ requests: {
182
+ redirect: { name: :invalid_token_redirect },
183
+ token: { name: :invalid_token_token }
184
+ }
185
+ )
186
+
187
+ test from: :smart_app_redirect_stu2,
188
+ id: :smart_no_code_verifier_redirect,
189
+ config: {
190
+ options: {
191
+ redirect_message_proc: lambda do |auth_url|
192
+ %(
193
+ ### Invalid PKCE code_verifier 1/4
194
+
195
+ This launch does not provide a `code_verifier` and verifies the
196
+ server does not issue an access token.
197
+
198
+ [Follow this link to authorize with the SMART
199
+ server](#{auth_url}).
200
+
201
+ Tests will resume once Inferno receives a request at
202
+ `#{config.options[:redirect_uri]}` with a state of `#{state}`.
203
+ )
204
+ end
205
+ }
206
+ }
207
+ test from: :smart_code_received,
208
+ id: :smart_no_code_verifier_code_received
209
+ test from: :invalid_pkce_request do
210
+ title 'OAuth token exchange fails when no code_verifier is given'
211
+ id :smart_no_verifier_token_request
212
+ end
213
+
214
+ test from: :smart_app_redirect_stu2,
215
+ id: :smart_blank_code_verifier_redirect,
216
+ config: {
217
+ options: {
218
+ redirect_message_proc: lambda do |auth_url|
219
+ %(
220
+ ### Invalid PKCE code_verifier 2/4
221
+
222
+ This launch provides a blank `code_verifier` and verifies the
223
+ server does not issue an access token.
224
+
225
+ [Follow this link to authorize with the SMART
226
+ server](#{auth_url}).
227
+
228
+ Tests will resume once Inferno receives a request at
229
+ `#{config.options[:redirect_uri]}` with a state of `#{state}`.
230
+ )
231
+ end
232
+ }
233
+ }
234
+ test from: :smart_code_received,
235
+ id: :smart_blank_code_verifier_code_received
236
+ test from: :invalid_pkce_request do
237
+ title 'OAuth token exchange fails when code_verifier is blank'
238
+ id :smart_blank_verifier_token_request
239
+
240
+ def modify_oauth_params(oauth_params)
241
+ oauth_params.merge!(code_verifier: '')
242
+ end
243
+ end
244
+
245
+ test from: :smart_app_redirect_stu2,
246
+ id: :smart_bad_code_verifier_redirect,
247
+ config: {
248
+ options: {
249
+ redirect_message_proc: lambda do |auth_url|
250
+ %(
251
+ ### Invalid PKCE code_verifier 3/4
252
+
253
+ This launch provides an invalid `code_verifier` and verifies the
254
+ server does not issue an access token.
255
+
256
+ [Follow this link to authorize with the SMART
257
+ server](#{auth_url}).
258
+
259
+ Tests will resume once Inferno receives a request at
260
+ `#{config.options[:redirect_uri]}` with a state of `#{state}`.
261
+ )
262
+ end
263
+ }
264
+ }
265
+ test from: :smart_code_received,
266
+ id: :smart_bad_code_verifier_code_received
267
+ test from: :invalid_pkce_request do
268
+ title 'OAuth token exchange fails when code_verifier is incorrect'
269
+ id :smart_bad_code_verifier_token_request
270
+
271
+ def modify_oauth_params(oauth_params)
272
+ oauth_params.merge!(code_verifier: "#{SecureRandom.uuid}-#{SecureRandom.uuid}")
273
+ end
274
+ end
275
+
276
+ test from: :smart_app_redirect_stu2,
277
+ id: :smart_plain_code_verifier_redirect,
278
+ config: {
279
+ options: {
280
+ redirect_message_proc: lambda do |auth_url|
281
+ %(
282
+ ### Invalid PKCE code_verifier 4/4
283
+
284
+ This launch provides a `plain` `code_verifier` instead of one
285
+ encoded with `S256` and verifies the server does not issue an
286
+ access token.
287
+
288
+ [Follow this link to authorize with the SMART
289
+ server](#{auth_url}).
290
+
291
+ Tests will resume once Inferno receives a request at
292
+ `#{config.options[:redirect_uri]}` with a state of `#{state}`.
293
+ )
294
+ end
295
+ }
296
+ }
297
+ test from: :smart_code_received,
298
+ id: :smart_plain_code_verifier_code_received
299
+ test from: :invalid_pkce_request do
300
+ title 'OAuth token exchange fails when code_verifier matches code_challenge'
301
+ id :smart_plain_code_verifier_token_request
302
+
303
+ input :pkce_code_challenge
304
+
305
+ def modify_oauth_params(oauth_params)
306
+ oauth_params.merge!(code_verifier: pkce_code_challenge)
307
+ end
308
+ end
309
+ end
310
+ end