smart_app_launch_test_kit 0.5.1 → 0.6.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/inferno_reference_server_preset.json +15 -86
  3. data/config/presets/inferno_reference_server_stu2_2_preset.json +20 -69
  4. data/config/presets/inferno_reference_server_stu2_preset.json +20 -69
  5. data/lib/smart_app_launch/app_redirect_test.rb +12 -44
  6. data/lib/smart_app_launch/app_redirect_test_stu2.rb +2 -17
  7. data/lib/smart_app_launch/backend_services_authorization_group.rb +33 -57
  8. data/lib/smart_app_launch/backend_services_authorization_request_builder.rb +22 -9
  9. data/lib/smart_app_launch/backend_services_authorization_request_success_test.rb +26 -21
  10. data/lib/smart_app_launch/backend_services_authorization_response_body_test.rb +19 -5
  11. data/lib/smart_app_launch/backend_services_invalid_client_assertion_test.rb +30 -25
  12. data/lib/smart_app_launch/backend_services_invalid_grant_type_test.rb +30 -24
  13. data/lib/smart_app_launch/backend_services_invalid_jwt_test.rb +31 -26
  14. data/lib/smart_app_launch/client_assertion_builder.rb +27 -12
  15. data/lib/smart_app_launch/cors_openid_fhir_user_claim_test.rb +2 -2
  16. data/lib/smart_app_launch/cors_token_exchange_test.rb +2 -2
  17. data/lib/smart_app_launch/discovery_stu1_group.rb +6 -2
  18. data/lib/smart_app_launch/ehr_launch_group.rb +41 -24
  19. data/lib/smart_app_launch/ehr_launch_group_stu2.rb +26 -10
  20. data/lib/smart_app_launch/ehr_launch_group_stu2_2.rb +0 -16
  21. data/lib/smart_app_launch/openid_fhir_user_claim_test.rb +5 -4
  22. data/lib/smart_app_launch/openid_token_payload_test.rb +6 -8
  23. data/lib/smart_app_launch/smart_stu1_suite.rb +32 -24
  24. data/lib/smart_app_launch/smart_stu2_2_suite.rb +56 -30
  25. data/lib/smart_app_launch/smart_stu2_suite.rb +56 -31
  26. data/lib/smart_app_launch/smart_tls_test.rb +14 -0
  27. data/lib/smart_app_launch/standalone_launch_group.rb +42 -25
  28. data/lib/smart_app_launch/standalone_launch_group_stu2.rb +26 -10
  29. data/lib/smart_app_launch/standalone_launch_group_stu2_2.rb +0 -16
  30. data/lib/smart_app_launch/token_exchange_stu2_2_test.rb +5 -17
  31. data/lib/smart_app_launch/token_exchange_stu2_test.rb +8 -67
  32. data/lib/smart_app_launch/token_exchange_test.rb +18 -38
  33. data/lib/smart_app_launch/token_introspection_access_token_group.rb +12 -4
  34. data/lib/smart_app_launch/token_introspection_access_token_group_stu2_2.rb +9 -1
  35. data/lib/smart_app_launch/token_introspection_group.rb +2 -4
  36. data/lib/smart_app_launch/token_introspection_request_group.rb +2 -4
  37. data/lib/smart_app_launch/token_introspection_response_group.rb +64 -49
  38. data/lib/smart_app_launch/token_refresh_body_test.rb +9 -2
  39. data/lib/smart_app_launch/token_refresh_stu2_test.rb +10 -17
  40. data/lib/smart_app_launch/token_refresh_test.rb +19 -20
  41. data/lib/smart_app_launch/token_response_body_test.rb +14 -4
  42. data/lib/smart_app_launch/token_response_body_test_stu2_2.rb +3 -2
  43. data/lib/smart_app_launch/version.rb +2 -2
  44. data/lib/smart_app_launch/well_known_endpoint_test.rb +11 -1
  45. metadata +5 -4
