smart_app_launch_test_kit 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,120 @@
1
+ module SMARTAppLaunch
2
+ class SMARTAccessBrandsValidateEndpointURLs < Inferno::Test
3
+ id :smart_access_brands_valid_endpoint_urls
4
+ title 'All Endpoint resource referenced URLS should be valid and available'
5
+ description %(
6
+ Verify that User Access Brands Bundle contains Endpoints that contain URLs that are both valid
7
+ and available.
8
+ )
9
+
10
+ input :user_access_brands_bundle,
11
+ optional: true
12
+
13
+ input :endpoint_availability_limit,
14
+ title: 'Endpoint Availability Limit',
15
+ description: %(
16
+ Input a number to cap the number of Endpoints that Inferno will send requests to check for availability.
17
+ This can help speed up validation when there are large number of endpoints in the Service Base URL Bundle.
18
+ ),
19
+ optional: true
20
+
21
+ input :endpoint_availability_success_rate,
22
+ title: 'Endpoint Availability Success Rate',
23
+ description: %(
24
+ Select an option to choose how many Endpoints have to be available and send back a valid capability
25
+ statement for the Endpoint validation test to pass.
26
+ ),
27
+ type: 'radio',
28
+ options: {
29
+ list_options: [
30
+ {
31
+ label: 'All',
32
+ value: 'all'
33
+ },
34
+ {
35
+ label: 'At Least 1',
36
+ value: 'at_least_1'
37
+ },
38
+ {
39
+ label: 'None',
40
+ value: 'none'
41
+ }
42
+ ]
43
+ },
44
+ default: 'all'
45
+
46
+ def get_endpoint_availability_limit(endpoint_availability_limit)
47
+ return if endpoint_availability_limit.blank?
48
+
49
+ endpoint_availability_limit.to_i
50
+ end
51
+
52
+ def skip_message
53
+ %(
54
+ No User Access Brands request was made in the previous test, and no User Access Brands Bundle was provided as
55
+ input instead. Either provide a User Access Brands Publication URL to retrieve the Bundle via a HTTP GET
56
+ request, or provide the Bundle as an input.
57
+ )
58
+ end
59
+
60
+ run do
61
+ bundle_response = if user_access_brands_bundle.blank?
62
+ load_tagged_requests('smart_access_brands_bundle')
63
+ skip skip_message if requests.length != 1
64
+ requests.first.response_body
65
+ else
66
+ user_access_brands_bundle
67
+ end
68
+
69
+ skip_if bundle_response.blank?, 'No SMART Access Brands Bundle contained in the response'
70
+
71
+ assert_valid_json(bundle_response)
72
+ bundle_resource = FHIR.from_contents(bundle_response)
73
+
74
+ skip_if bundle_resource.entry.empty?, 'The given Bundle does not contain any resources'
75
+
76
+ endpoint_list = bundle_resource
77
+ .entry
78
+ .map(&:resource)
79
+ .select { |resource| resource.resourceType == 'Endpoint' }
80
+ .map(&:address)
81
+ .uniq
82
+
83
+ check_limit = get_endpoint_availability_limit(endpoint_availability_limit)
84
+ one_endpoint_valid = false
85
+
86
+ endpoint_list.each_with_index do |address, index|
87
+ assert_valid_http_uri(address)
88
+
89
+ next if endpoint_availability_success_rate == 'none' || (check_limit.present? && index >= check_limit)
90
+
91
+ address = address.delete_suffix('/')
92
+ get("#{address}/metadata", client: nil, headers: { Accept: 'application/fhir+json' })
93
+
94
+ if endpoint_availability_success_rate == 'all'
95
+ assert_response_status(200)
96
+ assert resource.present?, 'The content received does not appear to be a valid FHIR resource'
97
+ assert_resource_type(:capability_statement)
98
+ else
99
+ warning do
100
+ assert_response_status(200)
101
+ assert resource.present?, 'The content received does not appear to be a valid FHIR resource'
102
+ assert_resource_type(:capability_statement)
103
+ end
104
+
105
+ if !one_endpoint_valid && response[:status] == 200 && resource.present? &&
106
+ resource.resourceType == 'CapabilityStatement'
107
+ one_endpoint_valid = true
108
+ end
109
+ end
110
+ end
111
+
112
+ if endpoint_availability_success_rate == 'at_least_1'
113
+ assert(one_endpoint_valid, %(
114
+ There were no Endpoints that were available and returned a valid Capability Statement in the Service Base
115
+ URL Bundle'
116
+ ))
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,70 @@
1
+ module SMARTAppLaunch
2
+ class SMARTAccessBrandsValidateEndpoints < Inferno::Test
3
+ id :smart_access_brands_valid_endpoints
4
+ title 'SMART Access Brands Bundle contains valid User Access Endpoints'
5
+ description %(
6
+ Verify that Bundle of User Access Brands and Endpoints contains Endpoints that are valid
7
+ Endpoint resources according to the [User Access Endpoint Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-endpoint.html).
8
+
9
+ Along with validating the Endpoint resources, this test also ensures that each endpoint contains a primary brand
10
+ by checking if it is referenced by at least 1 Organization resource.
11
+ )
12
+
13
+ def find_referenced_org(bundle_resource, endpoint_id)
14
+ bundle_resource
15
+ .entry
16
+ .map(&:resource)
17
+ .select { |resource| resource.resourceType == 'Organization' }
18
+ .map(&:endpoint)
19
+ .flatten
20
+ .map(&:reference)
21
+ .select { |reference| reference.include? endpoint_id }
22
+ end
23
+
24
+ def skip_message
25
+ %(
26
+ No User Access Brands request was made in the previous test, and no User Access Brands Bundle was provided as
27
+ input instead. Either provide a User Access Brands Publication URL to retrieve the Bundle via a HTTP GET
28
+ request, or provide the Bundle as an input.
29
+ )
30
+ end
31
+
32
+ input :user_access_brands_bundle,
33
+ optional: true
34
+
35
+ run do
36
+ bundle_response = if user_access_brands_bundle.blank?
37
+ load_tagged_requests('smart_access_brands_bundle')
38
+ skip skip_message if requests.length != 1
39
+ requests.first.response_body
40
+ else
41
+ user_access_brands_bundle
42
+ end
43
+
44
+ skip_if bundle_response.blank?, 'No SMART Access Brands Bundle contained in the response'
45
+
46
+ assert_valid_json(bundle_response)
47
+ bundle_resource = FHIR.from_contents(bundle_response)
48
+
49
+ skip_if bundle_resource.entry.empty?, 'The given Bundle does not contain any resources'
50
+
51
+ assert_valid_bundle_entries(bundle: bundle_resource,
52
+ resource_types: {
53
+ Endpoint: 'http://hl7.org/fhir/smart-app-launch/StructureDefinition/user-access-endpoint'
54
+ })
55
+
56
+ endpoint_ids =
57
+ bundle_resource
58
+ .entry
59
+ .map(&:resource)
60
+ .select { |resource| resource.resourceType == 'Endpoint' }
61
+ .map(&:id)
62
+
63
+ endpoint_ids.each do |endpoint_id|
64
+ endpoint_referenced_orgs = find_referenced_org(bundle_resource, endpoint_id)
65
+ assert !endpoint_referenced_orgs.empty?,
66
+ "Endpoint with id: #{endpoint_id} does not have any associated Organizations in the Bundle."
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'smart_access_brands_validate_bundle_test'
2
+ require_relative 'smart_access_brands_validate_endpoints_test'
3
+ require_relative 'smart_access_brands_validate_endpoint_urls_test'
4
+ require_relative 'smart_access_brands_validate_brands_test'
5
+
6
+ module SMARTAppLaunch
7
+ class SMARTAccessBrandsValidationGroup < Inferno::TestGroup
8
+ id :smart_access_brands_validation
9
+ title 'Validate SMART Access Brands Bundle'
10
+ description %(
11
+ These tests ensure that the publisher's User Access Brands publication is in
12
+ a valid Bundle according to the [User Access Brand Bundle Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brands-bundle.html).
13
+ It ensures that this User Access Brand Bundle has its brand and endpoint
14
+ details contained in valid Endpoints according to the [User Access Endpoint Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-endpoint.html)
15
+ and valid Brands (Organizations) according to the [User Access Brand Profile](https://hl7.org/fhir/smart-app-launch/STU2.2/StructureDefinition-user-access-brand.html).
16
+ )
17
+ run_as_group
18
+
19
+ test from: :smart_access_brands_valid_bundle
20
+ test from: :smart_access_brands_valid_endpoints
21
+ test from: :smart_access_brands_valid_endpoint_urls
22
+ test from: :smart_access_brands_valid_brands
23
+ end
24
+ end
@@ -0,0 +1,243 @@
1
+ require 'tls_test_kit'
2
+
3
+ require_relative 'jwks'
4
+ require_relative 'version'
5
+ require_relative 'discovery_stu2_group'
6
+ require_relative 'standalone_launch_group_stu2_2'
7
+ require_relative 'ehr_launch_group_stu2_2'
8
+ require_relative 'openid_connect_group'
9
+ require_relative 'token_introspection_group_stu2_2'
10
+ require_relative 'token_refresh_stu2_group'
11
+ require_relative 'backend_services_authorization_group'
12
+
13
+ module SMARTAppLaunch
14
+ class SMARTSTU22Suite < Inferno::TestSuite
15
+ id 'smart_stu2_2'
16
+ title 'SMART App Launch STU2.2'
17
+ version VERSION
18
+
19
+ resume_test_route :get, '/launch' do |request|
20
+ request.query_parameters['iss']
21
+ end
22
+
23
+ resume_test_route :get, '/redirect' do |request|
24
+ request.query_parameters['state']
25
+ end
26
+
27
+ route(
28
+ :get,
29
+ '/.well-known/jwks.json',
30
+ ->(_env) { [200, { 'Content-Type' => 'application/json' }, [JWKS.jwks_json]] }
31
+ )
32
+
33
+ @post_auth_page = File.read(File.join(__dir__, 'post_auth.html'))
34
+ post_auth_handler = proc { [200, {}, [@post_auth_page]] }
35
+
36
+ route :get, '/post_auth', post_auth_handler
37
+
38
+ config options: {
39
+ redirect_uri: "#{Inferno::Application['base_url']}/custom/smart_stu2_2/redirect",
40
+ launch_uri: "#{Inferno::Application['base_url']}/custom/smart_stu2_2/launch",
41
+ post_authorization_uri: "#{Inferno::Application['base_url']}/custom/smart_stu2_2/post_auth"
42
+ }
43
+
44
+ description <<~DESCRIPTION
45
+ The SMART App Launch Test Suite verifies that systems correctly implement
46
+ the [SMART App Launch IG](http://hl7.org/fhir/smart-app-launch/STU2.2/)
47
+ for providing authorization and/or authentication services to client
48
+ applications accessing HL7® FHIR® APIs. To get started, please first register
49
+ the Inferno client as a SMART App with the following information:
50
+
51
+ * SMART Launch URI: `#{config.options[:launch_uri]}`
52
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
53
+
54
+ If using asymmetric client authentication, register Inferno with the
55
+ following JWK Set URL:
56
+
57
+ * `#{Inferno::Application[:base_url]}/custom/smart_stu2_2/.well-known/jwks.json`
58
+ DESCRIPTION
59
+
60
+ input_instructions %(
61
+ When running tests at this level, the token introspection endpoint is not available as a manual input.
62
+ Instead, group 3 Token Introspection will assume the token introspection endpoint
63
+ will be output from group 1 Standalone Launch tests, specifically the SMART On FHIR Discovery tests that query
64
+ the .well-known/smart-configuration endpoint. However, including the token introspection
65
+ endpoint as part of the well-known ouput is NOT required and is not formally checked in the SMART On FHIR Discovery
66
+ tests. RFC-7662 on Token Introspection says that "The means by which the protected resource discovers the location of the introspection
67
+ endpoint are outside the scope of this specification" and the Token Introspection IG does not add any further
68
+ requirements to this.
69
+
70
+ If the token introspection endpoint of the system under test is NOT available at .well-known/smart-configuration,
71
+ please run the test groups individually and group 3 Token Introspection will include the introspection endpoint as a manual input.
72
+ )
73
+
74
+ group do
75
+ title 'Standalone Launch'
76
+ id :smart_full_standalone_launch
77
+
78
+ input_instructions <<~INSTRUCTIONS
79
+ Please register the Inferno client as a SMART App with the following
80
+ information:
81
+
82
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
83
+
84
+ If using asymmetric client authentication, register Inferno with the
85
+ following JWK Set URL:
86
+
87
+ * `#{Inferno::Application[:base_url]}/custom/smart_stu2_2/.well-known/jwks.json`
88
+ INSTRUCTIONS
89
+
90
+ run_as_group
91
+
92
+ group from: :smart_discovery_stu2
93
+ group from: :smart_standalone_launch_stu2_2
94
+
95
+ group from: :smart_openid_connect,
96
+ config: {
97
+ inputs: {
98
+ id_token: { name: :standalone_id_token },
99
+ client_id: { name: :standalone_client_id },
100
+ requested_scopes: { name: :standalone_requested_scopes },
101
+ access_token: { name: :standalone_access_token },
102
+ smart_credentials: { name: :standalone_smart_credentials }
103
+ }
104
+ }
105
+
106
+ group from: :smart_token_refresh_stu2,
107
+ id: :smart_standalone_refresh_without_scopes,
108
+ title: 'SMART Token Refresh Without Scopes',
109
+ config: {
110
+ inputs: {
111
+ refresh_token: { name: :standalone_refresh_token },
112
+ client_id: { name: :standalone_client_id },
113
+ client_secret: { name: :standalone_client_secret },
114
+ received_scopes: { name: :standalone_received_scopes }
115
+ },
116
+ outputs: {
117
+ refresh_token: { name: :standalone_refresh_token },
118
+ received_scopes: { name: :standalone_received_scopes },
119
+ access_token: { name: :standalone_access_token },
120
+ token_retrieval_time: { name: :standalone_token_retrieval_time },
121
+ expires_in: { name: :standalone_expires_in },
122
+ smart_credentials: { name: :standalone_smart_credentials }
123
+ }
124
+ }
125
+
126
+ group from: :smart_token_refresh_stu2,
127
+ id: :smart_standalone_refresh_with_scopes,
128
+ title: 'SMART Token Refresh With Scopes',
129
+ config: {
130
+ options: { include_scopes: true },
131
+ inputs: {
132
+ refresh_token: { name: :standalone_refresh_token },
133
+ client_id: { name: :standalone_client_id },
134
+ client_secret: { name: :standalone_client_secret },
135
+ received_scopes: { name: :standalone_received_scopes }
136
+ },
137
+ outputs: {
138
+ refresh_token: { name: :standalone_refresh_token },
139
+ received_scopes: { name: :standalone_received_scopes },
140
+ access_token: { name: :standalone_access_token },
141
+ token_retrieval_time: { name: :standalone_token_retrieval_time },
142
+ expires_in: { name: :standalone_expires_in },
143
+ smart_credentials: { name: :standalone_smart_credentials }
144
+ }
145
+ }
146
+ end
147
+
148
+ group do
149
+ title 'EHR Launch'
150
+ id :smart_full_ehr_launch
151
+
152
+ input_instructions <<~INSTRUCTIONS
153
+ Please register the Inferno client as a SMART App with the following
154
+ information:
155
+
156
+ * SMART Launch URI: `#{config.options[:launch_uri]}`
157
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
158
+
159
+ If using asymmetric client authentication, register Inferno with the
160
+ following JWK Set URL:
161
+
162
+ * `#{Inferno::Application[:base_url]}/custom/smart_stu2_2/.well-known/jwks.json`
163
+ INSTRUCTIONS
164
+
165
+ run_as_group
166
+
167
+ group from: :smart_discovery_stu2
168
+
169
+ group from: :smart_ehr_launch_stu2_2
170
+
171
+ group from: :smart_openid_connect,
172
+ config: {
173
+ inputs: {
174
+ id_token: { name: :ehr_id_token },
175
+ client_id: { name: :ehr_client_id },
176
+ requested_scopes: { name: :ehr_requested_scopes },
177
+ access_token: { name: :ehr_access_token },
178
+ smart_credentials: { name: :ehr_smart_credentials }
179
+ }
180
+ }
181
+
182
+ group from: :smart_token_refresh_stu2,
183
+ id: :smart_ehr_refresh_without_scopes,
184
+ title: 'SMART Token Refresh Without Scopes',
185
+ config: {
186
+ inputs: {
187
+ refresh_token: { name: :ehr_refresh_token },
188
+ client_id: { name: :ehr_client_id },
189
+ client_secret: { name: :ehr_client_secret },
190
+ received_scopes: { name: :ehr_received_scopes }
191
+ },
192
+ outputs: {
193
+ refresh_token: { name: :ehr_refresh_token },
194
+ received_scopes: { name: :ehr_received_scopes },
195
+ access_token: { name: :ehr_access_token },
196
+ token_retrieval_time: { name: :ehr_token_retrieval_time },
197
+ expires_in: { name: :ehr_expires_in },
198
+ smart_credentials: { name: :ehr_smart_credentials }
199
+ }
200
+ }
201
+
202
+ group from: :smart_token_refresh_stu2,
203
+ id: :smart_ehr_refresh_with_scopes,
204
+ title: 'SMART Token Refresh With Scopes',
205
+ config: {
206
+ options: { include_scopes: true },
207
+ inputs: {
208
+ refresh_token: { name: :ehr_refresh_token },
209
+ client_id: { name: :ehr_client_id },
210
+ client_secret: { name: :ehr_client_secret },
211
+ received_scopes: { name: :ehr_received_scopes }
212
+ },
213
+ outputs: {
214
+ refresh_token: { name: :ehr_refresh_token },
215
+ received_scopes: { name: :ehr_received_scopes },
216
+ access_token: { name: :ehr_access_token },
217
+ token_retrieval_time: { name: :ehr_token_retrieval_time },
218
+ expires_in: { name: :ehr_expires_in },
219
+ smart_credentials: { name: :ehr_smart_credentials }
220
+ }
221
+ }
222
+ end
223
+
224
+ group do
225
+ title 'Backend Services'
226
+ id :smart_backend_services
227
+
228
+ input_instructions <<~INSTRUCTIONS
229
+ Please register the Inferno client with the authorization services with the
230
+ following JWK Set URL:
231
+
232
+ * `#{Inferno::Application[:base_url]}/custom/smart_stu2_2/.well-known/jwks.json`
233
+ INSTRUCTIONS
234
+
235
+ run_as_group
236
+
237
+ group from: :smart_discovery_stu2
238
+ group from: :backend_services_authorization
239
+ end
240
+
241
+ group from: :smart_token_introspection_stu2_2
242
+ end
243
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'token_response_body_test_stu2_2'
2
+ require_relative 'standalone_launch_group_stu2'
3
+
4
+ module SMARTAppLaunch
5
+ class StandaloneLaunchGroupSTU22 < StandaloneLaunchGroupSTU2
6
+ id :smart_standalone_launch_stu2_2
7
+ description %(
8
+ # Background
9
+
10
+ The [Standalone
11
+ Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-standalone-launch)
12
+ allows an app, like Inferno, to be launched independent of an
13
+ existing EHR session. It is one of the two launch methods described in
14
+ the SMART App Launch Framework alongside EHR Launch. The app will
15
+ request authorization for the provided scope from the authorization
16
+ endpoint, ultimately receiving an authorization token which can be used
17
+ to gain access to resources on the FHIR server.
18
+
19
+ # Test Methodology
20
+
21
+ Inferno will redirect the user to the the authorization endpoint so that
22
+ they may provide any required credentials and authorize the application.
23
+ Upon successful authorization, Inferno will exchange the authorization
24
+ code provided for an access token.
25
+
26
+ For more information on the #{title}:
27
+
28
+ * [Standalone Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2.2/app-launch.html#launch-app-standalone-launch)
29
+ )
30
+
31
+ config(
32
+ inputs: {
33
+ use_pkce: {
34
+ default: 'true',
35
+ locked: true
36
+ },
37
+ pkce_code_challenge_method: {
38
+ default: 'S256',
39
+ locked: true
40
+ },
41
+ requested_scopes: {
42
+ default: 'launch/patient openid fhirUser offline_access patient/*.rs'
43
+ }
44
+ }
45
+ )
46
+
47
+ test from: :smart_token_response_body_stu2_2
48
+
49
+ token_response_body_index = children.find_index { |child| child.id.to_s.end_with? 'token_response_body' }
50
+ children[token_response_body_index] = children.pop
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'standalone_launch_group_stu2_2'
2
+
3
+ module SMARTAppLaunch
4
+ class SMARTTokenIntrospectionAccessTokenGroupSTU22 < SMARTTokenIntrospectionAccessTokenGroup
5
+ title 'Request New Access Token to Introspect'
6
+ run_as_group
7
+
8
+ id :smart_token_introspection_access_token_group_stu2_2
9
+
10
+ description %(
11
+ These tests are repeated from the Standalone Launch tests in order to receive a new, active access token that
12
+ will be provided for token introspection. This test group may be skipped if the tester can obtain an access token
13
+ __and__ the contents of the access token response body by some other means.
14
+
15
+ These tests are currently designed such that the token introspection URL must be present in the SMART well-known endpoint.
16
+
17
+ )
18
+
19
+ input_instructions %(
20
+ Register Inferno as a Standalone SMART App and provide the registration details below.
21
+ )
22
+
23
+ group from: :smart_standalone_launch_stu2_2
24
+
25
+ standalone_launch_index = children.find_index { |child| child.id.to_s.end_with? 'standalone_launch_stu2' }
26
+ children[standalone_launch_index] = children.pop
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ require_relative 'token_introspection_access_token_group_stu2_2'
2
+ require_relative 'token_introspection_group'
3
+
4
+ module SMARTAppLaunch
5
+ class SMARTTokenIntrospectionGroupSTU22 < SMARTTokenIntrospectionGroup
6
+ title 'Token Introspection'
7
+ id :smart_token_introspection_stu2_2
8
+ description %(
9
+ # Background
10
+
11
+ OAuth 2.0 Token introspection, as described in [RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662), allows
12
+ an authorized resource server to query an OAuth 2.0 authorization server for metadata on a token. The
13
+ [SMART App Launch STU2.2 Implementation Guide Section on Token Introspection](https://hl7.org/fhir/smart-app-launch/STU2.2/token-introspection.html)
14
+ states that "SMART on FHIR EHRs SHOULD support token introspection, which allows a broader ecosystem of resource servers
15
+ to leverage authorization decisions managed by a single authorization server."
16
+
17
+ # Test Methodology
18
+
19
+ In these tests, Inferno acts as an authorized resource server that queries the authorization server about an access
20
+ token, rather than a client to a FHIR resource server as in the previous SMART App Launch tests.
21
+ Ideally, Inferno should be registered with the authorization server as an authorized resource server
22
+ capable of accessing the token introspection endpoint through client credentials, per the SMART IG recommendations.
23
+ However, the SMART IG only formally REQUIRES "some form of authorization" to access
24
+ the token introspection endpoint and does not specifiy any one specific approach. As such, the token introspection tests are
25
+ broken up into three groups that each complete a discrete step in the token introspection process:
26
+
27
+ 1. **Request Access Token Group** - optional but recommended, repeats a subset of Standalone Launch tests
28
+ in order to receive a new access token with an authorization code grant. If skipped, testers will need to
29
+ obtain an access token out-of-band and manually provide values from the access token response as inputs to
30
+ the Validate Token Response group.
31
+ 2. **Issue Token Introspection Request Group** - optional but recommended, completes the introspection requests.
32
+ If skipped, testers will need to complete an introspection request out-of-band and manually provide the introspection
33
+ responses as inputs to the Validate Token Response group.
34
+ 3. **Validate Token Introspection Response Group** - required, validates the contents of the introspection responses.
35
+
36
+ Running all three test groups in order is the simplest and is highly recommended if the environment under test
37
+ can support it, as outputs from one group will feed the inputs of the next group. However, test groups can be run
38
+ independently if needed.
39
+
40
+ See the individual test groups for more details and guidance.
41
+ )
42
+ group from: :smart_token_introspection_access_token_group_stu2_2
43
+
44
+ access_token_group_index = children.find_index { |child| child.id.to_s.end_with? 'access_token_group' }
45
+ children[access_token_group_index] = children.pop
46
+
47
+ input_order :url, :standalone_client_id, :standalone_client_secret,
48
+ :authorization_method, :use_pkce, :pkce_code_challenge_method,
49
+ :standalone_requested_scopes, :client_auth_encryption_method,
50
+ :client_auth_type, :custom_authorization_header,
51
+ :optional_introspection_request_params
52
+ input_instructions %(
53
+ Executing tests at this level will run all three Token Introspection groups back-to-back. If test groups need
54
+ to be run independently, exit this window and select a specific test group instead.
55
+
56
+ These tests are currently designed such that the token introspection URL must be present in the SMART well-known endpoint.
57
+
58
+ If the introspection endpoint is protected, testers must enter their own HTTP Authorization header for the introspection request. See
59
+ [RFC 7616 The 'Basic' HTTP Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc7617) for the most common
60
+ approach that uses client credentials. Testers may also provide any additional parameters needed for their authorization
61
+ server to complete the introspection request.
62
+
63
+ **Note:** For both the Authorization header and request parameters, user-input
64
+ values will be sent exactly as entered and therefore the tester must
65
+ URI-encode any appropriate values.
66
+ )
67
+ end
68
+ end