smart_app_launch_test_kit 0.4.3 → 0.4.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.
@@ -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