@@ -13,20 +13,20 @@ module SMARTAppLaunch
13
13
  in which the access token was given to the client.
14
14
  )
15
15
 
16
- input_instructions %(
17
- There are two categories of input for this test group:
16
+ input_instructions %(
17
+ There are two categories of input for this test group:
18
18
 
19
19
  1. The access token response values, which will dictate what the tests will expect to find in the token
20
20
  introspection response. If the Request New Access Token group was run, these inputs will auto-populate.
21
-
21
+
22
22
  2. The token introspection response bodies. If the Issue Introspection Request test group was run, these will
23
23
  auto-populate; otherwise, the tester will need to an run out-of-band INTROSPECTION requests for a. An ACTIVE
24
24
  access token, AND b. An INACTIVE OR INVALID token
25
25
 
26
26
  See [RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662#section-2) for details on active vs inactive tokens.
27
27
  )
28
-
29
- test do
28
+
29
+ test do
30
30
  title 'Token introspection response for an active token contains required fields'
31
31
 
32
32
  description %(
@@ -39,29 +39,26 @@ module SMARTAppLaunch
39
39
  * `scope`, `client_id`, and `exp` claim(s) match between introspection response and access token
40
40
 
41
41
  It is not possible to know what the expected value for `exp` is in advance, so Inferno tests that the claim is
42
- present and represents a time greater than or equal to 10 minutes in the past.
42
+ present and represents a time greater than or equal to 10 minutes in the past.
43
43
 
44
44
  Conditionally Required:
45
- * IF launch context parameter(s) included in access token, introspection response includes claim(s) for
46
- launch context parameter(s)
45
+ * IF launch context parameter(s) included in access token, introspection response includes claim(s) for
46
+ launch context parameter(s)
47
47
  * Parameters checked for are `patient` and `encounter`
48
48
  * IF identity token was included as part of access token response, `iss` and `sub` claims are present in the
49
49
  introspection response and match those of the orignal ID token
50
50
 
51
51
  Optional but Recommended:
52
- * IF identity token was included as part of access token response, `fhirUser` claim SHOULD be present in
52
+ * IF identity token was included as part of access token response, `fhirUser` claim SHOULD be present in
53
53
  introspection response and should match the claim in the ID token
54
54
  )
55
55
 
56
- input :standalone_client_id,
57
- title: 'Access Token client_id',
58
- description: 'ID of the client that requested the access token being introspected'
59
-
56
+ input :standalone_smart_auth_info, type: :auth_info, options: { mode: 'auth' }
60
57
 
61
58
  input :standalone_received_scopes,
62
59
  title: 'Expected Introspection Response Value: scope',
63
60
  description: 'A space-separated list of scopes from the original access token response body'
64
-
61
+
65
62
  input :standalone_id_token,
66
63
  title: 'Access Token Response: id_token',
67
64
  type: 'textarea',
@@ -85,16 +82,16 @@ module SMARTAppLaunch
85
82
 
86
83
  def get_json_claim_value(json_response, claim_key)
87
84
  claim_value = json_response[claim_key]
88
- assert claim_value != nil, "Failure: introspection response has no claim for '#{claim_key}'"
89
- return claim_value
85
+ assert !claim_value.nil?, "Failure: introspection response has no claim for '#{claim_key}'"
86
+ claim_value
90
87
  end
91
-
88
+
92
89
  def assert_introspection_response_match(json_response, claim_key, expected_value)
93
90
  expected_value = expected_value.strip
94
91
  claim_value = get_json_claim_value(json_response, claim_key)
95
92
  claim_value = claim_value.strip
96
- assert claim_value.eql?(expected_value),
97
- "Failure: expected introspection response value for '#{claim_key}' to match expected value '#{expected_value}'"
93
+ assert claim_value.eql?(expected_value),
94
+ "Failure: expected introspection response value for '#{claim_key}' to match expected value '#{expected_value}'"
98
95
  end
99
96
 
100
97
  run do
@@ -103,47 +100,57 @@ module SMARTAppLaunch
103
100
  active_introspection_response_body_parsed = JSON.parse(active_token_introspection_response_body)
104
101
 
105
102
  # Required Fields
106
- assert active_introspection_response_body_parsed['active'] == true, "Failure: expected introspection response for 'active' to be Boolean value true for valid token"
107
- assert_introspection_response_match(active_introspection_response_body_parsed, 'client_id', standalone_client_id)
103
+ assert active_introspection_response_body_parsed['active'] == true,
104
+ "Failure: expected introspection response for 'active' to be Boolean value true for valid token"
105
+ assert_introspection_response_match(active_introspection_response_body_parsed, 'client_id',
106
+ standalone_smart_auth_info.client_id)
108
107
 
109
108
  response_scope_value = get_json_claim_value(active_introspection_response_body_parsed, 'scope')
110
109
 
111
110
  # splitting contents and comparing values allows a scope lists with the same contents but different orders to still pass
112
- response_scopes_split = response_scope_value.split()
113
- expected_scopes_split = standalone_received_scopes.split()
111
+ response_scopes_split = response_scope_value.split
112
+ expected_scopes_split = standalone_received_scopes.split
114
113
 
115
- assert response_scopes_split.length() == expected_scopes_split.length(),
116
- "Failure: number of scopes in introspection response, #{response_scopes_split.length()}, does not match number of scopes in access token response, #{expected_scopes_split.length()}"
114
+ assert response_scopes_split.length == expected_scopes_split.length,
115
+ "Failure: number of scopes in introspection response, #{response_scopes_split.length}, does not match number of scopes in access token response, #{expected_scopes_split.length}"
117
116
 
118
117
  expected_scopes_split.each do |scope|
119
- assert response_scopes_split.include?(scope), "Failure: expected scope '#{scope}' not present in introspection response scopes"
118
+ assert response_scopes_split.include?(scope),
119
+ "Failure: expected scope '#{scope}' not present in introspection response scopes"
120
120
  end
121
121
 
122
- # Cannot verify exact value for exp, so instead ensure its value represents a time >= 10 minutes in the past
122
+ # Cannot verify exact value for exp, so instead ensure its value represents a time >= 10 minutes in the past
123
123
  exp = active_introspection_response_body_parsed['exp']
124
- assert exp != nil, "Failure: introspection response has no claim for 'exp'"
125
- current_time = Time.now.to_i
126
- assert exp.to_i >= current_time - 600, "Failure: expired token, exp claim of #{exp} for active token is more than 10 minutes in the past"
127
-
124
+ assert !exp.nil?, "Failure: introspection response has no claim for 'exp'"
125
+ current_time = Time.now.to_i
126
+ assert exp.to_i >= current_time - 600,
127
+ "Failure: expired token, exp claim of #{exp} for active token is more than 10 minutes in the past"
128
+
128
129
  # Conditional fields
129
- assert_introspection_response_match(active_introspection_response_body_parsed, 'patient', standalone_patient_id) if standalone_patient_id.present?
130
- assert_introspection_response_match(active_introspection_response_body_parsed, 'encounter', standalone_encounter_id) if standalone_encounter_id.present?
130
+ if standalone_patient_id.present?
131
+ assert_introspection_response_match(active_introspection_response_body_parsed, 'patient',
132
+ standalone_patient_id)
133
+ end
134
+ if standalone_encounter_id.present?
135
+ assert_introspection_response_match(active_introspection_response_body_parsed, 'encounter',
136
+ standalone_encounter_id)
137
+ end
131
138
 
132
139
  # ID Token Fields
133
140
  if standalone_id_token.present?
134
- id_payload, id_header =
135
- JWT.decode(
136
- standalone_id_token,
137
- nil,
138
- false
139
- )
140
-
141
+ id_payload, =
142
+ JWT.decode(
143
+ standalone_id_token,
144
+ nil,
145
+ false
146
+ )
147
+
141
148
  # Required fields if ID token present
142
149
  id_token_iss = id_payload['iss']
143
150
  id_token_sub = id_payload['sub']
144
151
 
145
- assert id_token_iss != nil, "Failure: ID token from access token response does not have 'iss' claim"
146
- assert id_token_sub != nil, "Failure: ID token from access token response does not have 'sub' claim"
152
+ assert !id_token_iss.nil?, "Failure: ID token from access token response does not have 'iss' claim"
153
+ assert !id_token_sub.nil?, "Failure: ID token from access token response does not have 'sub' claim"
147
154
  assert_introspection_response_match(active_introspection_response_body_parsed, 'iss', id_token_iss)
148
155
  assert_introspection_response_match(active_introspection_response_body_parsed, 'sub', id_token_sub)
149
156
 
@@ -151,9 +158,14 @@ module SMARTAppLaunch
151
158
  fhirUser_id_claim = id_payload['fhirUser']
152
159
  fhirUser_intr_claim = active_introspection_response_body_parsed['fhirUser']
153
160
 
154
- info do
155
- assert fhirUser_intr_claim != nil, "Introspection response SHOULD include claim for fhirUser because ID token present in access token response" if fhirUser_id_claim != nil
156
- assert fhirUser_intr_claim.eql?(fhirUser_id_claim), "Introspection response claim for fhirUser SHOULD match value in ID token" if fhirUser_id_claim != nil
161
+ info do
162
+ unless fhirUser_id_claim.nil?
163
+ assert !fhirUser_intr_claim.nil?,
164
+ 'Introspection response SHOULD include claim for fhirUser because ID token present in access token response'
165
+
166
+ assert fhirUser_intr_claim.eql?(fhirUser_id_claim),
167
+ 'Introspection response claim for fhirUser SHOULD match value in ID token'
168
+ end
157
169
  end
158
170
  end
159
171
  end
@@ -180,12 +192,15 @@ module SMARTAppLaunch
180
192
  description: 'The JSON body of the token introspection response when provided an INVALID token'
181
193
 
182
194
  run do
183
- skip_if invalid_token_introspection_response_body.nil?, 'No invalid introspection response available to validate.'
195
+ skip_if invalid_token_introspection_response_body.nil?,
196
+ 'No invalid introspection response available to validate.'
184
197
  assert_valid_json(invalid_token_introspection_response_body)
185
198
  invalid_token_introspection_response_body_parsed = JSON.parse(invalid_token_introspection_response_body)
186
- assert invalid_token_introspection_response_body_parsed['active'] == false, "Failure: expected introspection response for 'active' to be Boolean value false for invalid token"
187
- assert invalid_token_introspection_response_body_parsed.size == 1, "Failure: expected only 'active' field to be present in introspection response for invalid token"
199
+ assert invalid_token_introspection_response_body_parsed['active'] == false,
200
+ "Failure: expected introspection response for 'active' to be Boolean value false for invalid token"
201
+ assert invalid_token_introspection_response_body_parsed.size == 1,
202
+ "Failure: expected only 'active' field to be present in introspection response for invalid token"
188
203
  end
189
204
  end
190
205
  end
191
- end
206
+ end
@@ -15,7 +15,8 @@ module SMARTAppLaunch
15
15
  Scopes returned must be a strict subset of the scopes granted in the original launch.
16
16
  )
17
17
  input :received_scopes
18
- output :refresh_token, :access_token, :token_retrieval_time, :expires_in, :received_scopes
18
+ input :smart_auth_info, type: :auth_info, options: { mode: 'auth' }
19
+ output :refresh_token, :access_token, :token_retrieval_time, :expires_in, :received_scopes, :smart_auth_info
19
20
  uses_request :token_refresh
20
21
 
21
22
  run do
@@ -25,16 +26,22 @@ module SMARTAppLaunch
25
26
 
26
27
  body = JSON.parse(response[:body])
27
28
  output refresh_token: body['refresh_token'] if body.key? 'refresh_token'
29
+ smart_auth_info.refresh_token = refresh_token if refresh_token.present?
28
30
 
29
31
  required_fields = ['access_token', 'token_type', 'expires_in', 'scope']
30
32
  validate_required_fields_present(body, required_fields)
31
33
 
32
34
  old_received_scopes = received_scopes
35
+ smart_auth_info.issue_time = Time.now
33
36
  output access_token: body['access_token'],
34
- token_retrieval_time: Time.now.iso8601,
37
+ token_retrieval_time: smart_auth_info.issue_time.iso8601,
35
38
  expires_in: body['expires_in'],
36
39
  received_scopes: body['scope']
37
40
 
41
+ smart_auth_info.access_token = access_token
42
+ smart_auth_info.expires_in = expires_in
43
+ output smart_auth_info: smart_auth_info
44
+
38
45
  validate_token_field_types(body)
39
46
  validate_token_type(body)
40
47
 
@@ -16,30 +16,23 @@ module SMARTAppLaunch
16
16
  the Pragma response header field with a value of no-cache to be
17
17
  consistent with the requirements of the inital access token exchange.
18
18
  )
