onc_certification_g10_test_kit 2.3.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,211 @@
1
+ module ONCCertificationG10TestKit
2
+ class SMARTInvalidTokenGroupSTU2 < Inferno::TestGroup
3
+ title 'SMART App Launch Error: Invalid Access Token Request'
4
+ short_title 'SMART Invalid Token Request'
5
+ input_instructions %(
6
+ Register Inferno as a standalone application using the following information:
7
+
8
+ * Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
9
+ )
10
+ description %(
11
+ # Background
12
+
13
+ The Invalid Access Token Request Sequence verifies that a SMART Launch
14
+ Sequence, specifically the [Standalone
15
+ Launch](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
16
+ Sequence, does not work in the case where the client sends an invalid
17
+ Authorization code or client ID during the code exchange step. This must
18
+ not result in a successful launch.
19
+
20
+ This test is not included as part of a regular SMART Launch Sequence
21
+ because some servers may not accept an authorization code after it has
22
+ been used unsuccessfully in this manner.
23
+ )
24
+ id :g10_smart_invalid_token_request_stu2
25
+ run_as_group
26
+
27
+ input :use_pkce,
28
+ title: 'Proof Key for Code Exchange (PKCE)',
29
+ type: 'radio',
30
+ default: 'true',
31
+ locked: true,
32
+ options: {
33
+ list_options: [
34
+ {
35
+ label: 'Enabled',
36
+ value: 'true'
37
+ },
38
+ {
39
+ label: 'Disabled',
40
+ value: 'false'
41
+ }
42
+ ]
43
+ }
44
+ input :pkce_code_challenge_method,
45
+ optional: true,
46
+ title: 'PKCE Code Challenge Method',
47
+ type: 'radio',
48
+ default: 'S256',
49
+ locked: true,
50
+ options: {
51
+ list_options: [
52
+ {
53
+ label: 'S256',
54
+ value: 'S256'
55
+ },
56
+ {
57
+ label: 'Plain',
58
+ value: 'plain'
59
+ }
60
+ ]
61
+ }
62
+
63
+ input_order :url,
64
+ :standalone_client_id,
65
+ :standalone_client_secret,
66
+ :standalone_requested_scopes,
67
+ :use_pkce,
68
+ :pkce_code_challenge_method,
69
+ :smart_authorization_url,
70
+ :smart_token_url
71
+
72
+ config(
73
+ inputs: {
74
+ client_id: {
75
+ name: :standalone_client_id,
76
+ title: 'Standalone Client ID',
77
+ description: 'Client ID provided during registration of Inferno as a standalone application'
78
+ },
79
+ client_secret: {
80
+ name: :standalone_client_secret,
81
+ title: 'Standalone Client Secret',
82
+ description: 'Client Secret provided during registration of Inferno as a standalone application'
83
+ },
84
+ requested_scopes: {
85
+ name: :standalone_requested_scopes,
86
+ title: 'Standalone Scope',
87
+ description: 'OAuth 2.0 scope provided by system to enable all required functionality',
88
+ type: 'textarea',
89
+ default: %(
90
+ launch/patient openid fhirUser offline_access
91
+ patient/Medication.read patient/AllergyIntolerance.read
92
+ patient/CarePlan.read patient/CareTeam.read patient/Condition.read
93
+ patient/Device.read patient/DiagnosticReport.read
94
+ patient/DocumentReference.read patient/Encounter.read
95
+ patient/Goal.read patient/Immunization.read patient/Location.read
96
+ patient/MedicationRequest.read patient/Observation.read
97
+ patient/Organization.read patient/Patient.read
98
+ patient/Practitioner.read patient/Procedure.read
99
+ patient/Provenance.read patient/PractitionerRole.read
100
+ ).gsub(/\s{2,}/, ' ').strip
101
+ },
102
+ url: {
103
+ title: 'Standalone FHIR Endpoint',
104
+ description: 'URL of the FHIR endpoint used by standalone applications'
105
+ },
106
+ code: {
107
+ name: :invalid_token_code
108
+ },
109
+ state: {
110
+ name: :invalid_token_state
111
+ },
112
+ smart_authorization_url: {
113
+ title: 'OAuth 2.0 Authorize Endpoint',
114
+ description: 'OAuth 2.0 Authorize Endpoint provided during the patient standalone launch'
115
+ },
116
+ smart_token_url: {
117
+ title: 'OAuth 2.0 Token Endpoint',
118
+ description: 'OAuth 2.0 Token Endpoint provided during the patient standalone launch'
119
+ },
120
+ pkce_code_verifier: {
121
+ name: :invalid_token_pkce_code_verifier
122
+ }
123
+ },
124
+ outputs: {
125
+ code: { name: :invalid_token_code },
126
+ state: { name: :invalid_token_state },
127
+ expires_in: { name: :invalid_token_expires_in },
128
+ pkce_code_verifier: { name: :invalid_token_pkce_code_verifier }
129
+ },
130
+ requests: {
131
+ redirect: { name: :invalid_token_redirect },
132
+ token: { name: :invalid_token_token }
133
+ }
134
+ )
135
+
136
+ test from: :smart_app_redirect_stu2
137
+ test from: :smart_code_received
138
+
139
+ test do
140
+ title ' OAuth token exchange fails when supplied invalid code'
141
+ description %(
142
+ If the request failed verification or is invalid, the authorization
143
+ server returns an error response.
144
+ )
145
+ uses_request :redirect
146
+
147
+ input :use_pkce, :pkce_code_verifier, :client_id, :client_secret, :smart_token_url
148
+
149
+ run do
150
+ skip_if request.query_parameters['error'].present?, 'Error during authorization request'
151
+
152
+ oauth2_params = {
153
+ grant_type: 'authorization_code',
154
+ code: 'BAD_CODE',
155
+ redirect_uri: config.options[:redirect_uri]
156
+ }
157
+ oauth2_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
158
+
159
+ if client_secret.present?
160
+ client_credentials = "#{client_id}:#{client_secret}"
161
+ oauth2_headers['Authorization'] = "Basic #{Base64.strict_encode64(client_credentials)}"
162
+ else
163
+ oauth2_params[:client_id] = client_id
164
+ end
165
+
166
+ oauth2_params[:code_verifier] = pkce_code_verifier if use_pkce == 'true'
167
+
168
+ post(smart_token_url, body: oauth2_params, name: :token, headers: oauth2_headers)
169
+
170
+ assert_response_status(400)
171
+ end
172
+ end
173
+
174
+ test do
175
+ title 'OAuth token exchange fails when supplied invalid client ID'
176
+ description %(
177
+ If the request failed verification or is invalid, the authorization
178
+ server returns an error response.
179
+ )
180
+ uses_request :redirect
181
+
182
+ input :use_pkce, :pkce_code_verifier, :code, :smart_token_url, :client_secret
183
+
184
+ run do
185
+ skip_if request.query_parameters['error'].present?, 'Error during authorization request'
186
+
187
+ client_id = 'BAD_CLIENT_ID'
188
+
189
+ oauth2_params = {
190
+ grant_type: 'authorization_code',
191
+ code: code,
192
+ redirect_uri: config.options[:redirect_uri]
193
+ }
194
+ oauth2_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
195
+
196
+ if client_secret.present?
197
+ client_credentials = "#{client_id}:#{client_secret}"
198
+ oauth2_headers['Authorization'] = "Basic #{Base64.strict_encode64(client_credentials)}"
199
+ else
200
+ oauth2_params[:client_id] = client_id
201
+ end
202
+
203
+ oauth2_params[:code_verifier] = pkce_code_verifier if use_pkce == 'true'
204
+
205
+ post(smart_token_url, body: oauth2_params, name: :token, headers: oauth2_headers)
206
+
207
+ assert_response_status([400, 401])
208
+ end
209
+ end
210
+ end
211
+ end
@@ -20,13 +20,17 @@ module ONCCertificationG10TestKit
20
20
 
21
21
  description %(
22
22
  This scenario demonstrates the ability to perform a Patient Standalone
23
- Launch to a [SMART on FHIR](http://hl7.org/fhir/smart-app-launch/1.0.0/)
24
- confidential client with limited access granted to the app based on user
25
- input. The tester is expected to grant the application access to a subset
26
- of desired resource types. The launch is performed using the same app
27
- configuration as in the Standalone Patient App test, demonstrating that
28
- the user is control over what scopes are granted to the app as required in
29
- the (g)(10) Standardized API criterion.
23
+ Launch to a SMART on FHIR confidential client with limited access granted
24
+ to the app based on user input. The tester is expected to grant the
25
+ application access to a subset of desired resource types. The launch is
26
+ performed using the same app configuration as in the Standalone Patient
27
+ App test, demonstrating that the user is control over what scopes are
28
+ granted to the app as required in the (g)(10) Standardized API criterion.
29
+
30
+ * [SMART on FHIR
31
+ (STU1)](http://www.hl7.org/fhir/smart-app-launch/1.0.0/)
32
+ * [SMART on FHIR
33
+ (STU2)](http://hl7.org/fhir/smart-app-launch/STU2)
30
34
  )
31
35
  id :g10_smart_limited_app
32
36
  run_as_group
@@ -76,14 +80,14 @@ module ONCCertificationG10TestKit
76
80
  Sequence](http://hl7.org/fhir/smart-app-launch/1.0.0/index.html#standalone-launch-sequence)
77
81
  )
78
82
 
83
+ required_suite_options smart_app_launch_version: 'smart_app_launch_1'
84
+
79
85
  config(
80
86
  inputs: {
81
87
  client_id: { locked: true },
82
88
  client_secret: { locked: true, optional: false },
83
89
  url: { locked: true },
84
90
  requested_scopes: { locked: true },
85
- use_pkce: { locked: true },
86
- pkce_code_challenge_method: { locked: true },
87
91
  code: { name: :limited_code },
88
92
  state: { name: :limited_state },
89
93
  patient_id: { name: :limited_patient_id },
@@ -161,6 +165,128 @@ module ONCCertificationG10TestKit
161
165
  end
162
166
  end
163
167
 
168
+ group from: :smart_standalone_launch_stu2,
169
+ config: {
170
+ inputs: {
171
+ use_pkce: {
172
+ default: 'true',
173
+ locked: true
174
+ },
175
+ pkce_code_challenge_method: {
176
+ locked: true
177
+ }
178
+ }
179
+ } do
180
+ title 'Standalone Launch With Limited Scope'
181
+ description %(
182
+ # Background
183
+
184
+ The [Standalone
185
+ Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
186
+ allows an app, like Inferno, to be launched independent of an
187
+ existing EHR session. It is one of the two launch methods described in
188
+ the SMART App Launch Framework alongside EHR Launch. The app will
189
+ request authorization for the provided scope from the authorization
190
+ endpoint, ultimately receiving an authorization token which can be used
191
+ to gain access to resources on the FHIR server.
192
+
193
+ # Test Methodology
194
+
195
+ Inferno will redirect the user to the the authorization endpoint so that
196
+ they may provide any required credentials and authorize the application.
197
+ Upon successful authorization, Inferno will exchange the authorization
198
+ code provided for an access token.
199
+
200
+ For more information on the #{title}:
201
+
202
+ * [Standalone Launch
203
+ Sequence](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
204
+ )
205
+
206
+ required_suite_options smart_app_launch_version: 'smart_app_launch_2'
207
+
208
+ config(
209
+ inputs: {
210
+ client_id: { locked: true },
211
+ client_secret: { locked: true },
212
+ url: { locked: true },
213
+ requested_scopes: { locked: true },
214
+ code: { name: :limited_code },
215
+ state: { name: :limited_state },
216
+ patient_id: { name: :limited_patient_id },
217
+ access_token: { name: :limited_access_token },
218
+ # TODO: separate standalone/ehr discovery outputs
219
+ smart_authorization_url: { locked: true, title: 'SMART Authorization Url' },
220
+ smart_token_url: { locked: true, title: 'SMART Token Url' },
221
+ received_scopes: { name: :limited_received_scopes },
222
+ smart_credentials: { name: :limited_smart_credentials }
223
+ },
224
+ outputs: {
225
+ code: { name: :limited_code },
226
+ token_retrieval_time: { name: :limited_token_retrieval_time },
227
+ state: { name: :limited_state },
228
+ id_token: { name: :limited_id_token },
229
+ refresh_token: { name: :limited_refresh_token },
230
+ access_token: { name: :limited_access_token },
231
+ expires_in: { name: :limited_expires_in },
232
+ patient_id: { name: :limited_patient_id },
233
+ encounter_id: { name: :limited_encounter_id },
234
+ received_scopes: { name: :limited_received_scopes },
235
+ intent: { name: :limited_intent },
236
+ smart_credentials: { name: :limited_smart_credentials }
237
+ },
238
+ requests: {
239
+ redirect: { name: :limited_redirect },
240
+ token: { name: :limited_token }
241
+ },
242
+ options: {
243
+ redirect_message_proc: lambda do |auth_url|
244
+ expected_resource_string =
245
+ expected_resources
246
+ .split(',')
247
+ .map(&:strip)
248
+ .map { |resource_type| "* #{resource_type}\n" }
249
+ .join
250
+
251
+ <<~MESSAGE
252
+ ### #{self.class.parent.parent.title}
253
+
254
+ [Follow this link to authorize with the SMART
255
+ server](#{auth_url}).
256
+
257
+ Tests will resume once Inferno receives a request at
258
+ `#{config.options[:redirect_uri]}` with a state of `#{state}`.
259
+
260
+ Access should only be granted to the following resources:
261
+
262
+ #{expected_resource_string}
263
+ MESSAGE
264
+ end
265
+ }
266
+ )
267
+
268
+ input :expected_resources,
269
+ title: 'Expected Resource Grant for Limited Access Launch',
270
+ description: 'The user will only grant access to the following resources during authorization.',
271
+ default: 'Patient, Condition, Observation'
272
+
273
+ test from: :g10_patient_context,
274
+ config: {
275
+ inputs: {
276
+ patient_id: { name: :limited_patient_id },
277
+ smart_credentials: { name: :limited_smart_credentials }
278
+ }
279
+ }
280
+
281
+ test from: :g10_limited_scope_grant do
282
+ config(
283
+ inputs: {
284
+ received_scopes: { name: :limited_received_scopes }
285
+ }
286
+ )
287
+ end
288
+ end
289
+
164
290
  group from: :g10_restricted_resource_type_access,
165
291
  config: {
166
292
  inputs: {
@@ -8,9 +8,9 @@ module ONCCertificationG10TestKit
8
8
  * Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
9
9
 
10
10
  Enter in the appropriate scope to enable patient-level access to all
11
- relevant resources. In addition, support for the OpenID Connect (openid
12
- fhirUser), refresh tokens (offline_access), and patient context
13
- (launch/patient) are required.
11
+ relevant resources. If using SMART v2, v2-style scopes must be used. In
12
+ addition, support for the OpenID Connect (openid fhirUser), refresh tokens
13
+ (offline_access), and patient context (launch/patient) are required.
14
14
  )
15
15
  description %(
16
16
  # Background
@@ -53,7 +53,19 @@ module ONCCertificationG10TestKit
53
53
  },
54
54
  requested_scopes: {
55
55
  name: :public_requested_scopes,
56
- title: 'Public Launch Scope'
56
+ title: 'Public Launch Scope',
57
+ default: %(
58
+ launch/patient openid fhirUser offline_access
59
+ patient/Medication.read patient/AllergyIntolerance.read
60
+ patient/CarePlan.read patient/CareTeam.read patient/Condition.read
61
+ patient/Device.read patient/DiagnosticReport.read
62
+ patient/DocumentReference.read patient/Encounter.read
63
+ patient/Goal.read patient/Immunization.read patient/Location.read
64
+ patient/MedicationRequest.read patient/Observation.read
65
+ patient/Organization.read patient/Patient.read
66
+ patient/Practitioner.read patient/Procedure.read
67
+ patient/Provenance.read patient/PractitionerRole.read
68
+ ).gsub(/\s{2,}/, ' ').strip
57
69
  },
58
70
  url: {
59
71
  title: 'Public Launch FHIR Endpoint',
@@ -0,0 +1,130 @@
1
+ module ONCCertificationG10TestKit
2
+ class SMARTPublicStandaloneLaunchGroupSTU2 < SMARTAppLaunch::StandaloneLaunchGroupSTU2
3
+ title 'Public Client Standalone Launch with OpenID Connect'
4
+ short_title 'SMART Public Client Launch'
5
+ input_instructions %(
6
+ Register Inferno as a standalone application using the following information:
7
+
8
+ * Redirect URI: `#{SMARTAppLaunch::AppRedirectTest.config.options[:redirect_uri]}`
9
+
10
+ Enter in the appropriate scope to enable patient-level access to all
11
+ relevant resources. In addition, support for the OpenID Connect (openid
12
+ fhirUser), refresh tokens (offline_access), and patient context
13
+ (launch/patient) are required.
14
+ )
15
+ id :g10_public_standalone_launch_stu2
16
+ run_as_group
17
+
18
+ config(
19
+ inputs: {
20
+ client_id: {
21
+ name: :public_client_id,
22
+ title: 'Public Launch Client ID'
23
+ },
24
+ client_secret: {
25
+ name: :public_client_secret,
26
+ title: 'Public Launch Client Secret',
27
+ default: nil,
28
+ optional: true,
29
+ locked: true
30
+ },
31
+ requested_scopes: {
32
+ name: :public_requested_scopes,
33
+ title: 'Public Launch Scope',
34
+ default: %(
35
+ launch/patient openid fhirUser offline_access patient/Medication.rs
36
+ patient/AllergyIntolerance.rs patient/CarePlan.rs
37
+ patient/CareTeam.rs patient/Condition.rs patient/Device.rs
38
+ patient/DiagnosticReport.rs patient/DocumentReference.rs
39
+ patient/Encounter.rs patient/Goal.rs patient/Immunization.rs
40
+ patient/Location.rs patient/MedicationRequest.rs
41
+ patient/Observation.rs patient/Organization.rs patient/Patient.rs
42
+ patient/Practitioner.rs patient/Procedure.rs patient/Provenance.rs
43
+ patient/PractitionerRole.rs
44
+ ).gsub(/\s{2,}/, ' ').strip
45
+ },
46
+ url: {
47
+ title: 'Public Launch FHIR Endpoint',
48
+ description: 'URL of the FHIR endpoint used by standalone applications'
49
+ },
50
+ code: {
51
+ name: :public_code
52
+ },
53
+ state: {
54
+ name: :public_state
55
+ },
56
+ smart_authorization_url: {
57
+ title: 'OAuth 2.0 Authorize Endpoint',
58
+ description: 'OAuth 2.0 Authorize Endpoint provided during the patient standalone launch'
59
+ },
60
+ smart_token_url: {
61
+ title: 'OAuth 2.0 Token Endpoint',
62
+ description: 'OAuth 2.0 Token Endpoint provided during the patient standalone launch'
63
+ },
64
+ smart_credentials: {
65
+ name: :public_smart_credentials
66
+ },
67
+ use_pkce: {
68
+ default: 'true',
69
+ locked: true
70
+ },
71
+ pkce_code_challenge_method: {
72
+ locked: true
73
+ }
74
+ },
75
+ outputs: {
76
+ code: { name: :public_code },
77
+ token_retrieval_time: { name: :public_token_retrieval_time },
78
+ state: { name: :public_state },
79
+ id_token: { name: :public_id_token },
80
+ refresh_token: { name: :public_refresh_token },
81
+ access_token: { name: :public_access_token },
82
+ expires_in: { name: :public_expires_in },
83
+ patient_id: { name: :public_patient_id },
84
+ encounter_id: { name: :public_encounter_id },
85
+ received_scopes: { name: :public_received_scopes },
86
+ intent: { name: :public_intent },
87
+ smart_credentials: { name: :public_smart_credentials }
88
+ },
89
+ requests: {
90
+ redirect: { name: :public_redirect },
91
+ token: { name: :public_token }
92
+ }
93
+ )
94
+
95
+ input_order :url,
96
+ :public_client_id,
97
+ :public_client_secret,
98
+ :public_requested_scopes,
99
+ :use_pkce,
100
+ :pkce_code_challenge_method,
101
+ :smart_authorization_url,
102
+ :smart_token_url
103
+
104
+ test from: :g10_patient_context,
105
+ config: {
106
+ inputs: {
107
+ patient_id: { name: :public_patient_id },
108
+ smart_credentials: { name: :public_smart_credentials }
109
+ }
110
+ }
111
+
112
+ test do
113
+ title 'OAuth token exchange response contains OpenID Connect id_token'
114
+ description %(
115
+ This test requires that an OpenID Connect id_token is provided to
116
+ demonstrate authentication capabilies for public clients.
117
+ )
118
+ id :g10_public_launch_id_token
119
+
120
+ input :id_token,
121
+ name: :public_id_token,
122
+ locked: true,
123
+ optional: true
124
+
125
+ run do
126
+ assert id_token.present?, 'Token response did not provide an id_token as required.'
127
+ end
128
+ end
129
+ end
130
+ end