smart_app_launch_test_kit 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae741dbca6b676c7c53c6be41a14f84292b4d620dbfecbf67f7472f5450c67fc
4
- data.tar.gz: 4136c596fa810d8109c18df863e39eda7283c170a59c7441dc07e1bcb4ebdc1f
3
+ metadata.gz: 7da125d1c3d1b6665088989e563aa9a046fc9e651ab3b1616e153cee1d695a6e
4
+ data.tar.gz: 329bf68903d2b72988bfbdd1c0525ec1a933c5450b285fc001c4d28d44552f2f
5
5
  SHA512:
6
- metadata.gz: dfa9bd45de2c2b917347724b9a706dde71d1287dc48a3e49a86371b1106c42257084f78f989dcb737fba425594fbc11f4562b3aab7bf37710a40cdff3fccb116
7
- data.tar.gz: ce2e0ed22fd192d88c5de32f334ada292ee5813b72c184eb9161f122d2cec82155ff6e83de19f8fe2c2d7a2ea0cd52b5c4adcdcefef0a668ff079abb7107776d
6
+ metadata.gz: '081f7d57660ef9446376fd1fd07753b00014258f9162b3ffc14f57165caa4ca255d3dc970483b30236a7fd9bbfec49c0bda9becb6c3a20f6805e679b6fc24814'
7
+ data.tar.gz: ee1f2ea1d9b75980516d2098528b229eabbacea1fc305c1242aacff60e1fbd7bf06a2d7b98788f313c000492264e9f5ef79b120ebeb0bd7e7e124e3147a48a47
@@ -10,7 +10,7 @@ module SMARTAppLaunch
10
10
  input :url
11
11
  receives_request :launch
12
12
 
13
- config options: { launch_uri: "#{Inferno::Application['inferno_host']}/custom/smart/launch" }
13
+ config options: { launch_uri: "#{Inferno::Application['base_url']}/custom/smart/launch" }
14
14
 
15
15
  run do