19
- input :client_auth_type
20
- input :client_auth_encryption_method, optional: true
21
- input :client_secret, optional: true
22
19
 
23
- def add_credentials_to_request(oauth2_headers, oauth2_params)
24
- case client_auth_type
25
- when 'public'
26
- oauth2_params['client_id'] = client_id
27
- when 'confidential_symmetric'
28
- assert client_secret.present?,
29
- "A client secret must be provided when using confidential symmetric client authentication."
20
+ input :smart_auth_info, type: :auth_info, options: { mode: 'auth' }
30
21
 
31
- credentials = Base64.strict_encode64("#{client_id}:#{client_secret}")
32
- oauth2_headers['Authorization'] = "Basic #{credentials}"
33
- when 'confidential_asymmetric'
22
+ def add_credentials_to_request(oauth2_headers, oauth2_params)
23
+ if smart_auth_info.asymmetric_auth?
34
24
  oauth2_params.merge!(
35
25
  client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
36
26
  client_assertion: ClientAssertionBuilder.build(
37
- iss: client_id,
38
- sub: client_id,
39
- aud: smart_token_url,
40
- client_auth_encryption_method: client_auth_encryption_method
27
+ iss: smart_auth_info.client_id,
28
+ sub: smart_auth_info.client_id,
29
+ aud: smart_auth_info.token_url,
30
+ client_auth_encryption_method: smart_auth_info.encryption_algorithm,
31
+ custom_jwks: smart_auth_info.jwks
41
32
  )
42
33
  )
