smart_app_launch_test_kit 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/inferno_reference_server_preset.json +103 -0
  3. data/config/presets/inferno_reference_server_stu2_2_preset.json +89 -0
  4. data/config/presets/inferno_reference_server_stu2_preset.json +89 -0
  5. data/config/presets/smart_access_brands.json.erb +13 -0
  6. data/config/presets/smart_access_brands_example_1.json +37 -0
  7. data/config/presets/smart_access_brands_example_2.json +37 -0
  8. data/config/presets/smart_access_brands_example_3.json +37 -0
  9. data/config/presets/smart_access_brands_example_4.json +37 -0
  10. data/lib/smart_app_launch/cors_metadata_request_test.rb +40 -0
  11. data/lib/smart_app_launch/cors_openid_fhir_user_claim_test.rb +48 -0
  12. data/lib/smart_app_launch/cors_token_exchange_test.rb +35 -0
  13. data/lib/smart_app_launch/cors_well_known_endpoint_test.rb +40 -0
  14. data/lib/smart_app_launch/discovery_stu2_2_group.rb +12 -0
  15. data/lib/smart_app_launch/ehr_launch_group_stu2_2.rb +14 -0
  16. data/lib/smart_app_launch/metadata.rb +75 -0
  17. data/lib/smart_app_launch/openid_connect_group_stu2_2.rb +40 -0
  18. data/lib/smart_app_launch/smart_access_brands_examples/smart_access_brands_example.json.erb +198 -0
  19. data/lib/smart_app_launch/smart_access_brands_suite.rb +4 -1
  20. data/lib/smart_app_launch/smart_stu1_suite.rb +7 -6
  21. data/lib/smart_app_launch/smart_stu2_2_suite.rb +10 -10
  22. data/lib/smart_app_launch/smart_stu2_suite.rb +11 -10
  23. data/lib/smart_app_launch/standalone_launch_group_stu2_2.rb +14 -0
  24. data/lib/smart_app_launch/token_exchange_stu2_2_test.rb +30 -0
  25. data/lib/smart_app_launch/token_exchange_test.rb +10 -14
  26. data/lib/smart_app_launch/token_introspection_access_token_group_stu2_2.rb +2 -4
  27. data/lib/smart_app_launch/token_introspection_group.rb +1 -2
  28. data/lib/smart_app_launch/token_introspection_group_stu2_2.rb +0 -21
  29. data/lib/smart_app_launch/token_introspection_request_group.rb +47 -36
  30. data/lib/smart_app_launch/token_refresh_test.rb +13 -9
  31. data/lib/smart_app_launch/version.rb +2 -1
  32. data/lib/smart_app_launch_test_kit.rb +1 -0
  33. metadata +26 -8
@@ -11,42 +11,44 @@ module SMARTAppLaunch
11
11
  id :smart_token_introspection_request_group
12
12
  description %(
13
13
  This group of tests executes the token introspection requests and ensures the correct HTTP response is returned
14
- but does not validate the contents of the token introspection response.
14
+ but does not validate the contents of the token introspection response.
15
15
 
16
- If Inferno cannot reasonably be configured to be authorized to access the token introspection endpoint, these tests
16
+ If Inferno cannot reasonably be configured to be authorized to access the token introspection endpoint, these tests
17
17
  can be skipped. Instead, an out-of-band token introspection request must be completed and the response body
18
18
  manually provided as input for the Validate Introspection Response test group.
19
19
  )
20
20
 