16
16
  wait(
@@ -8,13 +8,63 @@ module SMARTAppLaunch
8
8
  id :smart_app_redirect
9
9
 
10
10
  input :client_id, :requested_scopes, :url, :smart_authorization_url
11
+ input :use_pkce,
12
+ title: 'Proof Key for Code Exchange (PKCE)',
13
+ type: 'radio',
14
+ default: 'false',
15
+ options: {
16
+ list_options: [
17
+ {
18
+ label: 'Enabled',
19
+ value: 'true'
20
+ },
21
+ {
22
+ label: 'Disabled',
23
+ value: 'false'
24
+ }
25
+ ]
26
+ }
27
+ input :pkce_code_challenge_method,
28
+ optional: true,
29
+ title: 'PKCE Code Challenge Method',
30
+ type: 'radio',
31
+ default: 'S256',
32
+ options: {
33
+ list_options: [
34
+ {
35
+ label: 'S256',
36
+ value: 'S256'
37
+ },
38
+ {
39
+ label: 'plain',
40
+ value: 'plain'
41
+ }
42
+ ]
43
+ }
11
44
 
12
- output :state
45
+ output :state, :pkce_code_challenge, :pkce_code_verifier
13
46
  receives_request :redirect
14
47
 
15
- config options: { redirect_uri: "#{Inferno::Application['inferno_host']}/custom/smart/redirect" }
48
+ config options: { redirect_uri: "#{Inferno::Application['base_url']}/custom/smart/redirect" }
49
+
50
+ def self.calculate_s256_challenge(verifier)
51
+ Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), padding: false)
52
+ end
53
+
54
+ def aud
55
+ url
56
+ end
57
+
58
+ def wait_message(auth_url)
59
+ %(
60
+ [Follow this link to authorize with the SMART server](#{auth_url}).
61
+ Waiting to receive a request at `#{config.options[:redirect_uri]}` with
62
+ a state of `#{state}`.
63
+ )
64
+ end
16
65
 
17
66
  run do
67
+ info(config.options[:redirect_uri])
18
68
  assert_valid_http_uri(
19
69
  smart_authorization_url,
20
70
  "OAuth2 Authorization Endpoint '#{smart_authorization_url}' is not a valid URI"
@@ -28,11 +78,25 @@ module SMARTAppLaunch
28
78
  'redirect_uri' => config.options[:redirect_uri],
29
79
  'scope' => requested_scopes,
30
80
  'state' => state,
31
- 'aud' => url
81
+ 'aud' => aud
32
82
  }
33
83
 
34
84
  oauth2_params['launch'] = launch if self.class.inputs.include?(:launch)
35
85
 
86
+ if use_pkce == 'true'
87
+ code_verifier = SecureRandom.uuid
88
+ code_challenge =
89
+ if pkce_code_challenge_method == 'S256'
90
+ self.class.calculate_s256_challenge(code_verifier)
91
+ else
92
+ code_verifier
93
+ end
94
+
95
+ output pkce_code_verifier: code_verifier, pkce_code_challenge: code_challenge
96
+
97
+ oauth2_params.merge!('code_challenge' => code_challenge, 'code_challenge_method' => pkce_code_challenge_method)
98
+ end
99
+
36
100
  authorization_url = smart_authorization_url
37
101
 
38
102
  authorization_url +=
@@ -50,11 +114,7 @@ module SMARTAppLaunch
50
114
 
51
115
  wait(
52
116
  identifier: state,
53
- message: %(
54
- [Follow this link to authorize with the SMART
55
- server](#{authorization_url}). Waiting to receive a request at
56
- `#{config.options[:redirect_uri]}` with a state of `#{state}`.
57
- )
117
+ message: wait_message(authorization_url)
58
118
  )
59
119
  end
60
120
  end
@@ -2,6 +2,7 @@ module SMARTAppLaunch
2
2
  class DiscoveryGroup < Inferno::TestGroup
3
3
  id :smart_discovery
4
4
  title 'SMART on FHIR Discovery'
5
+ short_description 'Retrieve server\'s SMART on FHIR configuration.'
5
6
  description %(
6
7
  # Background
7
8
 
@@ -43,8 +44,7 @@ module SMARTAppLaunch
43
44
  )
44
45
  input :url,
45
46
  title: 'FHIR Endpoint',
46
- description: 'URL of the FHIR endpoint used by SMART applications',
47
- default: 'https://inferno.healthit.gov/reference-server/r4'
47
+ description: 'URL of the FHIR endpoint used by SMART applications'
48
48
  output :well_known_configuration,
49
49
  :well_known_authorization_url,
50
50
  :well_known_introspection_url,
@@ -10,6 +10,7 @@ module SMARTAppLaunch
10
10
  class EHRLaunchGroup < Inferno::TestGroup
11
11
  id :smart_ehr_launch
12
12
  title 'SMART EHR Launch'
13
+ short_description 'Demonstrate the ability to authorize an app using the EHR Launch.'
13
14
 
14
15
  description %(
15
16
  # Background
@@ -42,8 +43,7 @@ module SMARTAppLaunch
42
43
  client_id: {
43
44
  name: :ehr_client_id,
44
45
  title: 'EHR Launch Client ID',
45
- description: 'Client ID provided during registration of Inferno as an EHR launch application',
46
- default: 'SAMPLE_PUBLIC_CLIENT_ID'
46
+ description: 'Client ID provided during registration of Inferno as an EHR launch application'
47
47
  },
48
48
  client_secret: {
49
49
  name: :ehr_client_secret,
@@ -55,23 +55,11 @@ module SMARTAppLaunch
55
55
  title: 'EHR Launch Scope',
56
56
  description: 'OAuth 2.0 scope provided by system to enable all required functionality',
57
57
  type: 'textarea',
58
- default: %(
59
- launch openid fhirUser offline_access
60
- patient/Medication.read patient/AllergyIntolerance.read
61
- patient/CarePlan.read patient/CareTeam.read patient/Condition.read
62
- patient/Device.read patient/DiagnosticReport.read
63
- patient/DocumentReference.read patient/Encounter.read
64
- patient/Goal.read patient/Immunization.read patient/Location.read
65
- patient/MedicationRequest.read patient/Observation.read
66
- patient/Organization.read patient/Patient.read
67
- patient/Practitioner.read patient/Procedure.read
68
- patient/Provenance.read patient/PractitionerRole.read
69
- ).gsub(/\s{2,}/, ' ').strip
58
+ default: 'launch openid fhirUser offline_access user/*.read'
70
59
  },
71
60
  url: {
72
61
  title: 'EHR Launch FHIR Endpoint',
73
- description: 'URL of the FHIR endpoint used by EHR launched applications',
74
- default: 'https://inferno.healthit.gov/reference-server/r4'
62
+ description: 'URL of the FHIR endpoint used by EHR launched applications'
75
63
  },
76
64
  code: {
77
65
  name: :ehr_code
@@ -81,6 +69,9 @@ module SMARTAppLaunch
81
69
  },
82
70
  launch: {
83
71
  name: :ehr_launch
72
+ },
73
+ smart_credentials: {
74
+ name: :ehr_smart_credentials
84
75
  }
85
76
  },
86
77
  outputs: {
@@ -95,7 +86,8 @@ module SMARTAppLaunch
95
86
  patient_id: { name: :ehr_patient_id },
96
87
  encounter_id: { name: :ehr_encounter_id },
97
88
  received_scopes: { name: :ehr_received_scopes },
98
- intent: { name: :ehr_intent }
89
+ intent: { name: :ehr_intent },
90
+ smart_credentials: { name: :ehr_smart_credentials }
99
91
  },
100
92
  requests: {
101
93
  launch: { name: :ehr_launch },
@@ -106,10 +98,34 @@ module SMARTAppLaunch
106
98
 
107
99
  test from: :smart_app_launch
108
100
  test from: :smart_launch_received
101
+ test from: :tls_version_test,
102
+ id: :ehr_auth_tls,
103
+ title: 'OAuth 2.0 authorize endpoint secured by transport layer security',
104
+ description: %(
105
+ Apps MUST assure that sensitive information (authentication secrets,
106
+ authorization codes, tokens) is transmitted ONLY to authenticated
107
+ servers, over TLS-secured channels.
108
+ ),
109
+ config: {
110
+ inputs: { url: { name: :smart_authorization_url } },
111
+ options: { minimum_allowed_version: OpenSSL::SSL::TLS1_2_VERSION }
112
+ }
109
113
  test from: :smart_app_redirect do
110
114
  input :launch
111
115
  end
112
116
  test from: :smart_code_received
117
+ test from: :tls_version_test,
118
+ id: :ehr_token_tls,
119
+ title: 'OAuth 2.0 token endpoint secured by transport layer security',
120
+ description: %(
121
+ Apps MUST assure that sensitive information (authentication secrets,
122
+ authorization codes, tokens) is transmitted ONLY to authenticated
123
+ servers, over TLS-secured channels.
124
+ ),
125
+ config: {
126
+ inputs: { url: { name: :smart_token_url } },
127
+ options: { minimum_allowed_version: OpenSSL::SSL::TLS1_2_VERSION }
128
+ }
113
129
  test from: :smart_token_exchange
114
130
  test from: :smart_token_response_body
115
131
  test from: :smart_token_response_headers
@@ -11,6 +11,7 @@ module SMARTAppLaunch
11
11
  class OpenIDConnectGroup < Inferno::TestGroup
12
12
  id :smart_openid_connect
13
13
  title 'OpenID Connect'
14
+ short_description 'Demonstrate the ability to authenticate users with OpenID Connect.'
14
15
 
15
16
  description %(
16
17
  # Background
@@ -54,30 +55,5 @@ module SMARTAppLaunch
54
55
  test from: :smart_openid_token_payload
55
56
 
56
57
  test from: :smart_openid_fhir_user_claim
57
-
58
- # test do
59
- # id :smart_openid_fhir_user_retrieval
60
- # title 'fhirUser can be retrieved'
61
- # description %(
62
- # Verify that the FHIR resource referred to in the `fhirUser` claim can be
63
- # retrieved.
64
- # )
65
-
66
- # input :id_token_fhir_user, :openid_issuer, :standalone_access_token
67
- # makes_request :id_token_fhir_user
68
-
69
- # run do
70
- # skip_if id_token_fhir_user.blank?
71
-
72
- # split_fhir_user = id_token_fhir_user.split('/')
73
- # resource_type = split_fhir_user[-2]
74
- # resource_id = split_fhir_user[-1]
75
- # fhir_read(resource_type, resource_id)
76
-
77
- # assert_response_status(200)
78
- # assert_valid_json(response[:body])
79
- # assert_resource_type(resource_type)
80
- # end
81
- # end
82
58
  end
83
59
  end
@@ -1,30 +1,47 @@
1
1
  module SMARTAppLaunch
2
2
  class OpenIDFHIRUserClaimTest < Inferno::Test
3
3
  id :smart_openid_fhir_user_claim
4
- title 'ID token contains a valid fhirUser claim'
4
+ title 'FHIR resource representing the current user can be retrieved'
5
5
  description %(
6
- Verify that the `fhirUser` claim is present in the ID token. The
7
- `fhirUser` claim must be the url for a Patient, Practitioner,
8
- RelatedPerson, or Person resource.
9
- )
6
+ Verify that the `fhirUser` claim is present in the ID token and that the
7
+ FHIR resource it refers to can be retrieved. The `fhirUser` claim must be
8
+ the url for a Patient, Practitioner, RelatedPerson, or Person resource
9
+ )
10
10
 
11
- input :id_token_payload_json, :requested_scopes
11
+ input :id_token_payload_json, :requested_scopes, :url
12
+ input :smart_credentials, type: :oauth_credentials
12
13
  output :id_token_fhir_user
13
14
 
15
+ fhir_client do
16
+ url :url
17
+ oauth_credentials :smart_credentials
18
+ end
19
+
14
20
  run do
15
21
  skip_if id_token_payload_json.blank?
16
22
  skip_if !requested_scopes&.include?('fhirUser'), '`fhirUser` scope not requested'
17
23
 
24
+ assert_valid_json(id_token_payload_json)
18
25
  payload = JSON.parse(id_token_payload_json)
19
26
  fhir_user = payload['fhirUser']
20
27
 
21
28
  valid_fhir_user_resource_types = ['Patient', 'Practitioner', 'RelatedPerson', 'Person']
22
29
 
23
30
  assert fhir_user.present?, 'ID token does not contain `fhirUser` claim'
24
- assert valid_fhir_user_resource_types.any? { |type| fhir_user.include? type },
31
+
32
+ fhir_user_segments = fhir_user.split('/')
33
+ fhir_user_resource_type = fhir_user_segments[-2]
34
+ fhir_user_id = fhir_user_segments.last
35
+
36
+ assert valid_fhir_user_resource_types.include?(fhir_user_resource_type),
25
37
  "ID token `fhirUser` claim does not refer to a valid resource type: #{fhir_user}"
26
38
 
27
39
  output id_token_fhir_user: fhir_user
40
+
41
+ fhir_read(fhir_user_resource_type, fhir_user_id)
42
+
43
+ assert_response_status(200)
44
+ assert_resource_type(fhir_user_resource_type)
28
45
  end
29
46
  end
30
47
  end
@@ -8,6 +8,7 @@ module SMARTAppLaunch
8
8
  class StandaloneLaunchGroup < Inferno::TestGroup
9
9
  id :smart_standalone_launch
10
10
  title 'SMART Standalone Launch'
11
+ short_description 'Demonstrate the ability to authorize an app using the Standalone Launch.'
11
12
 
12
13
  description %(
13
14
  # Background
@@ -38,8 +39,7 @@ module SMARTAppLaunch
38
39
  client_id: {
39
40
  name: :standalone_client_id,
40
41
  title: 'Standalone Client ID',
41
- description: 'Client ID provided during registration of Inferno as a standalone application',
42
- default: 'SAMPLE_PUBLIC_CLIENT_ID'
42
+ description: 'Client ID provided during registration of Inferno as a standalone application'
43
43
  },
44
44
  client_secret: {
45
45
  name: :standalone_client_secret,
@@ -51,30 +51,22 @@ module SMARTAppLaunch
51
51
  title: 'Standalone Scope',
52
52
  description: 'OAuth 2.0 scope provided by system to enable all required functionality',
53
53
  type: 'textarea',
54
- default: %(
55
- launch/patient openid fhirUser offline_access
56
- patient/Medication.read patient/AllergyIntolerance.read
57
- patient/CarePlan.read patient/CareTeam.read patient/Condition.read
58
- patient/Device.read patient/DiagnosticReport.read
59
- patient/DocumentReference.read patient/Encounter.read
60
- patient/Goal.read patient/Immunization.read patient/Location.read
61
- patient/MedicationRequest.read patient/Observation.read
62
- patient/Organization.read patient/Patient.read
63
- patient/Practitioner.read patient/Procedure.read
64
- patient/Provenance.read patient/PractitionerRole.read
65
- ).gsub(/\s{2,}/, ' ').strip
54
+ default: 'launch/patient openid fhirUser offline_access patient/*.read'
66
55
  },
67
56
  url: {
68
57
  title: 'Standalone FHIR Endpoint',
69
- description: 'URL of the FHIR endpoint used by standalone applications',
70
- default: 'https://inferno.healthit.gov/reference-server/r4'
58
+ description: 'URL of the FHIR endpoint used by standalone applications'
71
59
  },
72
60
  code: {
73
61
  name: :standalone_code
74
62
  },
75
63
  state: {
76
64
  name: :standalone_state
65
+ },
66
+ smart_credentials: {
67
+ name: :standalone_smart_credentials
77
68
  }
69
+
78
70
  },
79
71
  outputs: {
80
72
  code: { name: :standalone_code },
@@ -87,7 +79,8 @@ module SMARTAppLaunch
87
79
  patient_id: { name: :standalone_patient_id },
88
80
  encounter_id: { name: :standalone_encounter_id },
89
81
  received_scopes: { name: :standalone_received_scopes },
90
- intent: { name: :standalone_intent }
82
+ intent: { name: :standalone_intent },
83
+ smart_credentials: { name: :standalone_smart_credentials }
91
84
  },
92
85
  requests: {
93
86
  redirect: { name: :standalone_redirect },
@@ -95,8 +88,32 @@ module SMARTAppLaunch
95
88
  }
96
89
  )
97
90
 
91
+ test from: :tls_version_test,
92
+ id: :standalone_auth_tls,
93
+ title: 'OAuth 2.0 authorize endpoint secured by transport layer security',
94
+ description: %(
95
+ Apps MUST assure that sensitive information (authentication secrets,
96
+ authorization codes, tokens) is transmitted ONLY to authenticated
97
+ servers, over TLS-secured channels.
98
+ ),
99
+ config: {
100
+ inputs: { url: { name: :smart_authorization_url } },
101
+ options: { minimum_allowed_version: OpenSSL::SSL::TLS1_2_VERSION }
102
+ }
98
103
  test from: :smart_app_redirect
99
104
  test from: :smart_code_received
105
+ test from: :tls_version_test,
106
+ id: :standalone_token_tls,
107
+ title: 'OAuth 2.0 token endpoint secured by transport layer security',
108
+ description: %(
109
+ Apps MUST assure that sensitive information (authentication secrets,
110
+ authorization codes, tokens) is transmitted ONLY to authenticated
111
+ servers, over TLS-secured channels.
112
+ ),
113
+ config: {
114
+ inputs: { url: { name: :smart_token_url } },
115
+ options: { minimum_allowed_version: OpenSSL::SSL::TLS1_2_VERSION }
116
+ }
100
117
  test from: :smart_token_exchange
101
118
  test from: :smart_token_response_body
102
119
  test from: :smart_token_response_headers
@@ -14,11 +14,29 @@ module SMARTAppLaunch
14
14
  :smart_token_url,
15
15
  :client_id
16
16
  input :client_secret, optional: true
17
+ input :use_pkce,
18
+ title: 'Proof Key for Code Exchange (PKCE)',
19
+ type: 'radio',
20
+ default: 'false',
21
+ options: {
22
+ list_options: [
23
+ {
24
+ label: 'Enabled',
25
+ value: 'true'
26
+ },
27
+ {
28
+ label: 'Disabled',
29
+ value: 'false'
30
+ }
31
+ ]
32
+ }
33
+ input :pkce_code_verifier, optional: true
17
34
  output :token_retrieval_time
35
+ output :smart_credentials
18
36
  uses_request :redirect
19
37
  makes_request :token
20
38
 
21
- config options: { redirect_uri: "#{Inferno::Application['inferno_host']}/custom/smart/redirect" }
39
+ config options: { redirect_uri: "#{Inferno::Application['base_url']}/custom/smart/redirect" }
22
40
 
23
41
  run do
24
42
  skip_if request.query_parameters['error'].present?, 'Error during authorization request'
@@ -37,11 +55,27 @@ module SMARTAppLaunch
37
55
  oauth2_params[:client_id] = client_id
38
56
  end
39
57
 
58
+ if use_pkce == 'true'
59
+ oauth2_params[:code_verifier] = pkce_code_verifier
60
+ end
61
+
40
62
  post(smart_token_url, body: oauth2_params, name: :token, headers: oauth2_headers)
41
63
 
64
+ assert_response_status(200)
65
+ assert_valid_json(request.response_body)
66
+
42
67
  output token_retrieval_time: Time.now.iso8601
43
68
 
44
- assert_response_status(200)
69
+ token_response_body = JSON.parse(request.response_body)
70
+ output smart_credentials: {
71
+ refresh_token: token_response_body['refresh_token'],
72
+ access_token: token_response_body['access_token'],
73
+ expires_in: token_response_body['expires_in'],
74
+ client_id: client_id,
75
+ client_secret: client_secret,
76
+ token_retrieval_time: token_retrieval_time,
77
+ token_url: smart_token_url
78
+ }.to_json
45
79
  end
46
80
  end
47
81
  end
@@ -5,21 +5,12 @@ module SMARTAppLaunch
5
5
  include TokenPayloadValidation
6
6
 
7
7
  id :smart_token_refresh_body
8
- title 'Server successfully refreshes the access token when optional scope parameter omitted'
8
+ title 'Token refresh response contains all required fields'
9
9
  description %(
10
- Server successfully exchanges refresh token at OAuth token endpoint
11
- without providing scope in the body of the request.
12
-
13
10
  The EHR authorization server SHALL return a JSON structure that includes
14
11
  an access token or a message indicating that the authorization request
15
12
  has been denied. `access_token`, `expires_in`, `token_type`, and `scope` are
16
13
  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
14
  )
24
15
  input :received_scopes
25
16
  output :refresh_token, :access_token, :token_retrieval_time, :expires_in, :received_scopes
@@ -6,6 +6,7 @@ module SMARTAppLaunch
6
6
  class TokenRefreshGroup < Inferno::TestGroup
7
7
  id :smart_token_refresh
8
8
  title 'SMART Token Refresh'
9
+ short_description 'Demonstrate the ability to exchange a refresh token for an access token.'
9
10
  description %(
10
11
  # Background
11
12
 
@@ -10,11 +10,6 @@ module SMARTAppLaunch
10
10
  Server successfully exchanges refresh token at OAuth token endpoint
11
11
  without providing scope in the body of the request.
12
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
13
  Although not required in the token refresh portion of the SMART App
19
14
  Launch Guide, the token refresh response should include the HTTP
20
15
  Cache-Control response header field with a value of no-store, as well as
@@ -23,6 +18,7 @@ module SMARTAppLaunch
23
18
  )
24
19
  input :well_known_token_url, :refresh_token, :client_id, :received_scopes
25
20
  input :client_secret, optional: true
21
+ output :smart_credentials, :token_retrieval_time
26
22
  makes_request :token_refresh
27
23
 
28
24
  run do
@@ -46,7 +42,20 @@ module SMARTAppLaunch
46
42
  post(well_known_token_url, body: oauth2_params, name: :token_refresh, headers: oauth2_headers)
47
43
 
48
44
  assert_response_status(200)
49
- assert_valid_json(response[:body])
45
+ assert_valid_json(request.response_body)
46
+
47
+ output token_retrieval_time: Time.now.iso8601
48
+
49
+ token_response_body = JSON.parse(request.response_body)
50
+ output smart_credentials: {
51
+ refresh_token: token_response_body['refresh_token'],
52
+ access_token: token_response_body['access_token'],
53
+ expires_in: token_response_body['expires_in'],
54
+ client_id: client_id,
55
+ client_secret: client_secret,
56
+ token_retrieval_time: token_retrieval_time,
57
+ token_url: well_known_token_url
58
+ }.to_json
50
59
  end
51
60
  end
52
61
  end
@@ -0,0 +1,3 @@
1
+ module SMARTAppLaunch
2
+ VERSION = '0.1.0'
3
+ end
@@ -1,13 +1,36 @@
1
+ require 'tls_test_kit'
2
+
3
+ require_relative 'smart_app_launch/version'
1
4
  require_relative 'smart_app_launch/discovery_group'
2
5
  require_relative 'smart_app_launch/standalone_launch_group'
3
6
  require_relative 'smart_app_launch/ehr_launch_group'
4
7
  require_relative 'smart_app_launch/openid_connect_group'
5
8
  require_relative 'smart_app_launch/token_refresh_group'
6
9
 
10
+ # TODO: Remove once this functionality is released in core:
11
+ # https://github.com/inferno-framework/inferno-core/pull/86
12
+ module Inferno
13
+ module DSL
14
+ module Runnable
15
+ def required_inputs(prior_outputs = [])
16
+ required_inputs =
17
+ inputs
18
+ .reject { |input| input_definitions[input][:optional] }
19
+ .map { |input| config.input_name(input) }
20
+ .reject { |input| prior_outputs.include?(input) }
21
+ children_required_inputs = children.flat_map { |child| child.required_inputs(prior_outputs) }
22
+ prior_outputs.concat(outputs.map { |output| config.output_name(output) })
23
+ (required_inputs + children_required_inputs).flatten.uniq
24
+ end
25
+ end
26
+ end
27
+ end
28
+
7
29
  module SMARTAppLaunch
8
30
  class SMARTSuite < Inferno::TestSuite
9
31
  id 'smart'
10
- title 'SMART'
32
+ title 'SMART App Launch STU1'
33
+ version VERSION
11
34
 
12
35
  resume_test_route :get, '/launch' do
13
36
  request.query_parameters['iss']
@@ -18,12 +41,13 @@ module SMARTAppLaunch
18
41
  end
19
42
 
20
43
  config options: {
21
- redirect_uri: "#{Inferno::Application['inferno_host']}/custom/smart/redirect",
22
- launch_uri: "#{Inferno::Application['inferno_host']}/custom/smart/launch"
44
+ redirect_uri: "#{Inferno::Application['base_url']}/custom/smart/redirect",
45
+ launch_uri: "#{Inferno::Application['base_url']}/custom/smart/launch"
23
46
  }
24
47
 
25
48
  group do
26
49
  title 'Standalone Launch'
50
+ id :smart_full_standalone_launch
27
51
 
28
52
  run_as_group
29
53
 
@@ -36,7 +60,9 @@ module SMARTAppLaunch
36
60
  inputs: {
37
61
  id_token: { name: :standalone_id_token },
38
62
  client_id: { name: :standalone_client_id },
39
- requested_scopes: { name: :standalone_requested_scopes }
63
+ requested_scopes: { name: :standalone_requested_scopes },
64
+ access_token: { name: :standalone_access_token },
65
+ smart_credentials: { name: :standalone_smart_credentials }
40
66
  }
41
67
  }
42
68
 
@@ -55,7 +81,8 @@ module SMARTAppLaunch
55
81
  received_scopes: { name: :standalone_received_scopes },
56
82
  access_token: { name: :standalone_access_token },
57
83
  token_retrieval_time: { name: :standalone_token_retrieval_time },
58
- expires_in: { name: :standalone_expires_in }
84
+ expires_in: { name: :standalone_expires_in },
85
+ smart_credentials: { name: :standalone_smart_credentials }
59
86
  }
