smart_app_launch_test_kit 0.5.1 → 0.6.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/SMART_RunClientAgainstServer.json.erb +31 -0
  3. data/config/presets/SMART_RunServerAgainstClient.json.erb +42 -0
  4. data/config/presets/inferno_reference_server_preset.json +15 -86
  5. data/config/presets/inferno_reference_server_stu2_2_preset.json +20 -69
  6. data/config/presets/inferno_reference_server_stu2_preset.json +20 -69
  7. data/lib/smart_app_launch/app_redirect_test.rb +12 -44
  8. data/lib/smart_app_launch/app_redirect_test_stu2.rb +2 -17
  9. data/lib/smart_app_launch/backend_services_authorization_group.rb +33 -59
  10. data/lib/smart_app_launch/backend_services_authorization_request_builder.rb +22 -9
  11. data/lib/smart_app_launch/backend_services_authorization_request_success_test.rb +32 -24
  12. data/lib/smart_app_launch/backend_services_authorization_response_body_test.rb +23 -5
  13. data/lib/smart_app_launch/backend_services_invalid_client_assertion_test.rb +30 -25
  14. data/lib/smart_app_launch/backend_services_invalid_grant_type_test.rb +30 -24
  15. data/lib/smart_app_launch/backend_services_invalid_jwt_test.rb +31 -26
  16. data/lib/smart_app_launch/client_assertion_builder.rb +27 -12
  17. data/lib/smart_app_launch/client_stu2_2_suite.rb +79 -0
  18. data/lib/smart_app_launch/client_suite/client_access_group.rb +26 -0
  19. data/lib/smart_app_launch/client_suite/client_access_interaction_test.rb +64 -0
  20. data/lib/smart_app_launch/client_suite/client_registration_group.rb +15 -0
  21. data/lib/smart_app_launch/client_suite/client_registration_verification_test.rb +52 -0
  22. data/lib/smart_app_launch/client_suite/client_token_request_verification_test.rb +146 -0
  23. data/lib/smart_app_launch/client_suite/client_token_use_verification_test.rb +47 -0
  24. data/lib/smart_app_launch/cors_openid_fhir_user_claim_test.rb +2 -2
  25. data/lib/smart_app_launch/cors_token_exchange_test.rb +2 -2
  26. data/lib/smart_app_launch/discovery_stu1_group.rb +6 -2
  27. data/lib/smart_app_launch/docs/demo/FHIR Request.postman_collection.json +81 -0
  28. data/lib/smart_app_launch/docs/smart_stu2_2_client_suite_description.md +121 -0
  29. data/lib/smart_app_launch/ehr_launch_group.rb +41 -24
  30. data/lib/smart_app_launch/ehr_launch_group_stu2.rb +26 -10
  31. data/lib/smart_app_launch/ehr_launch_group_stu2_2.rb +0 -16
  32. data/lib/smart_app_launch/endpoints/echoing_fhir_responder.rb +52 -0
  33. data/lib/smart_app_launch/endpoints/mock_smart_server/token.rb +27 -0
  34. data/lib/smart_app_launch/endpoints/mock_smart_server.rb +217 -0
  35. data/lib/smart_app_launch/metadata.rb +2 -2
  36. data/lib/smart_app_launch/openid_fhir_user_claim_test.rb +5 -4
  37. data/lib/smart_app_launch/openid_token_payload_test.rb +6 -8
  38. data/lib/smart_app_launch/smart_stu1_suite.rb +32 -24
  39. data/lib/smart_app_launch/smart_stu2_2_suite.rb +57 -30
  40. data/lib/smart_app_launch/smart_stu2_suite.rb +57 -31
  41. data/lib/smart_app_launch/smart_tls_test.rb +14 -0
  42. data/lib/smart_app_launch/standalone_launch_group.rb +42 -25
  43. data/lib/smart_app_launch/standalone_launch_group_stu2.rb +26 -10
  44. data/lib/smart_app_launch/standalone_launch_group_stu2_2.rb +0 -16
  45. data/lib/smart_app_launch/tags.rb +7 -0
  46. data/lib/smart_app_launch/token_exchange_stu2_2_test.rb +5 -17
  47. data/lib/smart_app_launch/token_exchange_stu2_test.rb +8 -67
  48. data/lib/smart_app_launch/token_exchange_test.rb +18 -38
  49. data/lib/smart_app_launch/token_introspection_access_token_group.rb +12 -4
  50. data/lib/smart_app_launch/token_introspection_access_token_group_stu2_2.rb +9 -1
  51. data/lib/smart_app_launch/token_introspection_group.rb +2 -4
  52. data/lib/smart_app_launch/token_introspection_request_group.rb +2 -4
  53. data/lib/smart_app_launch/token_introspection_response_group.rb +64 -49
  54. data/lib/smart_app_launch/token_refresh_body_test.rb +9 -2
  55. data/lib/smart_app_launch/token_refresh_stu2_test.rb +10 -17
  56. data/lib/smart_app_launch/token_refresh_test.rb +19 -20
  57. data/lib/smart_app_launch/token_response_body_test.rb +14 -4
  58. data/lib/smart_app_launch/token_response_body_test_stu2_2.rb +3 -2
  59. data/lib/smart_app_launch/urls.rb +40 -0
  60. data/lib/smart_app_launch/version.rb +2 -2
  61. data/lib/smart_app_launch/well_known_endpoint_test.rb +11 -1
  62. data/lib/smart_app_launch_test_kit.rb +1 -0
  63. metadata +21 -4