34
+ else
35
+ super
43
36
  end
44
37
  end
45
38
  end
@@ -16,17 +16,18 @@ module SMARTAppLaunch
16
16
  the Pragma response header field with a value of no-cache to be
17
17
  consistent with the requirements of the inital access token exchange.
18
18
  )
19
- input :smart_token_url, :refresh_token, :client_id, :received_scopes
20
- input :client_secret, optional: true
21
- output :smart_credentials, :token_retrieval_time
19
+ input :received_scopes
20
+ input :smart_auth_info, type: :auth_info, options: { mode: 'auth' }
21
+
22
+ output :smart_credentials, :token_retrieval_time, :smart_auth_info
22
23
  makes_request :token_refresh
23
24
 
24
25
  def add_credentials_to_request(oauth2_headers, oauth2_params)
25
- if client_secret.present?
26
- credentials = Base64.strict_encode64("#{client_id}:#{client_secret}")
26
+ if smart_auth_info.symmetric_auth?
27
+ credentials = Base64.strict_encode64("#{smart_auth_info.client_id}:#{smart_auth_info.client_secret}")
27
28
  oauth2_headers['Authorization'] = "Basic #{credentials}"
28
29
  else
29
- oauth2_params['client_id'] = client_id
30
+ oauth2_params['client_id'] = smart_auth_info.client_id
30
31
  end