21
21
  input_instructions %(
22
- If the Request New Access Token group was executed, the access token input will auto-populate with that token.
23
- Otherwise an active access token needs to be obtained out-of-band and input.
24
-
25
- Per [RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662#section-2), "the definition of an active token is
26
- currently dependent upon the authorization server, but this is commonly a token that has been issued by this
27
- authorization server, is not expired, has not been revoked, and is valid for use at the protected resource making
22
+ If the Request New Access Token group was executed, the access token input will auto-populate with that token.
23
+ Otherwise an active access token needs to be obtained out-of-band and input.
24
+
25
+ Per [RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662#section-2), "the definition of an active token is
26
+ currently dependent upon the authorization server, but this is commonly a token that has been issued by this
27
+ authorization server, is not expired, has not been revoked, and is valid for use at the protected resource making
28
28
  the introspection call."
29
29
 
30
30
  If the introspection endpoint is protected, testers must enter their own HTTP Authorization header for the introspection request. See
31
31
  [RFC 7616 The 'Basic' HTTP Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc7617) for the most common
32
- approach that uses client credentials. Testers may also provide any additional parameters needed for their authorization
32
+ approach that uses client credentials. Testers may also provide any additional parameters needed for their authorization
33
33
  server to complete the introspection request.
34
34
 
35
35
  **Note:** For both the Authorization header and request parameters, user-input
36
36
  values will be sent exactly as entered and therefore the tester must URI-encode any appropriate values.
37
37
  )
38
38
 
39
- input :well_known_introspection_url,
40
- title: 'Token Introspection Endpoint URL',
39
+ input :well_known_introspection_url,
40
+ title: 'Token Introspection Endpoint URL',
41
41
  description: 'The complete URL of the token introspection endpoint.'
42
42
 
43
43
  input :custom_authorization_header,
44
- title: 'HTTP Authorization Header for Introspection Request',
44
+ title: 'Custom HTTP Headers for Introspection Request',
45
45
  type: 'textarea',
46
46
  optional: true,
47
47
  description: %(
48
- Include header name, auth scheme, and auth parameters.
49
- Ex: 'Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW'
48
+ Add custom headers for the introspection request by adding each header's name and value with a new line
49
+ between each header.
50
+ Ex:
51
+ <Header 1 Name>: <Value 1>
50
52
  )
51
53
 
52
54
  input :optional_introspection_request_params,
@@ -54,69 +56,78 @@ module SMARTAppLaunch
54
56
  type: 'textarea',
55
57
  optional: true,
56
58
  description: %(
57
- Any additional parameters to append to the request body, separated by &. Example: 'param1=abc&param2=def'
59
+ Any additional parameters to append to the request body, separated by &. Example: 'param1=abc&param2=def'
58
60
  )
59
61
 
60
62
  test do
61
63
  title 'Token introspection endpoint returns a response when provided an active token'
62
64
  description %(
63
65
  This test will execute a token introspection request for an active token and ensure a 200 status and valid JSON
64
- body are returned in the response.
66
+ body are returned in the response.
65
67
  )
66
68
 
67
- input :standalone_access_token,
69
+ input :standalone_access_token,
68
70
  title: 'Access Token',
69
71
  description: 'The access token to be introspected. MUST be active.'
70
72
 
71
-
72
73
  output :active_token_introspection_response_body
73
74
 
74
75
  run do
75
-
76
76
  # If this is being chained from an earlier test, it might be blank if not present in the well-known endpoint
77
77
  skip_if well_known_introspection_url.nil?, 'No introspection URL present in SMART well-known endpoint.'
78
78
 
79
- headers = {'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded'}
79
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded' }
80
80
  body = "token=#{standalone_access_token}"
81
81
 
82
82
  if custom_authorization_header.present?
83
- parsed_header = custom_authorization_header.split(':', 2)
84
- assert parsed_header.length == 2, "Incorrect custom HTTP header format input, expected: '<header name>: <header value>'"
85
- headers[parsed_header[0]] = parsed_header[1].strip
83
+ custom_headers = custom_authorization_header.split("\n")
84
+ custom_headers.each do |custom_header|
85
+ parsed_header = custom_header.split(':', 2)
86
+ assert parsed_header.length == 2,
87
+ 'Incorrect custom HTTP header format input, expected: "<header name>: <header value>"'
88
+ headers[parsed_header[0]] = parsed_header[1].strip
89
+ end
86
90
  end
87
91
 
88
- if optional_introspection_request_params.present?
89
- body += "&#{optional_introspection_request_params}"
90
- end
92
+ body += "&#{optional_introspection_request_params}" if optional_introspection_request_params.present?
91
93
 
92
- post(well_known_introspection_url, body: body, headers: headers)
94
+ post(well_known_introspection_url, body:, headers:)
93
95
 
94
96
  assert_response_status(200)
95
97
  output active_token_introspection_response_body: request.response_body
96
98
  end
97
-
98
99
  end
99
100
 
100
- test do
101
+ test do
101
102
  title 'Token introspection endpoint returns a response when provided an invalid token'
102
103
  description %(
103
104
  This test will execute a token introspection request for an invalid token and ensure a 200 status and valid JSON
104
- body are returned in response.
105
+ body are returned in response.
105
106
  )
106
107
 
107
108
  output :invalid_token_introspection_response_body
108
109
  run do
109
-
110
110
  # If this is being chained from an earlier test, it might be blank if not present in the well-known endpoint
111
111
  skip_if well_known_introspection_url.nil?, 'No introspection URL present in SMART well-known endpoint.'
112
112
 
113
- headers = {'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded'}
114
- body = "token=invalid_token_value"
115
- post(well_known_introspection_url, body: body, headers: headers)
116
-
113
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded' }
114
+ body = 'token=invalid_token_value'
115
+
116
+ if custom_authorization_header.present?
117
+ custom_headers = custom_authorization_header.split("\n")
118
+ custom_headers.each do |custom_header|
119
+ parsed_header = custom_header.split(':', 2)
120
+ assert parsed_header.length == 2,
121
+ 'Incorrect custom HTTP header format input, expected: "<header name>: <header value>"'
122
+ headers[parsed_header[0]] = parsed_header[1].strip
123
+ end
124
+ end
125
+
126
+ post(well_known_introspection_url, body:, headers:)
127
+
117
128
  assert_response_status(200)
118
129
  output invalid_token_introspection_response_body: request.response_body
119
130
  end
120
131
  end
121
132
  end
122
- end
133
+ end
@@ -30,6 +30,10 @@ module SMARTAppLaunch
30
30
  end
31
31
  end
32
32
 
33
+ def make_auth_token_request(smart_token_url, oauth2_params, oauth2_headers)
34
+ post(smart_token_url, body: oauth2_params, name: :token_refresh, headers: oauth2_headers)
35
+ end
36
+
33
37
  run do
34
38
  skip_if refresh_token.blank?
35
39
 
@@ -43,7 +47,7 @@ module SMARTAppLaunch
43
47
 
44
48
  add_credentials_to_request(oauth2_headers, oauth2_params)
45
49
 
46
- post(smart_token_url, body: oauth2_params, name: :token_refresh, headers: oauth2_headers)
50
+ make_auth_token_request(smart_token_url, oauth2_params, oauth2_headers)
47
51
 
48
52
  assert_response_status(200)
49
53
  assert_valid_json(request.response_body)
@@ -52,14 +56,14 @@ module SMARTAppLaunch
52
56
 
53
57
  token_response_body = JSON.parse(request.response_body)
54
58
  output smart_credentials: {
55
- refresh_token: token_response_body['refresh_token'].presence || refresh_token,
56
- access_token: token_response_body['access_token'],
57
- expires_in: token_response_body['expires_in'],
58
- client_id: client_id,
59
- client_secret: client_secret,
60
- token_retrieval_time: token_retrieval_time,
61
- token_url: smart_token_url
62
- }.to_json
59
+ refresh_token: token_response_body['refresh_token'].presence || refresh_token,
60
+ access_token: token_response_body['access_token'],
61
+ expires_in: token_response_body['expires_in'],
62
+ client_id:,
63
+ client_secret:,
64
+ token_retrieval_time:,
65
+ token_url: smart_token_url
66
+ }.to_json
63
67
  end
64
68
  end
65
69
  end
@@ -1,3 +1,4 @@
1
1
  module SMARTAppLaunch
2
- VERSION = '0.4.5'.freeze
2
+ VERSION = '0.5.0'.freeze
3
+ LAST_UPDATED = '2025-02-10'.freeze # TODO: update next release
3
4
  end
@@ -1,5 +1,6 @@
1
1
  require 'tls_test_kit'
2
2
 
3
+ require_relative 'smart_app_launch/metadata'
3
4
  require_relative 'smart_app_launch/smart_stu1_suite'
4
5
  require_relative 'smart_app_launch/smart_stu2_suite'
5
6
  require_relative 'smart_app_launch/smart_stu2_2_suite'
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: 0.4.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen MacVicar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-08 00:00:00.000000000 Z
11
+ date: 2025-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inferno_core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.4.2
19
+ version: 0.6.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.4.2
26
+ version: 0.6.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: json-jwt
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.2.0
61
+ version: 0.3.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.2.0
68
+ version: 0.3.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: database_cleaner-sequel
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +145,14 @@ extensions: []
145
145
  extra_rdoc_files: []
146
146
  files:
147
147
  - LICENSE
148
+ - config/presets/inferno_reference_server_preset.json
149
+ - config/presets/inferno_reference_server_stu2_2_preset.json
150
+ - config/presets/inferno_reference_server_stu2_preset.json
151
+ - config/presets/smart_access_brands.json.erb
152
+ - config/presets/smart_access_brands_example_1.json
153
+ - config/presets/smart_access_brands_example_2.json
154
+ - config/presets/smart_access_brands_example_3.json
155
+ - config/presets/smart_access_brands_example_4.json
148
156
  - lib/smart_app_launch/app_launch_test.rb
149
157
  - lib/smart_app_launch/app_redirect_test.rb
150
158
  - lib/smart_app_launch/app_redirect_test_stu2.rb
@@ -157,14 +165,21 @@ files:
157
165
  - lib/smart_app_launch/backend_services_invalid_jwt_test.rb
158
166
  - lib/smart_app_launch/client_assertion_builder.rb
159
167
  - lib/smart_app_launch/code_received_test.rb
168
+ - lib/smart_app_launch/cors_metadata_request_test.rb
169
+ - lib/smart_app_launch/cors_openid_fhir_user_claim_test.rb
170
+ - lib/smart_app_launch/cors_token_exchange_test.rb
171
+ - lib/smart_app_launch/cors_well_known_endpoint_test.rb
160
172
  - lib/smart_app_launch/discovery_stu1_group.rb
173
+ - lib/smart_app_launch/discovery_stu2_2_group.rb
161
174
  - lib/smart_app_launch/discovery_stu2_group.rb
162
175
  - lib/smart_app_launch/ehr_launch_group.rb
163
176
  - lib/smart_app_launch/ehr_launch_group_stu2.rb
164
177
  - lib/smart_app_launch/ehr_launch_group_stu2_2.rb
165
178
  - lib/smart_app_launch/jwks.rb
166
179
  - lib/smart_app_launch/launch_received_test.rb
180
+ - lib/smart_app_launch/metadata.rb
167
181
  - lib/smart_app_launch/openid_connect_group.rb
182
+ - lib/smart_app_launch/openid_connect_group_stu2_2.rb
168
183
  - lib/smart_app_launch/openid_decode_id_token_test.rb
169
184
  - lib/smart_app_launch/openid_fhir_user_claim_test.rb
170
185
  - lib/smart_app_launch/openid_required_configuration_fields_test.rb
@@ -174,6 +189,7 @@ files:
174
189
  - lib/smart_app_launch/openid_token_payload_test.rb
175
190
  - lib/smart_app_launch/post_auth.html
176
191
  - lib/smart_app_launch/smart_access_brands_examples/r4_capability_statement.json
192
+ - lib/smart_app_launch/smart_access_brands_examples/smart_access_brands_example.json.erb
177
193
  - lib/smart_app_launch/smart_access_brands_group.rb
178
194
  - lib/smart_app_launch/smart_access_brands_retrieval_group.rb
179
195
  - lib/smart_app_launch/smart_access_brands_retrieve_bundle_test.rb
@@ -190,6 +206,7 @@ files:
190
206
  - lib/smart_app_launch/standalone_launch_group.rb
191
207
  - lib/smart_app_launch/standalone_launch_group_stu2.rb
192
208
  - lib/smart_app_launch/standalone_launch_group_stu2_2.rb
209
+ - lib/smart_app_launch/token_exchange_stu2_2_test.rb
193
210
  - lib/smart_app_launch/token_exchange_stu2_test.rb
194
211
  - lib/smart_app_launch/token_exchange_test.rb
195
212
  - lib/smart_app_launch/token_introspection_access_token_group.rb
@@ -217,6 +234,7 @@ homepage: https://github.com/inferno-framework/smart-app-launch-test-kit
217
234
  licenses:
218
235
  - Apache-2.0
219
236
  metadata:
237
+ inferno_test_kit: 'true'
220
238
  homepage_uri: https://github.com/inferno-framework/smart-app-launch-test-kit
221
239
  source_code_uri: https://github.com/inferno-framework/smart-app-launch-test-kit
222
240
  post_install_message:
@@ -227,14 +245,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
227
245
  requirements:
228
246
  - - ">="
229
247
  - !ruby/object:Gem::Version
230
- version: 3.1.2
248
+ version: 3.3.6
231
249
  required_rubygems_version: !ruby/object:Gem::Requirement
232
250
  requirements:
233
251
  - - ">="
234
252
  - !ruby/object:Gem::Version
235
253
  version: '0'
236
254
  requirements: []
237
- rubygems_version: 3.5.9
255
+ rubygems_version: 3.5.22
238
256
  signing_key:
239
257
  specification_version: 4
240
258
  summary: Inferno Tests for the SMART Application Launch Framework Implementation Guide