60
87
  }
61
88
 
@@ -75,13 +102,15 @@ module SMARTAppLaunch
75
102
  received_scopes: { name: :standalone_received_scopes },
76
103
  access_token: { name: :standalone_access_token },
77
104
  token_retrieval_time: { name: :standalone_token_retrieval_time },
78
- expires_in: { name: :standalone_expires_in }
105
+ expires_in: { name: :standalone_expires_in },
106
+ smart_credentials: { name: :standalone_smart_credentials }
79
107
  }
80
108
  }
81
109
  end
82
110
 
83
111
  group do
84
112
  title 'EHR Launch'
113
+ id :smart_full_ehr_launch
85
114
 
86
115
  run_as_group
87
116
 
@@ -94,7 +123,9 @@ module SMARTAppLaunch
94
123
  inputs: {
95
124
  id_token: { name: :ehr_id_token },
96
125
  client_id: { name: :ehr_client_id },
97
- requested_scopes: { name: :standalone_requested_scopes }
126
+ requested_scopes: { name: :ehr_requested_scopes },
127
+ access_token: { name: :ehr_access_token },
128
+ smart_credentials: { name: :ehr_smart_credentials }
98
129
  }
99
130
  }
100
131
 
@@ -113,7 +144,8 @@ module SMARTAppLaunch
113
144
  received_scopes: { name: :ehr_received_scopes },
