smart_app_launch_test_kit 1.0.1 → 1.0.2

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/execution_scripts/README.md +16 -0
  3. data/execution_scripts/client_vs_server/access_data_and_continue_client.rb +37 -0
  4. data/execution_scripts/client_vs_server/smart_v22_backend_services_with_commands.yaml +69 -0
  5. data/execution_scripts/client_vs_server/smart_v22_backend_services_with_commands_client_expected.json +1 -0
  6. data/execution_scripts/client_vs_server/smart_v22_backend_services_with_commands_server_expected.json +478 -0
  7. data/execution_scripts/client_vs_server/smart_v22_backend_services_with_commands_server_no_tls_expected.json +479 -0
  8. data/execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands.yaml +122 -0
  9. data/execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_client_expected.json +1 -0
  10. data/execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_client_no_tls_expected.json +1 -0
  11. data/execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_server_expected.json +1 -0
  12. data/execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_server_no_tls_expected.json +1 -0
  13. data/execution_scripts/client_vs_server/smart_v22_public_with_commands.yaml +123 -0
  14. data/execution_scripts/client_vs_server/smart_v22_public_with_commands_client_expected.json +1 -0
  15. data/execution_scripts/client_vs_server/smart_v22_public_with_commands_client_no_tls_expected.json +1 -0
  16. data/execution_scripts/client_vs_server/smart_v22_public_with_commands_server_expected.json +1 -0
  17. data/execution_scripts/client_vs_server/smart_v22_public_with_commands_server_no_tls_expected.json +1 -0
  18. data/execution_scripts/client_vs_server/visit_and_wait_to_return_to_inferno.rb +17 -0
  19. data/execution_scripts/reference_server/base_ref_server_authorize.rb +24 -0
  20. data/execution_scripts/reference_server/base_ref_server_ehr_launch.rb +24 -0
  21. data/execution_scripts/reference_server/ref_server_authorize_85_all_scopes.rb +3 -0
  22. data/execution_scripts/reference_server/ref_server_authorize_launched_all_scopes.rb +3 -0
  23. data/execution_scripts/reference_server/ref_server_ehr_launch_85.rb +3 -0
  24. data/execution_scripts/reference_server/smart_v1_vs_reference_server_with_commands.yaml +60 -0
  25. data/execution_scripts/reference_server/smart_v1_vs_reference_server_with_commands_expected.json +1 -0
  26. data/execution_scripts/reference_server/smart_v22_vs_reference_server_with_commands.yaml +93 -0
  27. data/execution_scripts/reference_server/smart_v22_vs_reference_server_with_commands_expected.json +1 -0
  28. data/execution_scripts/reference_server/smart_v22_vs_reference_server_with_commands_same_host_expected.json +4166 -0
  29. data/execution_scripts/reference_server/smart_v2_vs_reference_server_with_commands.yaml +81 -0
  30. data/execution_scripts/reference_server/smart_v2_vs_reference_server_with_commands_expected.json +1 -0
  31. data/lib/smart_app_launch/app_launch_test.rb +4 -0
  32. data/lib/smart_app_launch/app_redirect_test.rb +2 -1
  33. data/lib/smart_app_launch/client_suite/access_alca_interaction_test.rb +5 -1
  34. data/lib/smart_app_launch/client_suite/access_alcs_interaction_test.rb +5 -1
  35. data/lib/smart_app_launch/client_suite/access_alp_interaction_test.rb +6 -2
  36. data/lib/smart_app_launch/client_suite/access_bsca_interaction_test.rb +4 -1
  37. data/lib/smart_app_launch/client_suite/authentication_verification.rb +1 -1
  38. data/lib/smart_app_launch/client_suite/client_descriptions.rb +4 -3
  39. data/lib/smart_app_launch/cors_metadata_request_test.rb +11 -4
  40. data/lib/smart_app_launch/cors_openid_fhir_user_claim_test.rb +8 -4
  41. data/lib/smart_app_launch/cors_token_exchange_test.rb +8 -4
  42. data/lib/smart_app_launch/cors_well_known_endpoint_test.rb +8 -4
  43. data/lib/smart_app_launch/version.rb +2 -2
  44. metadata +34 -6
