smart_app_launch_test_kit 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c23a85c4635a9c978a52015cd1d00da2b271d3bfdec72bcfe6e100a04c1658ef
4
- data.tar.gz: 8ce426a018dbdf4705463db59dda3a57514f43e61f07b2125be4be118a24336a
3
+ metadata.gz: 97f3e4dca10ecb9dfe4aa30802d90f4272dd32ffd10715fa7b3a0855a04b5c8d
4
+ data.tar.gz: 016a700551ef524e7bdc7f8871beb03f7297dc3cba39e5b4b53349b22f22ed53
5
5
  SHA512:
6
- metadata.gz: b492ba57b918b07a58c775f51ee37334fb072dbf850a6f0fff6baaef2e9a151edbe62b7a8365f7848f6b4d0baca6f0e3b2c11bca968f0d0925c2181e890f25eb
7
- data.tar.gz: f1dd318a8e2cff15ece17cb4e4a743340287a1f966cf4919c0e5d56de97c1301d455ce8114b2c23b571f9884b3f9c8730c5ecb66158cc7e43f07625b971a1fde
6
+ metadata.gz: 519d2bb8c5bde6a7c5c28f04ec17bfdcff37c7270b6216707a05db4b04c94269d244c6a1888dd97a0c25c5a50764d4969a08c2774dcb636db7fd274301184427
7
+ data.tar.gz: eda1f5484f72de3a82c34eb1d0424a2b10a327baeb93d6e6df8f94be7a7e3ef378e5e6510378f62e7d9387e56424b42b52f4524fc557302d3a5ddab0654c782e
@@ -126,6 +126,8 @@ module SMARTAppLaunch
126
126
  oauth2_params
127
127
  )
128
128
 
