smart_app_launch_test_kit 0.0.1
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 +7 -0
- data/LICENSE +201 -0
- data/lib/smart_app_launch/app_launch_test.rb +25 -0
- data/lib/smart_app_launch/app_redirect_test.rb +61 -0
- data/lib/smart_app_launch/code_received_test.rb +31 -0
- data/lib/smart_app_launch/discovery_group.rb +229 -0
- data/lib/smart_app_launch/ehr_launch_group.rb +117 -0
- data/lib/smart_app_launch/launch_received_test.rb +19 -0
- data/lib/smart_app_launch/openid_connect_group.rb +83 -0
- data/lib/smart_app_launch/openid_decode_id_token_test.rb +30 -0
- data/lib/smart_app_launch/openid_fhir_user_claim_test.rb +30 -0
- data/lib/smart_app_launch/openid_required_configuration_fields_test.rb +50 -0
- data/lib/smart_app_launch/openid_retrieve_configuration_test.rb +31 -0
- data/lib/smart_app_launch/openid_retrieve_jwks_test.rb +43 -0
- data/lib/smart_app_launch/openid_token_header_test.rb +39 -0
- data/lib/smart_app_launch/openid_token_payload_test.rb +64 -0
- data/lib/smart_app_launch/standalone_launch_group.rb +104 -0
- data/lib/smart_app_launch/token_exchange_test.rb +47 -0
- data/lib/smart_app_launch/token_payload_validation.rb +47 -0
- data/lib/smart_app_launch/token_refresh_body_test.rb +52 -0
- data/lib/smart_app_launch/token_refresh_group.rb +45 -0
- data/lib/smart_app_launch/token_refresh_test.rb +52 -0
- data/lib/smart_app_launch/token_response_body_test.rb +53 -0
- data/lib/smart_app_launch/token_response_headers_test.rb +27 -0
- data/lib/smart_app_launch_test_kit.rb +141 -0
- metadata +168 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative 'token_refresh_test'
|
2
|
+
require_relative 'token_refresh_body_test'
|
3
|
+
require_relative 'token_response_headers_test'
|
4
|
+
|
5
|
+
module SMARTAppLaunch
|
6
|
+
class TokenRefreshGroup < Inferno::TestGroup
|
7
|
+
id :smart_token_refresh
|
8
|
+
title 'SMART Token Refresh'
|
9
|
+
description %(
|
10
|
+
# Background
|
11
|
+
|
12
|
+
The #{title} Sequence tests the ability of the system to successfuly
|
13
|
+
exchange a refresh token for an access token. Refresh tokens are typically
|
14
|
+
longer lived than access tokens and allow client applications to obtain a
|
15
|
+
new access token Refresh tokens themselves cannot provide access to
|
16
|
+
resources on the server.
|
17
|
+
|
18
|
+
Token refreshes are accomplished through a `POST` request to the token
|
19
|
+
exchange endpoint as described in the [SMART App Launch
|
20
|
+
Framework](http://www.hl7.org/fhir/smart-app-launch/#step-5-later-app-uses-a-refresh-token-to-obtain-a-new-access-token).
|
21
|
+
|
22
|
+
# Test Methodology
|
23
|
+
|
24
|
+
This test attempts to exchange the refresh token for a new access token
|
25
|
+
and verify that the information returned contains the required fields and
|
26
|
+
uses the proper headers.
|
27
|
+
|
28
|
+
For more information see:
|
29
|
+
|
30
|
+
* [The OAuth 2.0 Authorization
|
31
|
+
Framework](https://tools.ietf.org/html/rfc6749)
|
32
|
+
* [Using a refresh token to obtain a new access
|
33
|
+
token](http://hl7.org/fhir/smart-app-launch/#step-5-later-app-uses-a-refresh-token-to-obtain-a-new-access-token)
|
34
|
+
)
|
35
|
+
|
36
|
+
test from: :smart_token_refresh
|
37
|
+
test from: :smart_token_refresh_body
|
38
|
+
test from: :smart_token_response_headers,
|
39
|
+
config: {
|
40
|
+
requests: {
|
41
|
+
token: { name: :token_refresh }
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'token_payload_validation'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class TokenRefreshTest < Inferno::Test
|
5
|
+
include TokenPayloadValidation
|
6
|
+
|
7
|
+
id :smart_token_refresh
|
8
|
+
title 'Server successfully refreshes the access token when optional scope parameter omitted'
|
9
|
+
description %(
|
10
|
+
Server successfully exchanges refresh token at OAuth token endpoint
|
11
|
+
without providing scope in the body of the request.
|
12
|
+
|
13
|
+
The EHR authorization server SHALL return a JSON structure that includes
|
14
|
+
an access token or a message indicating that the authorization request
|
15
|
+
has been denied. `access_token`, `expires_in`, `token_type`, and `scope` are
|
16
|
+
required. `access_token` must be `Bearer`.
|
17
|
+
|
18
|
+
Although not required in the token refresh portion of the SMART App
|
19
|
+
Launch Guide, the token refresh response should include the HTTP
|
20
|
+
Cache-Control response header field with a value of no-store, as well as
|
21
|
+
the Pragma response header field with a value of no-cache to be
|
22
|
+
consistent with the requirements of the inital access token exchange.
|
23
|
+
)
|
24
|
+
input :well_known_token_url, :refresh_token, :client_id, :received_scopes
|
25
|
+
input :client_secret, optional: true
|
26
|
+
makes_request :token_refresh
|
27
|
+
|
28
|
+
run do
|
29
|
+
skip_if refresh_token.blank?
|
30
|
+
|
31
|
+
oauth2_params = {
|
32
|
+
'grant_type' => 'refresh_token',
|
33
|
+
'refresh_token' => refresh_token
|
34
|
+
}
|
35
|
+
oauth2_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
36
|
+
|
37
|
+
oauth2_params['scope'] = received_scopes if config.options[:include_scopes]
|
38
|
+
|
39
|
+
if client_secret.present?
|
40
|
+
credentials = Base64.strict_encode64("#{client_id}:#{client_secret}")
|
41
|
+
oauth2_headers['Authorization'] = "Basic #{credentials}"
|
42
|
+
else
|
43
|
+
oauth2_params['client_id'] = client_id
|
44
|
+
end
|
45
|
+
|
46
|
+
post(well_known_token_url, body: oauth2_params, name: :token_refresh, headers: oauth2_headers)
|
47
|
+
|
48
|
+
assert_response_status(200)
|
49
|
+
assert_valid_json(response[:body])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'token_payload_validation'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class TokenResponseBodyTest < Inferno::Test
|
5
|
+
include TokenPayloadValidation
|
6
|
+
|
7
|
+
title 'Token exchange response body contains required information encoded in JSON'
|
8
|
+
description %(
|
9
|
+
The EHR authorization server shall return a JSON structure that includes
|
10
|
+
an access token or a message indicating that the authorization request
|
11
|
+
has been denied. `access_token`, `token_type`, and `scope` are required.
|
12
|
+
`token_type` must be Bearer. `expires_in` is required for token
|
13
|
+
refreshes.
|
14
|
+
)
|
15
|
+
id :smart_token_response_body
|
16
|
+
|
17
|
+
input :requested_scopes
|
18
|
+
output :id_token,
|
19
|
+
:refresh_token,
|
20
|
+
:access_token,
|
21
|
+
:expires_in,
|
22
|
+
:patient_id,
|
23
|
+
:encounter_id,
|
24
|
+
:received_scopes,
|
25
|
+
:intent
|
26
|
+
uses_request :token
|
27
|
+
|
28
|
+
run do
|
29
|
+
skip_if request.status != 200, 'Token exchange was unsuccessful'
|
30
|
+
|
31
|
+
assert_valid_json(request.response_body)
|
32
|
+
token_response_body = JSON.parse(request.response_body)
|
33
|
+
|
34
|
+
output id_token: token_response_body['id_token'],
|
35
|
+
refresh_token: token_response_body['refresh_token'],
|
36
|
+
access_token: token_response_body['access_token'],
|
37
|
+
expires_in: token_response_body['expires_in'],
|
38
|
+
patient_id: token_response_body['patient'],
|
39
|
+
encounter_id: token_response_body['encounter'],
|
40
|
+
received_scopes: token_response_body['scope'],
|
41
|
+
intent: token_response_body['intent']
|
42
|
+
|
43
|
+
validate_required_fields_present(token_response_body, ['access_token', 'token_type', 'expires_in', 'scope'])
|
44
|
+
validate_token_field_types(token_response_body)
|
45
|
+
validate_token_type(token_response_body)
|
46
|
+
check_for_missing_scopes(requested_scopes, token_response_body)
|
47
|
+
|
48
|
+
assert access_token.present?, 'Token response did not contain an access token'
|
49
|
+
assert token_response_body['token_type']&.casecmp('Bearer')&.zero?,
|
50
|
+
'`token_type` field must have a value of `Bearer`'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class TokenResponseHeadersTest < Inferno::Test
|
3
|
+
title 'Response includes correct HTTP Cache-Control and Pragma headers'
|
4
|
+
description %(
|
5
|
+
The authorization servers response must include the HTTP Cache-Control
|
6
|
+
response header field with a value of no-store, as well as the Pragma
|
7
|
+
response header field with a value of no-cache.
|
8
|
+
)
|
9
|
+
id :smart_token_response_headers
|
10
|
+
|
11
|
+
uses_request :token
|
12
|
+
|
13
|
+
run do
|
14
|
+
skip_if request.status != 200, 'Token exchange was unsuccessful'
|
15
|
+
|
16
|
+
cc_header = request.response_header('Cache-Control')&.value
|
17
|
+
|
18
|
+
assert cc_header&.downcase&.include?('no-store'),
|
19
|
+
'Token response must have `Cache-Control` header containing `no-store`.'
|
20
|
+
|
21
|
+
pragma_header = request.response_header('Pragma')&.value
|
22
|
+
|
23
|
+
assert pragma_header&.downcase&.include?('no-cache'),
|
24
|
+
'Token response must have `Pragma` header containing `no-cache`.'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require_relative 'smart_app_launch/discovery_group'
|
2
|
+
require_relative 'smart_app_launch/standalone_launch_group'
|
3
|
+
require_relative 'smart_app_launch/ehr_launch_group'
|
4
|
+
require_relative 'smart_app_launch/openid_connect_group'
|
5
|
+
require_relative 'smart_app_launch/token_refresh_group'
|
6
|
+
|
7
|
+
module SMARTAppLaunch
|
8
|
+
class SMARTSuite < Inferno::TestSuite
|
9
|
+
id 'smart'
|
10
|
+
title 'SMART'
|
11
|
+
|
12
|
+
resume_test_route :get, '/launch' do
|
13
|
+
request.query_parameters['iss']
|
14
|
+
end
|
15
|
+
|
16
|
+
resume_test_route :get, '/redirect' do
|
17
|
+
request.query_parameters['state']
|
18
|
+
end
|
19
|
+
|
20
|
+
config options: {
|
21
|
+
redirect_uri: "#{Inferno::Application['inferno_host']}/custom/smart/redirect",
|
22
|
+
launch_uri: "#{Inferno::Application['inferno_host']}/custom/smart/launch"
|
23
|
+
}
|
24
|
+
|
25
|
+
group do
|
26
|
+
title 'Standalone Launch'
|
27
|
+
|
28
|
+
run_as_group
|
29
|
+
|
30
|
+
group from: :smart_discovery
|
31
|
+
|
32
|
+
group from: :smart_standalone_launch
|
33
|
+
|
34
|
+
group from: :smart_openid_connect,
|
35
|
+
config: {
|
36
|
+
inputs: {
|
37
|
+
id_token: { name: :standalone_id_token },
|
38
|
+
client_id: { name: :standalone_client_id },
|
39
|
+
requested_scopes: { name: :standalone_requested_scopes }
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
group from: :smart_token_refresh,
|
44
|
+
id: :smart_standalone_refresh_without_scopes,
|
45
|
+
title: 'SMART Token Refresh Without Scopes',
|
46
|
+
config: {
|
47
|
+
inputs: {
|
48
|
+
refresh_token: { name: :standalone_refresh_token },
|
49
|
+
client_id: { name: :standalone_client_id },
|
50
|
+
client_secret: { name: :standalone_client_secret },
|
51
|
+
received_scopes: { name: :standalone_received_scopes }
|
52
|
+
},
|
53
|
+
outputs: {
|
54
|
+
refresh_token: { name: :standalone_refresh_token },
|
55
|
+
received_scopes: { name: :standalone_received_scopes },
|
56
|
+
access_token: { name: :standalone_access_token },
|
57
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
58
|
+
expires_in: { name: :standalone_expires_in }
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
group from: :smart_token_refresh,
|
63
|
+
id: :smart_standalone_refresh_with_scopes,
|
64
|
+
title: 'SMART Token Refresh With Scopes',
|
65
|
+
config: {
|
66
|
+
options: { include_scopes: true },
|
67
|
+
inputs: {
|
68
|
+
refresh_token: { name: :standalone_refresh_token },
|
69
|
+
client_id: { name: :standalone_client_id },
|
70
|
+
client_secret: { name: :standalone_client_secret },
|
71
|
+
received_scopes: { name: :standalone_received_scopes }
|
72
|
+
},
|
73
|
+
outputs: {
|
74
|
+
refresh_token: { name: :standalone_refresh_token },
|
75
|
+
received_scopes: { name: :standalone_received_scopes },
|
76
|
+
access_token: { name: :standalone_access_token },
|
77
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
78
|
+
expires_in: { name: :standalone_expires_in }
|
79
|
+
}
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
group do
|
84
|
+
title 'EHR Launch'
|
85
|
+
|
86
|
+
run_as_group
|
87
|
+
|
88
|
+
group from: :smart_discovery
|
89
|
+
|
90
|
+
group from: :smart_ehr_launch
|
91
|
+
|
92
|
+
group from: :smart_openid_connect,
|
93
|
+
config: {
|
94
|
+
inputs: {
|
95
|
+
id_token: { name: :ehr_id_token },
|
96
|
+
client_id: { name: :ehr_client_id },
|
97
|
+
requested_scopes: { name: :standalone_requested_scopes }
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
group from: :smart_token_refresh,
|
102
|
+
id: :smart_ehr_refresh_without_scopes,
|
103
|
+
title: 'SMART Token Refresh Without Scopes',
|
104
|
+
config: {
|
105
|
+
inputs: {
|
106
|
+
refresh_token: { name: :ehr_refresh_token },
|
107
|
+
client_id: { name: :ehr_client_id },
|
108
|
+
client_secret: { name: :ehr_client_secret },
|
109
|
+
received_scopes: { name: :ehr_received_scopes }
|
110
|
+
},
|
111
|
+
outputs: {
|
112
|
+
refresh_token: { name: :ehr_refresh_token },
|
113
|
+
received_scopes: { name: :ehr_received_scopes },
|
114
|
+
access_token: { name: :ehr_access_token },
|
115
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
116
|
+
expires_in: { name: :ehr_expires_in }
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
group from: :smart_token_refresh,
|
121
|
+
id: :smart_ehr_refresh_with_scopes,
|
122
|
+
title: 'SMART Token Refresh With Scopes',
|
123
|
+
config: {
|
124
|
+
options: { include_scopes: true },
|
125
|
+
inputs: {
|
126
|
+
refresh_token: { name: :ehr_refresh_token },
|
127
|
+
client_id: { name: :ehr_client_id },
|
128
|
+
client_secret: { name: :ehr_client_secret },
|
129
|
+
received_scopes: { name: :ehr_received_scopes }
|
130
|
+
},
|
131
|
+
outputs: {
|
132
|
+
refresh_token: { name: :ehr_refresh_token },
|
133
|
+
received_scopes: { name: :ehr_received_scopes },
|
134
|
+
access_token: { name: :ehr_access_token },
|
135
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
136
|
+
expires_in: { name: :ehr_expires_in }
|
137
|
+
}
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smart_app_launch_test_kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen MacVicar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: inferno_core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.6
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.2.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.2.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: database_cleaner-sequel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: factory_bot
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '6.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '6.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.1.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.1.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: webmock
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.11'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.11'
|
111
|
+
description: Inferno Tests for the SMART Application Launch Framework Implementation
|
112
|
+
Guide
|
113
|
+
email:
|
114
|
+
- inferno@groups.mitre.org
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- LICENSE
|
120
|
+
- lib/smart_app_launch/app_launch_test.rb
|
121
|
+
- lib/smart_app_launch/app_redirect_test.rb
|
122
|
+
- lib/smart_app_launch/code_received_test.rb
|
123
|
+
- lib/smart_app_launch/discovery_group.rb
|
124
|
+
- lib/smart_app_launch/ehr_launch_group.rb
|
125
|
+
- lib/smart_app_launch/launch_received_test.rb
|
126
|
+
- lib/smart_app_launch/openid_connect_group.rb
|
127
|
+
- lib/smart_app_launch/openid_decode_id_token_test.rb
|
128
|
+
- lib/smart_app_launch/openid_fhir_user_claim_test.rb
|
129
|
+
- lib/smart_app_launch/openid_required_configuration_fields_test.rb
|
130
|
+
- lib/smart_app_launch/openid_retrieve_configuration_test.rb
|
131
|
+
- lib/smart_app_launch/openid_retrieve_jwks_test.rb
|
132
|
+
- lib/smart_app_launch/openid_token_header_test.rb
|
133
|
+
- lib/smart_app_launch/openid_token_payload_test.rb
|
134
|
+
- lib/smart_app_launch/standalone_launch_group.rb
|
135
|
+
- lib/smart_app_launch/token_exchange_test.rb
|
136
|
+
- lib/smart_app_launch/token_payload_validation.rb
|
137
|
+
- lib/smart_app_launch/token_refresh_body_test.rb
|
138
|
+
- lib/smart_app_launch/token_refresh_group.rb
|
139
|
+
- lib/smart_app_launch/token_refresh_test.rb
|
140
|
+
- lib/smart_app_launch/token_response_body_test.rb
|
141
|
+
- lib/smart_app_launch/token_response_headers_test.rb
|
142
|
+
- lib/smart_app_launch_test_kit.rb
|
143
|
+
homepage: https://github.com/inferno_community/smart-app-launch-test-kit
|
144
|
+
licenses:
|
145
|
+
- Apache-2.0
|
146
|
+
metadata:
|
147
|
+
homepage_uri: https://github.com/inferno_community/smart-app-launch-test-kit
|
148
|
+
source_code_uri: https://github.com/inferno_community/smart-app-launch-test-kit
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
require_paths:
|
152
|
+
- lib
|
153
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 2.7.0
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
requirements: []
|
164
|
+
rubygems_version: 3.1.6
|
165
|
+
signing_key:
|
166
|
+
specification_version: 4
|
167
|
+
summary: Inferno Tests for the SMART Application Launch Framework Implementation Guide
|
168
|
+
test_files: []
|