@@ -14,7 +14,7 @@ module SMARTAppLaunch
14
14
 
15
15
  input :url
16
16
  input :smart_auth_info, type: :auth_info, options: { mode: 'auth' }
17
- output :state, :pkce_code_challenge, :pkce_code_verifier
17
+ output :state, :pkce_code_challenge, :pkce_code_verifier, :authorization_url
18
18
  receives_request :redirect
19
19
 
20
20
  def default_redirect_uri
@@ -104,6 +104,7 @@ module SMARTAppLaunch
104
104
  )
105
105
 
106
106
  info("Inferno redirecting browser to #{authorization_url}.")
107
+ output(authorization_url:)
107
108
 
108
109
  wait(
109
110
  identifier: state,
@@ -52,6 +52,8 @@ module SMARTAppLaunch
52
52
  description: INPUT_ECHOED_FHIR_RESPONSE_DESCRIPTION
53
53
 
54
54
  output :launch_key
55
+ output :launch_urls
56
+ output :continuation_url
55
57
 
56
58
  def client_suite_id
57
59
  return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
@@ -69,11 +71,13 @@ module SMARTAppLaunch
69
71
  )
70
72
  end
71
73
 
74
+ continuation_url = "#{client_resume_pass_url}?token=#{client_id}"
75
+ output(continuation_url:)
72
76
  wait(
73
77
  identifier: client_id,
74
78
  message: access_wait_dialog_app_launch_access_prefix(client_id, 'confidential asymmetric', client_fhir_base_url) +
75
79
  access_wait_dialog_ehr_launch_instructions(smart_launch_urls, client_fhir_base_url) +
76
- access_wait_dialog_access_response_and_continue_suffix(client_id, client_resume_pass_url)
80
+ access_wait_dialog_access_response_and_continue_suffix(continuation_url)
77
81
  )
78
82
  end
79
83
  end
@@ -52,6 +52,8 @@ module SMARTAppLaunch
52
52
  description: INPUT_ECHOED_FHIR_RESPONSE_DESCRIPTION
53
53
 
54
54
  output :launch_key
55
+ output :launch_urls
56
+ output :continuation_url
55
57
 
56
58
  def client_suite_id
57
59
  return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
@@ -69,11 +71,13 @@ module SMARTAppLaunch
69
71
  )
70
72
  end
71
73
 
74
+ continuation_url = "#{client_resume_pass_url}?token=#{client_id}"
75
+ output(continuation_url:)
72
76
  wait(
73
77
  identifier: client_id,
74
78
  message: access_wait_dialog_app_launch_access_prefix(client_id, 'confidential symmetric', client_fhir_base_url) +
75
79
  access_wait_dialog_ehr_launch_instructions(smart_launch_urls, client_fhir_base_url) +
76
- access_wait_dialog_access_response_and_continue_suffix(client_id, client_resume_pass_url)
80
+ access_wait_dialog_access_response_and_continue_suffix(continuation_url)
77
81
  )
78
82
  end
79
83
  end
@@ -51,6 +51,8 @@ module SMARTAppLaunch
51
51
  description: INPUT_ECHOED_FHIR_RESPONSE_DESCRIPTION
52
52
 
53
53
  output :launch_key
54
+ output :launch_urls
55
+ output :continuation_url
54
56
 
55
57
  def client_suite_id
56
58
  return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
@@ -67,12 +69,14 @@ module SMARTAppLaunch
67
69
  'Input **Launch Context** is not valid JSON and will be disregarded when responding to token requests'
68
70
  )
69
71
  end
70
-
72
+
73
+ continuation_url = "#{client_resume_pass_url}?token=#{client_id}"
74
+ output(continuation_url:)
71
75
  wait(
72
76
  identifier: client_id,
73
77
  message: access_wait_dialog_app_launch_access_prefix(client_id, 'public', client_fhir_base_url) +
74
78
  access_wait_dialog_ehr_launch_instructions(smart_launch_urls, client_fhir_base_url) +
75
- access_wait_dialog_access_response_and_continue_suffix(client_id, client_resume_pass_url)
79
+ access_wait_dialog_access_response_and_continue_suffix(continuation_url)
76
80
  )