129
+ info("Inferno redirecting browser to #{authorization_url}.")
130
+
129
131
  wait(
130
132
  identifier: state,
131
133
  message: wait_message(authorization_url)
@@ -0,0 +1,63 @@
1
+ require 'jwt'
2
+
3
+ require_relative 'jwks'
4
+
5
+ module SMARTAppLaunch
6
+ class ClientAssertionBuilder
7
+ def self.build(...)
8
+ new(...).client_assertion
9
+ end
10
+
11
+ attr_reader :aud,
12
+ :client_assertion_type,
13
+ :content_type,
14
+ :client_auth_encryption_method,
15
+ :exp,
16
+ :grant_type,
17
+ :iss,
18
+ :jti,
19
+ :sub
20
+
21
+ def initialize(
22
+ client_auth_encryption_method:,
23
+ iss:,
24
+ sub:,
25
+ aud:,
26
+ exp: 5.minutes.from_now.to_i,
27
+ jti: SecureRandom.hex(32)
28
+ )
29
+ @client_auth_encryption_method = client_auth_encryption_method
30
+ @iss = iss
31
+ @sub = sub
32
+ @aud = aud
33
+ @content_type = content_type
34
+ @grant_type = grant_type
35
+ @client_assertion_type = client_assertion_type
36
+ @exp = exp
37
+ @jti = jti
38
+ end
39
+
40
+ def private_key
41
+ @private_key ||=
42
+ JWKS.jwks
43
+ .find { |key| key[:key_ops]&.include?('sign') && key[:alg] == client_auth_encryption_method }
44
+ end
45
+
46
+ def jwt_payload
47
+ { iss:, sub:, aud:, exp:, jti: }.compact
48
+ end
49
+
50
+ def kid
51
+ private_key.kid
52
+ end
53
+
54
+ def signing_key
55
+ private_key.signing_key
56
+ end
57
+
58
+ def client_assertion
59
+ @client_assertion ||=
60
+ JWT.encode jwt_payload, signing_key, client_auth_encryption_method, { alg: client_auth_encryption_method, kid:, typ: 'JWT' }
61
+ end
62
+ end
63
+ end
@@ -13,7 +13,7 @@ module SMARTAppLaunch
13
13
  code = request.query_parameters['code']
14
14
  output code: code
15
15
 
16
- assert code.present?, 'No `code` paramater received'
16
+ assert code.present?, 'No `code` parameter received'
17
17
 
18
18
  error = request.query_parameters['error']
19
19
 
@@ -48,7 +48,8 @@ module SMARTAppLaunch
48
48
  client_secret: {
49
49
  name: :ehr_client_secret,
50
50
  title: 'EHR Launch Client Secret',
51
- description: 'Client Secret provided during registration of Inferno as an EHR launch application'
51
+ description: 'Client Secret provided during registration of Inferno as an EHR launch application. ' \
52
+ 'Only for clients using confidential symmetric authentication.'
52
53
  },
53
54
  requested_scopes: {
54
55
  name: :ehr_requested_scopes,
@@ -1,5 +1,6 @@
1
1
  require_relative 'app_redirect_test_stu2'
2
2
  require_relative 'ehr_launch_group'
3
+ require_relative 'token_exchange_stu2_test'
3
4
 
4
5
  module SMARTAppLaunch
5
6
  class EHRLaunchGroupSTU2 < EHRLaunchGroup
@@ -52,5 +53,12 @@ module SMARTAppLaunch
52
53
 
53
54
  redirect_index = children.find_index { |child| child.id.to_s.end_with? 'app_redirect' }
54
55
  children[redirect_index] = children.pop
56
+
57
+ test from: :smart_token_exchange_stu2
58
+
59
+ token_exchange_index = children.find_index { |child| child.id.to_s.end_with? 'token_exchange' }
60
+ children[token_exchange_index] = children.pop
61
+
62
+ children[token_exchange_index].id(:smart_token_exchange)
55
63
  end
56
64
  end
@@ -0,0 +1,27 @@
1
+ require 'jwt'
2
+
3
+ module SMARTAppLaunch
4
+ class JWKS
5
+ class << self
6
+ def jwks_json
7
+ @jwks_json ||=
8
+ JSON.pretty_generate(
9
+ { keys: jwks.export[:keys].select { |key| key[:key_ops]&.include?('verify') } }
10
+ )
11
+ end
12
+
13
+ def default_jwks_path
14
+ @default_jwks_path ||= File.join(__dir__, 'smart_jwks.json')
15
+ end
16
+
17
+ def jwks_path
18
+ @jwks_path ||=
19
+ ENV.fetch('SMART_JWKS_PATH', default_jwks_path)
20
+ end
21
+
22
+ def jwks
23
+ @jwks ||= JWT::JWK::Set.new(JSON.parse(File.read(jwks_path)))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -2,7 +2,8 @@ module SMARTAppLaunch
2
2
  class LaunchReceivedTest < Inferno::Test
3
3
  title 'EHR server sends launch parameter'
4
4
  description %(
5
- Code is a required querystring parameter on the redirect.
5
+ The `launch` URL parameter associates the app's authorization request with
6
+ the current EHR session.
6
7
  )
7
8
  id :smart_launch_received
8
9
 
@@ -13,7 +14,7 @@ module SMARTAppLaunch
13
14
  launch = request.query_parameters['launch']
14
15
  output launch: launch
15
16
 
16
- assert launch.present?, 'No `launch` paramater received'
17
+ assert launch.present?, 'No `launch` parameter received'
17
18
  end
18
19
  end
19
20
  end
@@ -0,0 +1,58 @@
1
+ {
2
+ "keys":
3
+ [{
4
+ "kty": "EC",
5
+ "crv": "P-384",
6
+ "x": "JQKTsV6PT5Szf4QtDA1qrs0EJ1pbimQmM2SKvzOlIAqlph3h1OHmZ2i7MXahIF2C",
7
+ "y": "bRWWQRJBgDa6CTgwofYrHjVGcO-A7WNEnu4oJA5OUJPPPpczgx1g2NsfinK-D2Rw",
8
+ "use": "sig",
9
+ "key_ops": [
10
+ "verify"
11
+ ],
12
+ "ext": true,
13
+ "kid": "4b49a739d1eb115b3225f4cf9beb6d1b",
14
+ "alg": "ES384"
15
+ },
16
+ {
17
+ "kty": "EC",
18
+ "crv": "P-384",
19
+ "d": "kDkn55p7gryKk2tj6z2ij7ExUnhi0ngxXosvqa73y7epwgthFqaJwApmiXXU2yhK",
20
+ "x": "JQKTsV6PT5Szf4QtDA1qrs0EJ1pbimQmM2SKvzOlIAqlph3h1OHmZ2i7MXahIF2C",
21
+ "y": "bRWWQRJBgDa6CTgwofYrHjVGcO-A7WNEnu4oJA5OUJPPPpczgx1g2NsfinK-D2Rw",
22
+ "key_ops": [
23
+ "sign"
24
+ ],
25
+ "ext": true,
26
+ "kid": "4b49a739d1eb115b3225f4cf9beb6d1b",
27
+ "alg": "ES384"
28
+ },
29
+ {
30
+ "kty": "RSA",
31
+ "alg": "RS384",
32
+ "n": "vjbIzTqiY8K8zApeNng5ekNNIxJfXAue9BjoMrZ9Qy9m7yIA-tf6muEupEXWhq70tC7vIGLqJJ4O8m7yiH8H2qklX2mCAMg3xG3nbykY2X7JXtW9P8VIdG0sAMt5aZQnUGCgSS3n0qaooGn2LUlTGIR88Qi-4Nrao9_3Ki3UCiICeCiAE224jGCg0OlQU6qj2gEB3o-DWJFlG_dz1y-Mxo5ivaeM0vWuodjDrp-aiabJcSF_dx26sdC9dZdBKXFDq0t19I9S9AyGpGDJwzGRtWHY6LsskNHLvo8Zb5AsJ9eRZKpnh30SYBZI9WHtzU85M9WQqdScR69Vyp-6Uhfbvw",
33
+ "e": "AQAB",
34
+ "use": "sig",
35
+ "key_ops": [
36
+ "verify"
37
+ ],
38
+ "ext": true,
39
+ "kid": "b41528b6f37a9500edb8a905a595bdd7"
40
+ },
41
+ {
42
+ "kty": "RSA",
43
+ "alg": "RS384",
44
+ "n": "vjbIzTqiY8K8zApeNng5ekNNIxJfXAue9BjoMrZ9Qy9m7yIA-tf6muEupEXWhq70tC7vIGLqJJ4O8m7yiH8H2qklX2mCAMg3xG3nbykY2X7JXtW9P8VIdG0sAMt5aZQnUGCgSS3n0qaooGn2LUlTGIR88Qi-4Nrao9_3Ki3UCiICeCiAE224jGCg0OlQU6qj2gEB3o-DWJFlG_dz1y-Mxo5ivaeM0vWuodjDrp-aiabJcSF_dx26sdC9dZdBKXFDq0t19I9S9AyGpGDJwzGRtWHY6LsskNHLvo8Zb5AsJ9eRZKpnh30SYBZI9WHtzU85M9WQqdScR69Vyp-6Uhfbvw",
45
+ "e": "AQAB",
46
+ "d": "rriV9GYimi5by7TOW4xNh6_gYBHVRDBsft2OFF8qapdVHt2GNuRDDxc_B6ga6TY2Enh2MLKLTr1dD3W4FIdTCJiMerrorp07FJS7nJEMgWQDxrfgkX4_EqrhW42L5d4vypYnRXEEW6u4gzkx5uFOkdvJBIK7CsIdSaBFYhochnynNgvbKWasi4rl2hayEH8tdf3B7Z6OIH9alspBTaq3j_zJt_KkrpYEzIUb4UgALB5NTWn5YKr0Avk_asOg8YfjViQwO9ASGaWjQeJ2Rx8OEQwBMQHSDMCSWNiWmYOu9PcwSZFc1vLxqzyIM8QrQSJHCCMo_wGYgke_r0CLeONHEQ",
47
+ "p": "5hH_QApWGeobRi1n7XbMfJYohB8K3JDPa0MspfplHpJ-17JiGG2sNoBdBcpaPRf9OX48P8VqO0qrSSRAk-I-uO6OO9BHbIukXJILqnY2JmurYzbcYbt5FVbknlHRJojkF6-7sFBazpueUlOnXCw7X7Z_SkfNE4QX5Ejm2Zm5mek",
48
+ "q": "06bZz7c7K9s1-aEZsxYnLJ9eTpKlt1tIBDA_LwIh5W3w259pes2kUtimbnkyOf-V2ZIERsFCh5s-S9IOEMvAIa6M5j9GW1ILNT7AcHIUfcyFcH-FF8BU_KJdRP5PXnIXFdYcylvsdoIdchy1AaUIzyiKRCU3HBYI75hez0l_F2c",
49
+ "dp": "h_sVIXW6hCCRND48EedIX06k7conMkxIu_39ErDXOWWeoMAnKIcR5TijQnviL__QxD1vQMXezuKIMHfDz2RGbClbWdD1lhtG7wvG515tDPJQXxia0wzqOQmdoFF9S8hXAAT26vPjaAAkaEZXQaxG_4Au5elgNWu6b0wDXZN1Vpk",
50
+ "dq": "GqS0YpuUTU8JGmWXUJ4HTGy7eHSpe8134V8ZdRd1oOYYHe2RX64nc25mdR24nuh3uq3Q7_9AGsYGL5E_yAl-JD9O6WUpvDE1y_wcSYty3Os0GRdUb8r8Z9kgmKDS6Pa_xTXw5eBwgfKbNlQ6zPwzgbB-x1lP-K8lbNPni3ybDR0",
51
+ "qi": "cqQfoi0sM5Su8ZOhznmdWrDIQB28H6fBKiabgaIKkbWZV4e0nwFvLquHjPOvv4Ao8iEGU5dyhvg0n5BKYPi-4mp6M6OA1sy0NrTr7EsKSYGyu2pBq9rw4oAYTM2LXKg6K-awgUUlkc451IwxHBAe15PWCBM3kvLQeijNid0Vz5I",
52
+ "key_ops": [
53
+ "sign"
54
+ ],
55
+ "ext": true,
56
+ "kid": "b41528b6f37a9500edb8a905a595bdd7"
57
+ }]
58
+ }
@@ -1,5 +1,6 @@
1
1
  require 'tls_test_kit'
2
2
 
3
+ require_relative 'jwks'
3
4
  require_relative 'version'
4
5
  require_relative 'discovery_stu1_group'
5
6
  require_relative 'standalone_launch_group'
@@ -21,15 +22,39 @@ module SMARTAppLaunch
21
22
  request.query_parameters['state']
22
23
  end
23
24
 
25
+ route(
26
+ :get,
27
+ '/.well-known/jwks.json',
28
+ ->(_env) { [200, { 'Content-Type' => 'application/json' }, [JWKS.jwks_json]] }
29
+ )
30
+
24
31
  config options: {
25
32
  redirect_uri: "#{Inferno::Application['base_url']}/custom/smart/redirect",
26
33
  launch_uri: "#{Inferno::Application['base_url']}/custom/smart/launch"
27
34
  }
28
35
 
36
+ description <<~DESCRIPTION
37
+ The SMART App Launch Test Suite verifies that systems correctly implement
38
+ the [SMART App Launch IG](http://hl7.org/fhir/smart-app-launch/1.0.0/)
39
+ for providing authorization and/or authentication services to client
40
+ applications accessing HL7® FHIR® APIs. To get started, please first register
41
+ the Inferno client as a SMART App with the following information:
42
+
43
+ * SMART Launch URI: `#{config.options[:launch_uri]}`
44
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
45
+ DESCRIPTION
46
+
29
47
  group do
30
48
  title 'Standalone Launch'
31
49
  id :smart_full_standalone_launch
32
50
 
51
+ input_instructions <<~INSTRUCTIONS
52
+ Please register the Inferno client as a SMART App with the following
53
+ information:
54
+
55
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
56
+ INSTRUCTIONS
57
+
33
58
  run_as_group
34
59
 
35
60
  group from: :smart_discovery
@@ -92,6 +117,14 @@ module SMARTAppLaunch
92
117
  title 'EHR Launch'
93
118
  id :smart_full_ehr_launch
94
119
 
120
+ input_instructions <<~INSTRUCTIONS
121
+ Please register the Inferno client as a SMART App with the following
122
+ information:
123
+
124
+ * SMART Launch URI: `#{config.options[:launch_uri]}`
125
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
126
+ INSTRUCTIONS
127
+
95
128
  run_as_group
96
129
 
97
130
  group from: :smart_discovery
@@ -1,5 +1,6 @@
1
1
  require 'tls_test_kit'
2
2
 
3
+ require_relative 'jwks'
3
4
  require_relative 'version'
4
5
  require_relative 'discovery_stu2_group'
5
6
  require_relative 'standalone_launch_group_stu2'
@@ -21,6 +22,12 @@ module SMARTAppLaunch
21
22
  request.query_parameters['state']
22
23
  end
23
24
 
25
+ route(
26
+ :get,
27
+ '/.well-known/jwks.json',
28
+ ->(_env) { [200, { 'Content-Type' => 'application/json' }, [JWKS.jwks_json]] }
29
+ )
30
+
24
31
  @post_auth_page = File.read(File.join(__dir__, 'post_auth.html'))
25
32
  post_auth_handler = proc { [200, {}, [@post_auth_page]] }
26
33
 
@@ -32,10 +39,38 @@ module SMARTAppLaunch
32
39
  post_authorization_uri: "#{Inferno::Application['base_url']}/custom/smart_stu2/post_auth"
33
40
  }
34
41
 
42
+ description <<~DESCRIPTION
43
+ The SMART App Launch Test Suite verifies that systems correctly implement
44
+ the [SMART App Launch IG](http://hl7.org/fhir/smart-app-launch/STU2/)
45
+ for providing authorization and/or authentication services to client
46
+ applications accessing HL7® FHIR® APIs. To get started, please first register
47
+ the Inferno client as a SMART App with the following information:
48
+
49
+ * SMART Launch URI: `#{config.options[:launch_uri]}`
50
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
51
+
52
+ If using asymmetric client authentication, register Inferno with the
53
+ following JWK Set URL:
54
+
55
+ * `#{Inferno::Application[:base_url]}/custom/smart_stu2/.well-known/jwks.json`
56
+ DESCRIPTION
57
+
35
58
  group do
36
59
  title 'Standalone Launch'
37
60
  id :smart_full_standalone_launch
38
61
 
62
+ input_instructions <<~INSTRUCTIONS
63
+ Please register the Inferno client as a SMART App with the following
64
+ information:
65
+
66
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
67
+
68
+ If using asymmetric client authentication, register Inferno with the
69
+ following JWK Set URL:
70
+
71
+ * `#{Inferno::Application[:base_url]}/custom/smart_stu2/.well-known/jwks.json`
72
+ INSTRUCTIONS
73
+
39
74
  run_as_group
40
75
 
41
76
  group from: :smart_discovery_stu2
@@ -98,6 +133,19 @@ module SMARTAppLaunch
98
133
  title 'EHR Launch'
99
134
  id :smart_full_ehr_launch
100
135
 
136
+ input_instructions <<~INSTRUCTIONS
137
+ Please register the Inferno client as a SMART App with the following
138
+ information:
139
+
140
+ * SMART Launch URI: `#{config.options[:launch_uri]}`
141
+ * OAuth Redirect URI: `#{config.options[:redirect_uri]}`
142
+
143
+ If using asymmetric client authentication, register Inferno with the
144
+ following JWK Set URL:
145
+
146
+ * `#{Inferno::Application[:base_url]}/custom/smart_stu2/.well-known/jwks.json`
147
+ INSTRUCTIONS
148
+
101
149
  run_as_group
102
150
 
103
151
  group from: :smart_discovery_stu2
@@ -44,7 +44,8 @@ module SMARTAppLaunch
44
44
  client_secret: {
45
45
  name: :standalone_client_secret,
46
46
  title: 'Standalone Client Secret',
47
- description: 'Client Secret provided during registration of Inferno as a standalone application'
47
+ description: 'Client Secret provided during registration of Inferno as a standalone application. ' \
48
+ 'Only for clients using confidential symmetric authentication.'
48
49
  },
49
50
  requested_scopes: {
50
51
  name: :standalone_requested_scopes,
@@ -1,4 +1,5 @@
1
1
  require_relative 'app_redirect_test_stu2'
2
+ require_relative 'token_exchange_stu2_test'
2
3
  require_relative 'standalone_launch_group'
3
4
 
4
5
  module SMARTAppLaunch
@@ -48,5 +49,12 @@ module SMARTAppLaunch
48
49
 
49
50
  redirect_index = children.find_index { |child| child.id.to_s.end_with? 'app_redirect' }
50
51
  children[redirect_index] = children.pop
52
+
53
+ test from: :smart_token_exchange_stu2
54
+
55
+ token_exchange_index = children.find_index { |child| child.id.to_s.end_with? 'token_exchange' }
56
+ children[token_exchange_index] = children.pop
57
+
58
+ children[token_exchange_index].id('smart_token_exchange')
51
59
  end
52
60
  end
@@ -0,0 +1,91 @@
1
+ require_relative 'client_assertion_builder'
2
+ require_relative 'token_exchange_test'
3
+
4
+ module SMARTAppLaunch
5
+ class TokenExchangeSTU2Test < TokenExchangeTest
6
+ title 'OAuth token exchange request succeeds when supplied correct information'
7
+ description %(
8
+ After obtaining an authorization code, the app trades the code for an
9
+ access token via HTTP POST to the EHR authorization server's token
10
+ endpoint URL, using content-type application/x-www-form-urlencoded, as
11
+ described in section [4.1.3 of
12
+ RFC6749](https://tools.ietf.org/html/rfc6749#section-4.1.3).
13
+ )
14
+ id :smart_token_exchange_stu2
15
+
16
+ input :client_auth_encryption_method,
17
+ title: 'Encryption Method (Confidential Asymmetric Client Auth Only)',
18
+ type: 'radio',
19
+ default: 'ES384',
20
+ options: {
21
+ list_options: [
22
+ {
23
+ label: 'ES384',
24
+ value: 'ES384'
25
+ },
26
+ {
27
+ label: 'RS384',
28
+ value: 'RS384'
29
+ }
30
+ ]
31
+ }
32
+
33
+ input :client_auth_type,
34
+ title: 'Client Authentication Method',
35
+ type: 'radio',
36
+ options: {
37
+ list_options: [
38
+ {
39
+ label: 'Public',
40
+ value: 'public'
41
+ },
42
+ {
43
+ label: 'Confidential Symmetric',
44
+ value: 'confidential_symmetric'
45
+ },
46
+ {
47
+ label: 'Confidential Asymmetric',
48
+ value: 'confidential_asymmetric'
49
+ }
50
+ ]
51
+ }
52
+
53
+ config(
54
+ inputs: {
55
+ use_pkce: {
56
+ default: 'true',
57
+ options: {
58
+ list_options: [
59
+ {
60
+ label: 'Enabled',
61
+ value: 'true'
62
+ }
63
+ ]
64
+ }
65
+ }
66
+ }
67
+ )
68
+
69
+ def add_credentials_to_request(oauth2_params, oauth2_headers)
70
+ if client_auth_type == 'confidential_symmetric'
71
+ assert client_secret.present?,
72
+ "A client secret must be provided when using confidential symmetric client authentication."
73
+
74
+ client_credentials = "#{client_id}:#{client_secret}"
75
+ oauth2_headers['Authorization'] = "Basic #{Base64.strict_encode64(client_credentials)}"
76
+ elsif client_auth_type == 'public'
77
+ oauth2_params[:client_id] = client_id
78
+ else
79
+ oauth2_params.merge!(
80
+ client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
81
+ client_assertion: ClientAssertionBuilder.build(
82
+ iss: client_id,
83
+ sub: client_id,
84
+ aud: smart_token_url,
85
+ client_auth_encryption_method: client_auth_encryption_method
86
+ )
87
+ )
88
+ end
89
+ end
90
+ end
91
+ end
@@ -38,22 +38,26 @@ module SMARTAppLaunch
38
38
 
39
39
  config options: { redirect_uri: "#{Inferno::Application['base_url']}/custom/smart/redirect" }
40
40
 
41
+ def add_credentials_to_request(oauth2_params, oauth2_headers)
42
+ if client_secret.present?
43
+ client_credentials = "#{client_id}:#{client_secret}"
44
+ oauth2_headers['Authorization'] = "Basic #{Base64.strict_encode64(client_credentials)}"
45
+ else
46
+ oauth2_params[:client_id] = client_id
47
+ end
48
+ end
49
+
41
50
  run do
42
51
  skip_if request.query_parameters['error'].present?, 'Error during authorization request'
43
52
 
44
53
  oauth2_params = {
45
- grant_type: 'authorization_code',
46
54
  code: code,
47
- redirect_uri: config.options[:redirect_uri]
55
+ redirect_uri: config.options[:redirect_uri],
56
+ grant_type: 'authorization_code'
48
57
  }
49
58
  oauth2_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
50
59
 
51
- if client_secret.present?
52
- client_credentials = "#{client_id}:#{client_secret}"
53
- oauth2_headers['Authorization'] = "Basic #{Base64.strict_encode64(client_credentials)}"
54
- else
55
- oauth2_params[:client_id] = client_id
56
- end
60
+ add_credentials_to_request(oauth2_params, oauth2_headers)
57
61
 
58
62
  if use_pkce == 'true'
59
63
  oauth2_params[:code_verifier] = pkce_code_verifier
@@ -1,3 +1,3 @@
1
1
  module SMARTAppLaunch
2
- VERSION = '0.2.1'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
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.2.1
4
+ version: 0.3.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: 2023-01-05 00:00:00.000000000 Z
11
+ date: 2023-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inferno_core
@@ -134,11 +134,13 @@ files:
134
134
  - lib/smart_app_launch/app_launch_test.rb
135
135
  - lib/smart_app_launch/app_redirect_test.rb
136
136
  - lib/smart_app_launch/app_redirect_test_stu2.rb
137
+ - lib/smart_app_launch/client_assertion_builder.rb
137
138
  - lib/smart_app_launch/code_received_test.rb
138
139
  - lib/smart_app_launch/discovery_stu1_group.rb
139
140
  - lib/smart_app_launch/discovery_stu2_group.rb
140
141
  - lib/smart_app_launch/ehr_launch_group.rb
141
142
  - lib/smart_app_launch/ehr_launch_group_stu2.rb
143
+ - lib/smart_app_launch/jwks.rb
142
144
  - lib/smart_app_launch/launch_received_test.rb
143
145
  - lib/smart_app_launch/openid_connect_group.rb
144
146
  - lib/smart_app_launch/openid_decode_id_token_test.rb
@@ -149,10 +151,12 @@ files:
149
151
  - lib/smart_app_launch/openid_token_header_test.rb
150
152
  - lib/smart_app_launch/openid_token_payload_test.rb
151
153
  - lib/smart_app_launch/post_auth.html
154
+ - lib/smart_app_launch/smart_jwks.json
152
155
  - lib/smart_app_launch/smart_stu1_suite.rb
153
156
  - lib/smart_app_launch/smart_stu2_suite.rb
154
157
  - lib/smart_app_launch/standalone_launch_group.rb
155
158
  - lib/smart_app_launch/standalone_launch_group_stu2.rb
159
+ - lib/smart_app_launch/token_exchange_stu2_test.rb
156
160
  - lib/smart_app_launch/token_exchange_test.rb
157
161
  - lib/smart_app_launch/token_payload_validation.rb
158
162
  - lib/smart_app_launch/token_refresh_body_test.rb