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.
- checksums.yaml +4 -4
- data/config/presets/inferno_reference_server_preset.json +15 -86
- data/config/presets/inferno_reference_server_stu2_2_preset.json +20 -69
- data/config/presets/inferno_reference_server_stu2_preset.json +20 -69
- data/lib/smart_app_launch/app_redirect_test.rb +12 -44
- data/lib/smart_app_launch/app_redirect_test_stu2.rb +2 -17
- data/lib/smart_app_launch/backend_services_authorization_group.rb +33 -57
- data/lib/smart_app_launch/backend_services_authorization_request_builder.rb +22 -9
- data/lib/smart_app_launch/backend_services_authorization_request_success_test.rb +26 -21
- data/lib/smart_app_launch/backend_services_authorization_response_body_test.rb +19 -5
- data/lib/smart_app_launch/backend_services_invalid_client_assertion_test.rb +30 -25
- data/lib/smart_app_launch/backend_services_invalid_grant_type_test.rb +30 -24
- data/lib/smart_app_launch/backend_services_invalid_jwt_test.rb +31 -26
- data/lib/smart_app_launch/client_assertion_builder.rb +27 -12
- data/lib/smart_app_launch/cors_openid_fhir_user_claim_test.rb +2 -2
- data/lib/smart_app_launch/cors_token_exchange_test.rb +2 -2
- data/lib/smart_app_launch/discovery_stu1_group.rb +6 -2
- data/lib/smart_app_launch/ehr_launch_group.rb +41 -24
- data/lib/smart_app_launch/ehr_launch_group_stu2.rb +26 -10
- data/lib/smart_app_launch/ehr_launch_group_stu2_2.rb +0 -16
- data/lib/smart_app_launch/openid_fhir_user_claim_test.rb +5 -4
- data/lib/smart_app_launch/openid_token_payload_test.rb +6 -8
- data/lib/smart_app_launch/smart_stu1_suite.rb +32 -24
- data/lib/smart_app_launch/smart_stu2_2_suite.rb +56 -30
- data/lib/smart_app_launch/smart_stu2_suite.rb +56 -31
- data/lib/smart_app_launch/smart_tls_test.rb +14 -0
- data/lib/smart_app_launch/standalone_launch_group.rb +42 -25
- data/lib/smart_app_launch/standalone_launch_group_stu2.rb +26 -10
- data/lib/smart_app_launch/standalone_launch_group_stu2_2.rb +0 -16
- data/lib/smart_app_launch/token_exchange_stu2_2_test.rb +5 -17
- data/lib/smart_app_launch/token_exchange_stu2_test.rb +8 -67
- data/lib/smart_app_launch/token_exchange_test.rb +18 -38
- data/lib/smart_app_launch/token_introspection_access_token_group.rb +12 -4
- data/lib/smart_app_launch/token_introspection_access_token_group_stu2_2.rb +9 -1
- data/lib/smart_app_launch/token_introspection_group.rb +2 -4
- data/lib/smart_app_launch/token_introspection_request_group.rb +2 -4
- data/lib/smart_app_launch/token_introspection_response_group.rb +64 -49
- data/lib/smart_app_launch/token_refresh_body_test.rb +9 -2
- data/lib/smart_app_launch/token_refresh_stu2_test.rb +10 -17
- data/lib/smart_app_launch/token_refresh_test.rb +19 -20
- data/lib/smart_app_launch/token_response_body_test.rb +14 -4
- data/lib/smart_app_launch/token_response_body_test_stu2_2.rb +3 -2
- data/lib/smart_app_launch/version.rb +2 -2
- data/lib/smart_app_launch/well_known_endpoint_test.rb +11 -1
- 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
|
-
|
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 :
|
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
|
89
|
-
|
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
|
-
|
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,
|
107
|
-
|
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
|
116
|
-
|
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),
|
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
|
125
|
-
current_time = Time.now.to_i
|
126
|
-
assert exp.to_i >= current_time - 600,
|
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
|
-
|
130
|
-
|
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,
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
146
|
-
assert id_token_sub
|
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
|
-
|
156
|
-
|
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?,
|
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,
|
187
|
-
|
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
|
-
|
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:
|
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
|
-
|
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
|
-
|
32
|
-
|
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:
|
40
|
-
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 :
|
20
|
-
input :
|
21
|
-
|
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
|
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(
|
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
|
-
|
56
|
-
|
56
|
+
smart_auth_info.issue_time = Time.now
|
57
57
|
token_response_body = JSON.parse(request.response_body)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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 :
|
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
|
-
|
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 :
|
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)
|
@@ -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.
|
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-
|
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.
|
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.
|
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
|