77
81
  end
78
82
  end
@@ -31,6 +31,7 @@ module SMARTAppLaunch
31
31
  type: 'textarea',
32
32
  optional: true,
33
33
  description: INPUT_ECHOED_FHIR_RESPONSE_DESCRIPTION
34
+ output :continuation_url
34
35
 
35
36
  def client_suite_id
36
37
  return config.options[:endpoint_suite_id] if config.options[:endpoint_suite_id].present?
@@ -39,10 +40,12 @@ module SMARTAppLaunch
39
40
  end
40
41
 
41
42
  run do
43
+ continuation_url = "#{client_resume_pass_url}?token=#{client_id}"
44
+ output(continuation_url:)
42
45
  wait(
43
46
  identifier: client_id,
44
47
  message: access_wait_dialog_backend_services_access_prefix(client_id, client_fhir_base_url) +
45
- access_wait_dialog_access_response_and_continue_suffix(client_id, client_resume_pass_url)
48
+ access_wait_dialog_access_response_and_continue_suffix(continuation_url)
46
49
  )
47
50
  end
48
51
  end
@@ -34,7 +34,7 @@ module SMARTAppLaunch
34
34
  return unless decoded_token.present?
35
35
 
36
36
  check_jwt_header(decoded_token.header, request_num)
37
- check_jwt_payload(decoded_token.payload, jti_list, request_num)
37
+ check_jwt_payload(decoded_token.unverified_payload, jti_list, request_num)
38
38
  check_jwt_signature(decoded_token, request_num)
39
39
  end
40
40
 
@@ -91,15 +91,16 @@ module SMARTAppLaunch
91
91
 
92
92
  launch_query_string = Rack::Utils.build_query({ iss: fhir_base_url, launch: launch_key })
93
93
  ehr_launch_locations = smart_launch_urls.split(',').map { |launch_url| "#{launch_url}?#{launch_query_string}" }
94
+ output(launch_urls: ehr_launch_locations.join(','))
95
+
94
96
  ehr_launch_links_string = ehr_launch_locations.map { |url| "- [launch](#{url})" }.join("\n")
95
-
96
97
  "\n\nOr open one of the following links in a new tab to perform an EHR launch:\n#{ehr_launch_links_string}\n\n"
97
98
  else
98
99
  ''
99
100
  end
100
101
  end
101
102
 
102
- def access_wait_dialog_access_response_and_continue_suffix(client_id, resume_pass_url)
103
+ def access_wait_dialog_access_response_and_continue_suffix(continuation_url)
103
104
  <<~SUFFIX
104
105
  Inferno will respond to requests with either:
105
106
  - A resource from the Bundle in the **Available Resources** input if the request is a read matching
@@ -107,7 +108,7 @@ module SMARTAppLaunch
107
108
  - Otherwise, the contents of the **Default FHIR Response** if provided.
108
109
  - Otherwise, an OperationOutcome indicating nothing to echo.
109
110
 