31
32
  end
32
33
 
@@ -35,11 +36,11 @@ module SMARTAppLaunch
35
36
  end
36
37
 
37
38
  run do
38
- skip_if refresh_token.blank?
39
+ skip_if smart_auth_info.refresh_token.blank?
39
40
 
40
41
  oauth2_params = {
41
42
  'grant_type' => 'refresh_token',
42
- 'refresh_token' => refresh_token
43
+ 'refresh_token' => smart_auth_info.refresh_token
43
44
  }
44
45
  oauth2_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
45
46
 
@@ -47,23 +48,21 @@ module SMARTAppLaunch
47
48
 
48
49
  add_credentials_to_request(oauth2_headers, oauth2_params)
49
50
 
50
- make_auth_token_request(smart_token_url, oauth2_params, oauth2_headers)
51
+ make_auth_token_request(smart_auth_info.token_url, oauth2_params, oauth2_headers)
51
52
 
52
53
  assert_response_status(200)
53
54
  assert_valid_json(request.response_body)
54
55
 
55
- output token_retrieval_time: Time.now.iso8601
56
-
56
+ smart_auth_info.issue_time = Time.now
57
57
  token_response_body = JSON.parse(request.response_body)
58
- output smart_credentials: {
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
58
+
59
+ smart_auth_info.refresh_token = token_response_body['refresh_token'].presence || smart_auth_info.refresh_token
60
+ smart_auth_info.access_token = token_response_body['access_token']
61
+ smart_auth_info.expires_in = token_response_body['expires_in']
62
+
63
+ output smart_credentials: smart_auth_info,
64
+ token_retrieval_time: smart_auth_info.issue_time.iso8601,
65
+ smart_auth_info: smart_auth_info
67
66
  end
68
67
  end
69
68
  end
@@ -16,7 +16,7 @@ module SMARTAppLaunch
16
16
  )
