smart_app_launch_test_kit 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/smart_app_launch/app_redirect_test.rb +2 -0
- data/lib/smart_app_launch/client_assertion_builder.rb +63 -0
- data/lib/smart_app_launch/ehr_launch_group.rb +2 -1
- data/lib/smart_app_launch/ehr_launch_group_stu2.rb +8 -0
- data/lib/smart_app_launch/jwks.rb +27 -0
- data/lib/smart_app_launch/smart_jwks.json +58 -0
- data/lib/smart_app_launch/smart_stu1_suite.rb +33 -0
- data/lib/smart_app_launch/smart_stu2_suite.rb +48 -0
- data/lib/smart_app_launch/standalone_launch_group.rb +2 -1
- data/lib/smart_app_launch/standalone_launch_group_stu2.rb +8 -0
- data/lib/smart_app_launch/token_exchange_stu2_test.rb +91 -0
- data/lib/smart_app_launch/token_exchange_test.rb +12 -8
- data/lib/smart_app_launch/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97f3e4dca10ecb9dfe4aa30802d90f4272dd32ffd10715fa7b3a0855a04b5c8d
|
4
|
+
data.tar.gz: 016a700551ef524e7bdc7f8871beb03f7297dc3cba39e5b4b53349b22f22ed53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 519d2bb8c5bde6a7c5c28f04ec17bfdcff37c7270b6216707a05db4b04c94269d244c6a1888dd97a0c25c5a50764d4969a08c2774dcb636db7fd274301184427
|
7
|
+
data.tar.gz: eda1f5484f72de3a82c34eb1d0424a2b10a327baeb93d6e6df8f94be7a7e3ef378e5e6510378f62e7d9387e56424b42b52f4524fc557302d3a5ddab0654c782e
|
@@ -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
|
@@ -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
|
@@ -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
|
-
|
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
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_app_launch_test_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.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-
|
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
|