110
- [Click here](#{resume_pass_url}?token=#{client_id}) once the client has made a data access request.
111
+ [Click here](#{continuation_url}) once the client has made a data access request.
111
112
  SUFFIX
112
113
  end
113
114
  end
@@ -33,10 +33,17 @@ module SMARTAppLaunch
33
33
 
34
34
  assert_response_status(200)
35
35
  inferno_origin = Inferno::Application['inferno_host']
36
- cors_allow_origin = request.response_header('Access-Control-Allow-Origin')&.value
37
- assert cors_allow_origin.present?, 'No `Access-Control-Allow-Origin` header received.'
38
- assert cors_allow_origin == inferno_origin || cors_allow_origin == '*',
39
- "`Access-Control-Allow-Origin` must be `#{inferno_origin}`, but received: `#{cors_allow_origin}`"
36
+
37
+ if url.starts_with?(inferno_origin)
38
+ info 'No CORS headers required: Inferno and the target server are on the same host.'
39
+ else
40
+ cors_allow_origin = request.response_header('Access-Control-Allow-Origin')&.value
41
+ assert cors_allow_origin.present?, 'No `Access-Control-Allow-Origin` header received.'
42
+ assert cors_allow_origin == inferno_origin || cors_allow_origin == '*',
43
+ "`Access-Control-Allow-Origin` must be `#{inferno_origin}`, but received: `#{cors_allow_origin}`"
44
+ end
45
+
46
+
40
47
  end
41
48
  end
42
49
  end
@@ -41,10 +41,14 @@ module SMARTAppLaunch
41
41
  assert_response_status(200)
42
42
 
43
43
  inferno_origin = Inferno::Application['inferno_host']
44
- cors_allow_origin = request.response_header('Access-Control-Allow-Origin')&.value
45
- assert cors_allow_origin.present?, 'No `Access-Control-Allow-Origin` header received.'
46
- assert cors_allow_origin == inferno_origin || cors_allow_origin == '*',
47
- "`Access-Control-Allow-Origin` must be `#{inferno_origin}`, but received: `#{cors_allow_origin}`"
44
+ if url.starts_with?(inferno_origin)
45
+ info 'No CORS headers required: Inferno and the target server are on the same host.'
46
+ else
47
+ cors_allow_origin = request.response_header('Access-Control-Allow-Origin')&.value
48
+ assert cors_allow_origin.present?, 'No `Access-Control-Allow-Origin` header received.'
49
+ assert cors_allow_origin == inferno_origin || cors_allow_origin == '*',
50
+ "`Access-Control-Allow-Origin` must be `#{inferno_origin}`, but received: `#{cors_allow_origin}`"
51
+ end
48
52
  end
49
53
  end
50
54
  end
@@ -28,10 +28,14 @@ module SMARTAppLaunch
28
28
  skip_if request.status != 200, 'Previous request was unsuccessful, cannot check for CORS support'
29
29
 
30
30
  inferno_origin = Inferno::Application['inferno_host']
31
- cors_header = request.response_header('Access-Control-Allow-Origin')&.value
32
-
33
- assert cors_header == inferno_origin || cors_header == '*',
34
- "Request must have `Access-Control-Allow-Origin` header containing `#{inferno_origin}`"
31
+ if request.url.starts_with?(inferno_origin)
32
+ info 'No CORS headers required: Inferno and the target server are on the same host.'
33
+ else
34
+ cors_header = request.response_header('Access-Control-Allow-Origin')&.value
35
+
36
+ assert cors_header == inferno_origin || cors_header == '*',
37
+ "Request must have `Access-Control-Allow-Origin` header containing `#{inferno_origin}`"
38
+ end
35
39
  end
36
40
  end
37
41
  end
@@ -33,10 +33,14 @@ module SMARTAppLaunch
33
33
  'Origin' => inferno_origin })
34
34
  assert_response_status(200)
35
35
 
36
- cors_allow_origin = request.response_header('Access-Control-Allow-Origin')&.value
37
- assert cors_allow_origin.present?, 'No `Access-Control-Allow-Origin` header received.'
38
- assert cors_allow_origin == inferno_origin || cors_allow_origin == '*',
39
- "`Access-Control-Allow-Origin` must be `#{inferno_origin}`, but received: `#{cors_allow_origin}`"
36
+ if request.url.starts_with?(inferno_origin)
37
+ info 'No CORS headers required: Inferno and the target server are on the same host.'
38
+ else
39
+ cors_allow_origin = request.response_header('Access-Control-Allow-Origin')&.value
40
+ assert cors_allow_origin.present?, 'No `Access-Control-Allow-Origin` header received.'
41
+ assert cors_allow_origin == inferno_origin || cors_allow_origin == '*',
42
+ "`Access-Control-Allow-Origin` must be `#{inferno_origin}`, but received: `#{cors_allow_origin}`"
43
+ end
40
44
  end