17
17
  id :smart_token_response_body
18
18
 
19
- input :requested_scopes
19
+ input :smart_auth_info, type: :auth_info, options: { mode: 'auth' }
20
20
  output :id_token,
21
21
  :refresh_token,
22
22
  :access_token,
@@ -24,7 +24,9 @@ module SMARTAppLaunch
24
24
  :patient_id,
25
25
  :encounter_id,
26
26
  :received_scopes,
27
- :intent
27
+ :intent,
28
+ :smart_auth_info
29
+
28
30
  uses_request :token
29
31
 
30
32
  run do
@@ -33,6 +35,10 @@ module SMARTAppLaunch
33
35
  assert_valid_json(request.response_body)
34
36
  token_response_body = JSON.parse(request.response_body)
35
37
 
38
+ smart_auth_info.refresh_token = token_response_body['refresh_token']
39
+ smart_auth_info.access_token = token_response_body['access_token']
40
+ smart_auth_info.expires_in = token_response_body['expires_in']
41
+
36
42
  output id_token: token_response_body['id_token'],
37
43
  refresh_token: token_response_body['refresh_token'],
38
44
  access_token: token_response_body['access_token'],
@@ -40,12 +46,16 @@ module SMARTAppLaunch
40
46
  patient_id: token_response_body['patient'],
41
47
  encounter_id: token_response_body['encounter'],
42
48
  received_scopes: token_response_body['scope'],
43
- intent: token_response_body['intent']
49
+ intent: token_response_body['intent'],
50
+ smart_auth_info: smart_auth_info
44
51
 
45
52
  validate_required_fields_present(token_response_body, ['access_token', 'token_type', 'expires_in', 'scope'])
46
53
  validate_token_field_types(token_response_body)
47
54
  validate_token_type(token_response_body)
48
- check_for_missing_scopes(requested_scopes, token_response_body) unless config.options[:ignore_missing_scopes_check]
55
+ unless config.options[:ignore_missing_scopes_check]
56
+ check_for_missing_scopes(smart_auth_info.requested_scopes,
57
+ token_response_body)
58
+ end
49
59
 
50
60
  assert access_token.present?, 'Token response did not contain an access token'
51
61
  assert token_response_body['token_type']&.casecmp('Bearer')&.zero?,
@@ -14,7 +14,7 @@ module SMARTAppLaunch
14
14
  )
15
15
  id :smart_token_response_body_stu2_2
16
16
 
17
- input :requested_scopes
17
+ input :smart_auth_info, type: :auth_info, options: { mode: 'auth' }
18
18
  output :id_token,
19
19
  :refresh_token,
20
20
  :access_token,
@@ -22,7 +22,8 @@ module SMARTAppLaunch
22
22
  :patient_id,
23
23
  :encounter_id,
24
24
  :received_scopes,
25
- :intent
25
+ :intent,
26
+ :smart_auth_info
26
27
  uses_request :token
27
28
 
28
29
  def validate_fhir_context(fhir_context)
@@ -1,4 +1,4 @@
1
1
  module SMARTAppLaunch
2
- VERSION = '0.5.1'.freeze
3
- LAST_UPDATED = '2025-03-07'.freeze
2
+ VERSION = '0.6.0'.freeze
3
+ LAST_UPDATED = '2025-03-14'.freeze
4
4
  end
@@ -13,6 +13,8 @@ module SMARTAppLaunch
13
13
  input :url,
14
14
  title: 'FHIR Endpoint',
15
15
  description: 'URL of the FHIR endpoint used by SMART applications'
16
+ input :smart_auth_info,
17
+ type: :auth_info
16
18
 
17
19
  output :well_known_configuration,
18
20
  :well_known_authorization_url,
@@ -20,7 +22,8 @@ module SMARTAppLaunch
20
22
  :well_known_management_url,
21
23
  :well_known_registration_url,
22
24
  :well_known_revocation_url,
23
- :well_known_token_url
25
+ :well_known_token_url,
26
+ :smart_auth_info
24
27
  makes_request :smart_well_known_configuration
25
28
 
26
29
  run do
@@ -46,6 +49,13 @@ module SMARTAppLaunch
46
49
  well_known_revocation_url: make_url_absolute(base_url, config['revocation_endpoint']),
47
50
  well_known_token_url: make_url_absolute(base_url, config['token_endpoint'])
48
51
 
52
+ if smart_auth_info.use_discovery
53
+ smart_auth_info.auth_url = well_known_authorization_url
54
+ smart_auth_info.token_url = well_known_token_url
55
+
56
+ output smart_auth_info: smart_auth_info
57
+ end
58
+
49
59
  content_type = request.response_header('Content-Type')&.value
50
60
 
51
61
  assert content_type.present?, 'No `Content-Type` header received.'
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.5.1
4
+ version: 0.6.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: 2025-03-07 00:00:00.000000000 Z
11
+ date: 2025-03-14 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.6.2
19
+ version: 0.6.3
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.6.2
26
+ version: 0.6.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: json-jwt
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -203,6 +203,7 @@ files:
203
203
  - lib/smart_app_launch/smart_stu1_suite.rb
204
204
  - lib/smart_app_launch/smart_stu2_2_suite.rb
205
205
  - lib/smart_app_launch/smart_stu2_suite.rb
206
+ - lib/smart_app_launch/smart_tls_test.rb
206
207
  - lib/smart_app_launch/standalone_launch_group.rb
207
208
  - lib/smart_app_launch/standalone_launch_group_stu2.rb
208
209
  - lib/smart_app_launch/standalone_launch_group_stu2_2.rb