@@ -0,0 +1,64 @@
1
+ require_relative '../urls'
2
+ require_relative '../endpoints/mock_smart_server'
3
+
4
+ module SMARTAppLaunch
5
+ class SMARTClientAccessInteraction < Inferno::Test
6
+ include URLs
7
+
8
+ id :smart_client_access_interaction
9
+ title 'Perform SMART-secured Access'
10
+ description %(
11
+ During this test, Inferno will wait for the client to access data
12
+ using a SMART token obtained during earlier tests.
13
+ )
14
+ input :client_id,
15
+ title: 'Client Id',
16
+ type: 'text',
17
+ locked: true,
18
+ description: %(
19
+ The registered Client Id for use in obtaining access tokens.
20
+ Create a new session if you need to change this value.
21
+ )
22
+ input :smart_jwk_set,
23
+ title: 'JSON Web Key Set (JWKS)',
24
+ type: 'textarea',
25
+ optional: true,
26
+ locked: true,
27
+ description: %(
28
+ The SMART client's JSON Web Key Set in the form of either a publicly accessible url
29
+ containing the JWKS, or the raw JWKS JSON. Must include the key(s) Inferno will need to
30
+ verify signatures on token requests made by the client.
31
+ Create a new session if you need to change this value.
32
+ )
33
+ input :echoed_fhir_response,
34
+ title: 'FHIR Response to Echo',
35
+ type: 'textarea',
36
+ description: %(
37
+ JSON representation of a FHIR resource for Inferno to echo when a request
38
+ is made to the simulated FHIR server. The provided content will be echoed
39
+ back exactly and no check will be made that it is appropriate for the request
40
+ made. If nothing is provided, an OperationOutcome will be returned.
41
+ ),
42
+ optional: true
43
+
44
+ run do
45
+ wait(
46
+ identifier: client_id,
47
+ message: %(
48
+ **Access**
49
+
50
+ Use the registered client id (#{client_id}) to obtain an access
51
+ token using SMART Backend Services
52
+ and use that token to access a FHIR endpoint under the simulated server's base URL
53
+
54
+ `#{client_fhir_base_url}`
55
+
56
+ Inferno will echo the response provided in the **FHIR Response to Echo** input.
57
+
58
+ [Click here](#{client_resume_pass_url}?token=#{client_id}) once you performed
59
+ the access.
60
+ )
61
+ )
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'client_registration_verification_test'
2
+
3
+ module SMARTAppLaunch
4
+ class SMARTClientRegistration < Inferno::TestGroup
5
+ id :smart_client_registration
6
+ title 'Client Registration'
7
+ description %(
8
+ During these tests, Inferno will verify the registration details provided as inputs,
9
+ including the client's JSON Web Key Set.
10
+ )
11
+ run_as_group
12
+
13
+ test from: :smart_client_registration_verification
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ require_relative '../tags'
2
+ require_relative '../endpoints/mock_smart_server'
3
+
4
+ module SMARTAppLaunch
5
+ class SMARTClientRegistrationVerification < Inferno::Test
6
+
7
+ id :smart_client_registration_verification
8
+ title 'Verify SMART Registration'
9
+ description %(
10
+ During this test, Inferno will verify that the SMART registration details
11
+ provided are conformant.
12
+ )
13
+ input :smart_jwk_set,
14
+ title: 'SMART JSON Web Key Set (JWKS)',
15
+ type: 'textarea',
16
+ description: %(
17
+ The SMART client's JSON Web Key Set including the key(s) Inferno will need to
18
+ verify signatures on token requests made by the client. May be provided as either
19
+ a publicly accessible url containing the JWKS, or the raw JWKS JSON.
20
+ )
21
+ input :client_id,
22
+ title: 'Client Id',
23
+ type: 'text',
24
+ optional: true,
25
+ description: %(
26
+ If a particular client id is desired, put it here. Otherwise a
27
+ default of the Inferno session id will be used.
28
+ )
29
+
30
+ output :client_id
31
+
32
+ run do
33
+ omit_if smart_jwk_set.blank?, # for re-use: mark the smart_jwk_set input as optional when importing to enable
34
+ 'Not configured for SMART authentication.'
35
+
36
+ if client_id.blank?
37
+ client_id = test_session_id
38
+ output(client_id:)
39
+ end
40
+
41
+ jwks_warnings = []
42
+ parsed_smart_jwk_set = MockSMARTServer.jwk_set(smart_jwk_set, jwks_warnings)
43
+ jwks_warnings.each { |warning| add_message('warning', warning) }
44
+
45
+ assert parsed_smart_jwk_set.length.positive?, 'JWKS content does not include any valid keys.'
46
+
47
+ # TODO: add key-specific verification per end of https://build.fhir.org/ig/HL7/smart-app-launch/client-confidential-asymmetric.html#registering-a-client-communicating-public-keys
48
+
49
+ assert messages.none? { |msg| msg[:type] == 'error' }, 'Invalid key set provided. See messages for details'
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,146 @@
1
+ require_relative '../tags'
2
+ require_relative '../urls'
3
+ require_relative '../endpoints/mock_smart_server'
4
+
5
+ module SMARTAppLaunch
6
+ class SMARTClientTokenRequestVerification < Inferno::Test
7
+ include URLs
8
+
9
+ id :smart_client_token_request_verification
10
+ title 'Verify SMART Token Requests'
11
+ description %(
12
+ Check that SMART token requests are conformant.
13
+ )
14
+
15
+ input :client_id,
16
+ title: 'Client Id',
17
+ type: 'text',
18
+ optional: false,
19
+ locked: true,
20
+ description: %(
21
+ The registered Client Id for use in obtaining access tokens.
22
+ Create a new session if you need to change this value.
23
+ )
24
+ input :smart_jwk_set,
25
+ title: 'JSON Web Key Set (JWKS)',
26
+ type: 'textarea',
27
+ optional: false,
28
+ locked: true,
29
+ description: %(
30
+ The SMART client's JSON Web Key Set in the form of either a publicly accessible url
31
+ containing the JWKS, or the raw JWKS JSON. Must include the key(s) Inferno will need to
32
+ verify signatures on token requests made by the client.
33
+ Create a new session if you need to change this value.
34
+ )
35
+ output :smart_tokens
36
+
37
+ run do
38
+ omit_if smart_jwk_set.blank?, # for re-use: mark the smart_jwk_set input as optional when importing to enable
39
+ 'SMART Backend Services authentication not demonstrated as a part of this test session.'
40
+
41
+ load_tagged_requests(TOKEN_TAG, SMART_TAG)
42
+ skip_if requests.blank?, 'No SMART token requests made.'
43
+
44
+ jti_list = []
45
+ token_list = []
46
+ requests.each_with_index do |token_request, index|
47
+ request_params = URI.decode_www_form(token_request.request_body).to_h
48
+ check_request_params(request_params, index + 1)
49
+ check_client_assertion(request_params['client_assertion'], index + 1, jti_list)
50
+ token_list << extract_token_from_response(token_request)
51
+ end
52
+
53
+ output smart_tokens: token_list.compact.join("\n")
54
+
55
+ assert messages.none? { |msg|
56
+ msg[:type] == 'error'
57
+ }, 'Invalid token requests detected. See messages for details.'
58
+ end
59
+
60
+ def check_request_params(params, request_num)
61
+ if params['grant_type'] != 'client_credentials'
62
+ add_message('error',
63
+ "Token request #{request_num} had an incorrect `grant_type`: expected 'client_credentials', " \
64
+ "but got '#{params['grant_type']}'")
65
+ end
66
+ if params['client_assertion_type'] != 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
67
+ add_message('error',
68
+ "Token request #{request_num} had an incorrect `client_assertion_type`: " \
69
+ "expected 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', " \
70
+ "but got '#{params['client_assertion_type']}'")
71
+ end
72
+ return unless params['scope'].blank?
73
+
74
+ add_message('error', "Token request #{request_num} did not include the requested `scope`")
75
+ end
76
+
77
+ def check_client_assertion(assertion, request_num, jti_list)
78
+ decoded_token =
79
+ begin
80
+ JWT::EncodedToken.new(assertion)
81
+ rescue StandardError => e
82
+ add_message('error', "Token request #{request_num} contained an invalid client assertion jwt: #{e}")
83
+ nil
84
+ end
85
+
86
+ return unless decoded_token.present?
87
+
88
+ check_jwt_header(decoded_token.header, request_num)
89
+ check_jwt_payload(decoded_token.payload, request_num, jti_list)
90
+ check_jwt_signature(decoded_token, request_num)
91
+ end
92
+
93
+ def check_jwt_header(header, request_num)
94
+ return unless header['typ'] != 'JWT'
95
+
96
+ add_message('error', "client assertion jwt on token request #{request_num} has an incorrect `typ` header: " \
97
+ "expected 'JWT', got '#{header['typ']}'")
98
+ end
99
+
100
+ def check_jwt_payload(claims, request_num, jti_list)
101
+ if claims['iss'] != client_id
102
+ add_message('error', "client assertion jwt on token request #{request_num} has an incorrect `iss` claim: " \
103
+ "expected '#{client_id}', got '#{claims['iss']}'")
104
+ end
105
+
106
+ if claims['sub'] != client_id
107
+ add_message('error', "client assertion jwt on token request #{request_num} has an incorrect `sub` claim: " \
108
+ "expected '#{client_id}', got '#{claims['sub']}'")
109
+ end
110
+
111
+ if claims['aud'] != client_token_url
112
+ add_message('error', "client assertion jwt on token request #{request_num} has an incorrect `aud` claim: " \
113
+ "expected '#{client_token_url}', got '#{claims['aud']}'")
114
+ end
115
+
116
+ if claims['exp'].blank?
117
+ add_message('error', "client assertion jwt on token request #{request_num} is missing the `exp` claim.")
118
+ end
119
+
120
+ if claims['jti'].blank?
121
+ add_message('error', "client assertion jwt on token request #{request_num} is missing the `jti` claim.")
122
+ elsif jti_list.include?(claims['jti'])
123
+ add_message('error', "client assertion jwt on token request #{request_num} has a `jti` claim that was " \
124
+ "previouly used: '#{claims['jti']}'.")
125
+ else
126
+ jti_list << claims['jti']
127
+ end
128
+ end
129
+
130
+ def check_jwt_signature(encoded_token, request_num)
131
+ error = MockSMARTServer.smart_assertion_signature_verification(encoded_token, smart_jwk_set)
132
+
133
+ return unless error.present?
134
+
135
+ add_message('error', "Signature validation failed on token request #{request_num}: #{error}")
136
+ end
137
+
138
+ def extract_token_from_response(request)
139
+ return unless request.status == 200
140
+
141
+ JSON.parse(request.response_body)&.dig('access_token')
142
+ rescue
143
+ nil
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,47 @@
1
+ require_relative '../tags'
2
+ require_relative '../endpoints/mock_smart_server'
3
+
4
+ module SMARTAppLaunch
5
+ class SMARTClientTokenUseVerification < Inferno::Test
6
+
7
+ id :smart_client_token_use_verification
8
+ title 'Verify SMART Token Use'
9
+ description %(
10
+ Check that a SMART token returned to the client was used for request
11
+ authentication.
12
+ )
13
+
14
+ input :smart_tokens,
15
+ optional: true # verified in the test to return a more specific error message
16
+ input :smart_jwk_set,
17
+ optional: false,
18
+ locked: true
19
+
20
+ def access_request_tags
21
+ return config.options[:access_request_tags] if config.options[:access_request_tags].present?
22
+
23
+ [ACCESS_TAG]
24
+ end
25
+
26
+ run do
27
+ omit_if smart_jwk_set.blank?, # for re-use: mark the smart_jwk_set input as optional when importing to enable
28
+ 'SMART Authentication not demonstrated as a part of this test session.'
29
+
30
+ access_requests = access_request_tags.map do |access_request_tag|
31
+ load_tagged_requests(access_request_tag).reject { |access| access.status == 401 }
32
+ end.flatten
33
+ obtained_tokens = smart_tokens&.split("\n")
34
+
35
+ skip_if obtained_tokens.blank?, 'No token requests made.'
36
+ skip_if access_requests.blank?, 'No successful access requests made.'
37
+
38
+ used_tokens = access_requests.map do |access_request|
39
+ access_request.request_headers.find do |header|
40
+ header.name.downcase == 'authorization'
41
+ end&.value&.delete_prefix('Bearer ')
42
+ end.compact
43
+
44
+ assert (used_tokens & obtained_tokens).present?, 'Returned tokens never used in any requests.'
45
+ end
46
+ end
47
+ end
@@ -16,11 +16,11 @@ module SMARTAppLaunch
16
16
  optional
17
17
 
18
18
  input :url, :id_token_fhir_user
19
- input :smart_credentials, type: :oauth_credentials
19
+ input :smart_auth_info, type: :auth_info
20
20
 
21
21
  fhir_client do
22
22
  url :url
23
- oauth_credentials :smart_credentials
23
+ auth_info :smart_auth_info
24
24
  headers 'Origin' => Inferno::Application['inferno_host']
25
25
  end
26
26
 
@@ -15,10 +15,10 @@ module SMARTAppLaunch
15
15
 
16
16
  uses_request :cors_token_request
17
17
 
18
- input :client_auth_type
18
+ input :smart_auth_info, type: :auth_info, options: { mode: 'auth' }
19
19
 
20
20
  run do
21
- omit_if client_auth_type != 'public', %(
21
+ omit_if smart_auth_info.auth_type != 'public', %(
22
22
  Client type is not public, Cross-Origin Resource Sharing (CORS) is not required to be supported for
23
23
  non-public client types
24
24
  )
@@ -40,8 +40,12 @@ module SMARTAppLaunch
40
40
  * [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html)
41
41
  )
42
42
 
43
- test from: :well_known_endpoint,
44
- id: 'Test01'
43
+ test from: :well_known_endpoint do
44
+ id 'Test01'
45
+ input :smart_auth_info,
46
+ type: :auth_info
47
+ end
48
+
45
49
  test from: :well_known_capabilities_stu1,
46
50
  id: 'Test02'
47
51
 
@@ -0,0 +1,81 @@
1
+ {
2
+ "info": {
3
+ "_postman_id": "22f52416-c6ae-4ffc-a388-54616465d149",
4
+ "name": "FHIR Request",
5
+ "description": "Make a simple FHIR request with a specific bearer token. Useful for security client tests like SMART and UDAP.\n\n- base_url: points to a running instance of inferno. Typical values will be\n \n - Inferno production: [https://inferno.healthit.gov/suites](https://inferno.healthit.gov/suites)\n \n - Inferno QA: [https://inferno-qa.healthit.gov/suites](https://inferno-qa.healthit.gov/suites)\n \n - Local docker: [http://localhost](http://localhost)\n \n - Local development: [http://localhost:4567](http://localhost:4567)",
6
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
7
+ "_exporter_id": "32597978"
8
+ },
9
+ "item": [
10
+ {
11
+ "name": "Patient Read",
12
+ "request": {
13
+ "auth": {
14
+ "type": "bearer",
15
+ "bearer": [
16
+ {
17
+ "key": "token",
18
+ "value": "{{bearer_token}}",
19
+ "type": "string"
20
+ }
21
+ ]
22
+ },
23
+ "method": "GET",
24
+ "header": [],
25
+ "url": {
26
+ "raw": "{{base_url}}/custom/{{target_suite}}/fhir/Patient/example",
27
+ "host": [
28
+ "{{base_url}}"
29
+ ],
30
+ "path": [
31
+ "custom",
32
+ "{{target_suite}}",
33
+ "fhir",
34
+ "Patient",
35
+ "example"
36
+ ]
37
+ }
38
+ },
39
+ "response": []
40
+ }
41
+ ],
42
+ "event": [
43
+ {
44
+ "listen": "prerequest",
45
+ "script": {
46
+ "type": "text/javascript",
47
+ "packages": {},
48
+ "exec": [
49
+ ""
50
+ ]
51
+ }
52
+ },
53
+ {
54
+ "listen": "test",
55
+ "script": {
56
+ "type": "text/javascript",
57
+ "packages": {},
58
+ "exec": [
59
+ ""
60
+ ]
61
+ }
62
+ }
63
+ ],
64
+ "variable": [
65
+ {
66
+ "key": "base_url",
67
+ "value": "https://inferno.healthit.gov/suites",
68
+ "type": "string"
69
+ },
70
+ {
71
+ "key": "target_suite",
72
+ "value": "smart_client_stu2_2",
73
+ "type": "string"
74
+ },
75
+ {
76
+ "key": "bearer_token",
77
+ "value": "",
78
+ "type": "string"
79
+ }
80
+ ]
81
+ }
@@ -0,0 +1,121 @@
1
+ ## Overview
2
+
3
+ The SMART App Launch STU 2.2 Client Test Suite verifies the conformance of
4
+ client systems to the STU 2.2.0 version of the HL7® FHIR®
5
+ [SMART App Launch IG](https://hl7.org/fhir/smart-app-launch/STU2.2/).
6
+
7
+ ## Scope
8
+
9
+ The SMART App Launch Client Test Suite verifies that systems correctly implement
10
+ the [SMART App Launch IG](http://hl7.org/fhir/smart-app-launch/STU2.2/)
11
+ for authorizating and/or authenticating with a server in order to gain
12
+ access to HL7® FHIR® APIs. At this time, the suite only contains tests for
13
+ the [Backend Services](https://hl7.org/fhir/smart-app-launch/STU2.2/backend-services.html)
14
+ flow.
15
+
16
+ These tests are a **DRAFT** intended to allow implementers to perform
17
+ preliminary checks of their systems against SMART requirements and
18
+ [provide feedback](https://github.com/inferno-framework/smart-app-launch-test-kit/issues)
19
+ on the tests. Future versions of these tests may verify other
20
+ requirements and may change the test verification logic.
21
+
22
+ ## Test Methodology
23
+
24
+ For these tests Inferno simulates a SMART server that supports the backend services
25
+ flow. Testers will
26
+ 1. Provide registration details as inputs, including a JSON Web Key Set (JWKS)
27
+ an optionally a client id if a specific one should be used.
28
+ 2. Request an access token using the registered JWKS and client id.
29
+ 3. Use that access token on a FHIR API request.
30
+
31
+ The simulated server is relatively permissive in the sense that it will often
32
+ provide successful responses even when the request is not conformant. When
33
+ requesting tokens, Inferno will return an access token as long as it can find
34
+ the client id and the signature is valid. This allows incomplete systems to
35
+ run the tests. However, these non-conformant requests will be flagged by
36
+ the tests as failures so that systems will not pass the tests without being
37
+ fully conformant.
38
+
39
+ ## Running the Tests
40
+
41
+ ### Quick Start
42
+
43
+ The following inputs must be provided by the tester at a minimum to execute
44
+ any tests in this suite:
45
+ 1. **SMART JSON Web Key Set (JWKS)**: The SMART client's public JSON Web Key Set including
46
+ key(s) that Inferno will use to verify the signature on incoming token requests. May
47
+ be provided as either a publicly accessible url containing the JWKS, or the raw JWKS.
48
+
49
+ The *Additional Inputs* section below describes options available to customize
50
+ the behavior of Inferno's server simulation.
51
+
52
+ ### Demonstration
53
+
54
+ To try out these tests without a SMART client implementation, these tests can be exercised
55
+ using the SMART App Launch server test suite and a simple HTTP request generator. The following
56
+ steps use [Postman](https://www.postman.com/) to generate the access request using
57
+ [this collection](https://github.com/inferno-framework/smart-app-launch-test-kit/blob/main/lib/smart_app_launch/docs/demo/FHIR%20Request.postman_collection.json). Install the app and import the collection before following these
58
+ steps.
59
+
60
+ 1. Start an instance of the SMART App Launch STU2.2 Client test suite.
61
+ 2. From the drop down in the upper left, select preset "Demo: Run Against the SMART Server Suite".
62
+ 3. Click the "RUN ALL TESTS" button in the upper right and click "SUBMIT"
63
+ 4. In a new tab, start an instance of the SMART App Launch STU2.2 Test Suite
64
+ 5. From the drop down in the upper left, select preset "Demo: Run Against the SMART Client Suite"
65
+ 6. Select test group **3** Backend Services from the left panel, click the "RUN TESTS" button
66
+ in the upper right, and click "SUBMIT"
67
+ 7. Find the access token to use for the data access request by opening test **3.2.05** Authorization
68
+ request succeeds when supplied correct information, click on the "REQUESTS" tab, clicking on the "DETAILS"
69
+ button, and expanding the "Response Body". Copy the "access_token" value, which will be a ~100 character
70
+ string of letters and numbers (e.g., eyJjbGllbnRfaWQiOiJzbWFydF9jbGllbnRfdGVzdF9kZW1vIiwiZXhwaXJhdGlvbiI6MTc0MzUxNDk4Mywibm9uY2UiOiJlZDI5MWIwNmZhMTE4OTc4In0)
71
+ 8. Open Postman and open the "FHIR Request" Collection. Click the "Variables" tab and add the copied access token
72
+ as the current value of the `bearer_token` variable. Also update the
73
+ `base_url` value for where the test is running (see details on the "Overview" tab).
74
+ Save the collection.
75
+ 9. Select the "Patient Read" request and click "Send". A FHIR Patient resource should be returned.
76
+ 10. Return to the client tests and click the link to continue and complete the tests.
77
+
78
+ The client tests should pass with the exception of test **1.2.02** Verify SMART Token Requests. This is
79
+ expected as the Server tests make several intentionally invalid token requests. Inferno's simulated SMART
80
+ server responds successfully to those requests when the client id can be identified, but flags them as
81
+ not conformant causing these expected failures. Because responding with an access token to non-conformant
82
+ token requests is itself not conformant there are corresponding failures on the server test in tests **3.2.02**,
83
+ **3.2.04**, and **3.2.04**. There may be other SMART server test failures due to an assumption that
84
+ servers support the app launch capabilities in addition to backend services.
85
+
86
+ ### Additional Inputs
87
+
88
+ Two additional inputs are available to support testers
89
+ - **Client Id**: Testers may specify a client id for Inferno to use for the test session if they
90
+ have one already configured.
91
+ - **FHIR Response to Echo**: The focus of this test kit is on the auth protocol, so the
92
+ simulated FHIR server implemented in this test suite is very simple and will by default
93
+ return a FHIR OperationOutcome to any request made. Testers may provide a static
94
+ FHIR JSON body for Inferno to return instead. In this case, the simulation is a simple
95
+ echo and Inferno does not check that the response if appropriate for the request made.
96
+
97
+ ## Current Limitations
98
+
99
+ This test kit is still in draft form and does not test all of the requirements and features
100
+ described in the SMART App Launch IG for clients. Notably, only the backend services flow
101
+ is tested at this time.
102
+
103
+ The following sections list other known gaps and limitations.
104
+
105
+ ### SMART Server Simulation Limitations
106
+
107
+ This test suite contains a simulation of a SMART Backend Services server which is not fully
108
+ general and not all conformant clients may be able to interact with it. However, the intention
109
+ is not to prevent systems from passing for making conformant choices that Inferno's simulation
110
+ does not support. One specific example is that the SMART configuration metadata available at
111
+ `.well-known/smart-configuration` for the simulated server is fixed and cannot be changed by
112
+ testers at this time. Please report any issues that prevent conformant systems from passing in
113
+ the [github repository's issues page](https://github.com/inferno-framework/smart-app-launch-test-kit/issues/).
114
+
115
+ ### FHIR Server Simulation Limitations
116
+
117
+ The FHIR server simulation used to support clients in demonstrating their ability to access
118
+ FHIR APIs using access tokens obtained using the SMART flows is very limited. Testers are currently
119
+ able to provide a single static response that will be echoed for any FHIR request made. While
120
+ Inferno will never implement a fully general FHIR server simulation, additional options may be added
121
+ in the future based on community feedback.