41
45
  end
42
46
  end
@@ -1,4 +1,4 @@
1
1
  module SMARTAppLaunch
2
- VERSION = '1.0.1'.freeze
3
- LAST_UPDATED = '2026-05-12'.freeze
2
+ VERSION = '1.0.2'.freeze
3
+ LAST_UPDATED = '2026-05-22'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_app_launch_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
- - Stephen MacVicar
7
+ - Inferno Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-12 00:00:00.000000000 Z
11
+ date: 2026-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inferno_core
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '2.6'
53
+ version: '3.2'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '2.6'
60
+ version: '3.2'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: tls_test_kit
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -165,7 +165,6 @@ dependencies:
165
165
  description: Inferno Tests for the SMART Application Launch Framework Implementation
166
166
  Guide
167
167
  email:
168
- - inferno@groups.mitre.org
169
168
  executables: []
170
169
  extensions: []
171
170
  extra_rdoc_files: []
@@ -183,6 +182,35 @@ files:
183
182
  - config/presets/smart_access_brands_example_2.json
184
183
  - config/presets/smart_access_brands_example_3.json
185
184
  - config/presets/smart_access_brands_example_4.json
185
+ - execution_scripts/README.md
186
+ - execution_scripts/client_vs_server/access_data_and_continue_client.rb
187
+ - execution_scripts/client_vs_server/smart_v22_backend_services_with_commands.yaml
188
+ - execution_scripts/client_vs_server/smart_v22_backend_services_with_commands_client_expected.json
189
+ - execution_scripts/client_vs_server/smart_v22_backend_services_with_commands_server_expected.json
190
+ - execution_scripts/client_vs_server/smart_v22_backend_services_with_commands_server_no_tls_expected.json
191
+ - execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands.yaml
192
+ - execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_client_expected.json
193
+ - execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_client_no_tls_expected.json
194
+ - execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_server_expected.json
195
+ - execution_scripts/client_vs_server/smart_v22_confidential_symmetric_with_commands_server_no_tls_expected.json
196
+ - execution_scripts/client_vs_server/smart_v22_public_with_commands.yaml
197
+ - execution_scripts/client_vs_server/smart_v22_public_with_commands_client_expected.json
198
+ - execution_scripts/client_vs_server/smart_v22_public_with_commands_client_no_tls_expected.json
199
+ - execution_scripts/client_vs_server/smart_v22_public_with_commands_server_expected.json
200
+ - execution_scripts/client_vs_server/smart_v22_public_with_commands_server_no_tls_expected.json
201
+ - execution_scripts/client_vs_server/visit_and_wait_to_return_to_inferno.rb
202
+ - execution_scripts/reference_server/base_ref_server_authorize.rb
203
+ - execution_scripts/reference_server/base_ref_server_ehr_launch.rb
204
+ - execution_scripts/reference_server/ref_server_authorize_85_all_scopes.rb
205
+ - execution_scripts/reference_server/ref_server_authorize_launched_all_scopes.rb
206
+ - execution_scripts/reference_server/ref_server_ehr_launch_85.rb
207
+ - execution_scripts/reference_server/smart_v1_vs_reference_server_with_commands.yaml
208
+ - execution_scripts/reference_server/smart_v1_vs_reference_server_with_commands_expected.json
209
+ - execution_scripts/reference_server/smart_v22_vs_reference_server_with_commands.yaml
210
+ - execution_scripts/reference_server/smart_v22_vs_reference_server_with_commands_expected.json
211
+ - execution_scripts/reference_server/smart_v22_vs_reference_server_with_commands_same_host_expected.json
212
+ - execution_scripts/reference_server/smart_v2_vs_reference_server_with_commands.yaml
213
+ - execution_scripts/reference_server/smart_v2_vs_reference_server_with_commands_expected.json
186
214
  - lib/smart_app_launch/app_launch_test.rb
187
215
  - lib/smart_app_launch/app_redirect_test.rb
188
216
  - lib/smart_app_launch/app_redirect_test_stu2.rb