114
145
  access_token: { name: :ehr_access_token },
115
146
  token_retrieval_time: { name: :ehr_token_retrieval_time },
116
- expires_in: { name: :ehr_expires_in }
147
+ expires_in: { name: :ehr_expires_in },
148
+ smart_credentials: { name: :ehr_smart_credentials }
117
149
  }
118
150
  }
119
151
 
@@ -133,7 +165,8 @@ module SMARTAppLaunch
133
165
  received_scopes: { name: :ehr_received_scopes },
134
166
  access_token: { name: :ehr_access_token },
135
167
  token_retrieval_time: { name: :ehr_token_retrieval_time },
136
- expires_in: { name: :ehr_expires_in }
168
+ expires_in: { name: :ehr_expires_in },
169
+ smart_credentials: { name: :ehr_smart_credentials }
137
170
  }
138
171
  }
139
172
  end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_app_launch_test_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.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: 2021-10-21 00:00:00.000000000 Z
11
+ date: 2022-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inferno_core
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - ">"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.6
19
+ version: 0.1.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.0.6
26
+ version: 0.1.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: jwt
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.2.2
33
+ version: '2.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.2.2
40
+ version: '2.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: tls_test_kit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: database_cleaner-sequel
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -139,13 +153,14 @@ files:
139
153
  - lib/smart_app_launch/token_refresh_test.rb
140
154
  - lib/smart_app_launch/token_response_body_test.rb
141
155
  - lib/smart_app_launch/token_response_headers_test.rb
156
+ - lib/smart_app_launch/version.rb
142
157
  - lib/smart_app_launch_test_kit.rb
143
- homepage: https://github.com/inferno_community/smart-app-launch-test-kit
158
+ homepage: https://github.com/inferno_framework/smart-app-launch-test-kit
144
159
  licenses:
145
160
  - Apache-2.0
146
161
  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
162
+ homepage_uri: https://github.com/inferno_framework/smart-app-launch-test-kit
163
+ source_code_uri: https://github.com/inferno_framework/smart-app-launch-test-kit
149
164
  post_install_message:
150
165
  rdoc_options: []
151
166
  require_paths: