smart_app_launch_test_kit 0.4.0 → 0.4.1
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/backend_services_authorization_group.rb +88 -0
- data/lib/smart_app_launch/backend_services_authorization_request_builder.rb +74 -0
- data/lib/smart_app_launch/backend_services_authorization_request_success_test.rb +40 -0
- data/lib/smart_app_launch/backend_services_authorization_response_body_test.rb +40 -0
- data/lib/smart_app_launch/backend_services_invalid_client_assertion_test.rb +44 -0
- data/lib/smart_app_launch/backend_services_invalid_grant_type_test.rb +44 -0
- data/lib/smart_app_launch/backend_services_invalid_jwt_test.rb +55 -0
- data/lib/smart_app_launch/client_assertion_builder.rb +18 -10
- data/lib/smart_app_launch/smart_stu2_suite.rb +18 -0
- data/lib/smart_app_launch/token_exchange_stu2_test.rb +14 -14
- data/lib/smart_app_launch/version.rb +1 -1
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f66c9b0314200d03d78422dc6412fe30cd0a091cd34d2a4701b9daa9a85d45f6
|
4
|
+
data.tar.gz: 7a34428293bfe281ebeebf83e8ee37597ebdfef2bf158cb8a3893598eca09876
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5f432c1b4b43a2c84be8613acb0b31dec557b80b1b76996773c76486db5197265a3dc1f0b9a9015eaa35c17c65d5b6b7eb506d940676e0cdae3b798b1cf8799
|
7
|
+
data.tar.gz: b63b86f90dbfc0ea88b7cc65e2075333b07a2cee7d42f88f21d209f4a51274545532706ed0278efad3cd4a0316be4ae32e948632f4ba66247d5451c68eff9559
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative 'backend_services_authorization_request_builder'
|
2
|
+
require_relative 'backend_services_invalid_grant_type_test'
|
3
|
+
require_relative 'backend_services_invalid_client_assertion_test'
|
4
|
+
require_relative 'backend_services_invalid_jwt_test'
|
5
|
+
require_relative 'backend_services_authorization_request_success_test'
|
6
|
+
require_relative 'backend_services_authorization_response_body_test'
|
7
|
+
require_relative 'token_exchange_stu2_test'
|
8
|
+
|
9
|
+
module SMARTAppLaunch
|
10
|
+
class BackendServicesAuthorizationGroup < Inferno::TestGroup
|
11
|
+
title 'SMART Backend Services Authorization'
|
12
|
+
short_description 'Demonstrate SMART Backend Services Authorization'
|
13
|
+
|
14
|
+
id :backend_services_authorization
|
15
|
+
|
16
|
+
input :smart_token_url,
|
17
|
+
title: 'Backend Services Token Endpoint',
|
18
|
+
description: <<~DESCRIPTION
|
19
|
+
The OAuth 2.0 Token Endpoint used by the Backend Services specification to provide bearer tokens.
|
20
|
+
DESCRIPTION
|
21
|
+
|
22
|
+
input :backend_services_client_id,
|
23
|
+
title: 'Backend Services Client ID',
|
24
|
+
description: 'Client ID provided at registration to the Inferno application.'
|
25
|
+
input :backend_services_requested_scope,
|
26
|
+
title: 'Backend Services Requested Scopes',
|
27
|
+
description: 'Backend Services Scopes provided at registration to the Inferno application; will be `system/` scopes',
|
28
|
+
default: 'system/*.read'
|
29
|
+
|
30
|
+
input :client_auth_encryption_method,
|
31
|
+
title: 'Encryption Method for Asymmetric Confidential Client Authorization',
|
32
|
+
description: <<~DESCRIPTION,
|
33
|
+
The server is required to suport either ES384 or RS384 encryption methods for JWT signature verification.
|
34
|
+
Select which method to use.
|
35
|
+
DESCRIPTION
|
36
|
+
type: 'radio',
|
37
|
+
default: 'ES384',
|
38
|
+
options: {
|
39
|
+
list_options: [
|
40
|
+
{
|
41
|
+
label: 'ES384',
|
42
|
+
value: 'ES384'
|
43
|
+
},
|
44
|
+
{
|
45
|
+
label: 'RS384',
|
46
|
+
value: 'RS384'
|
47
|
+
}
|
48
|
+
]
|
49
|
+
}
|
50
|
+
input :backend_services_jwks_kid,
|
51
|
+
title: 'Backend Services JWKS kid',
|
52
|
+
description: <<~DESCRIPTION,
|
53
|
+
The key ID of the JWKS private key to use for signing the client assertion when fetching an auth token.
|
54
|
+
Defaults to the first JWK in the list if no kid is supplied.
|
55
|
+
DESCRIPTION
|
56
|
+
optional: true
|
57
|
+
|
58
|
+
output :bearer_token
|
59
|
+
|
60
|
+
test from: :tls_version_test do
|
61
|
+
title 'Authorization service token endpoint secured by transport layer security'
|
62
|
+
description <<~DESCRIPTION
|
63
|
+
The [SMART App Launch 2.0.0 IG specification for Backend Services](https://hl7.org/fhir/smart-app-launch/STU2/backend-services.html#request-1)
|
64
|
+
states "the client SHALL use the Transport Layer Security (TLS) Protocol Version 1.2 (RFC5246)
|
65
|
+
or a more recent version of TLS to authenticate the identity of the FHIR authorization server and to
|
66
|
+
establish an encrypted, integrity-protected link for securing all exchanges between the client and the
|
67
|
+
FHIR authorization server’s token endpoint. All exchanges described herein between the client and the
|
68
|
+
FHIR server SHALL be secured using TLS V1.2 or a more recent version of TLS."
|
69
|
+
DESCRIPTION
|
70
|
+
id :smart_backend_services_token_tls_version
|
71
|
+
|
72
|
+
config(
|
73
|
+
inputs: { url: { name: :smart_token_url } },
|
74
|
+
options: { minimum_allowed_version: OpenSSL::SSL::TLS1_2_VERSION }
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
test from: :smart_backend_services_invalid_grant_type
|
79
|
+
|
80
|
+
test from: :smart_backend_services_invalid_client_assertion
|
81
|
+
|
82
|
+
test from: :smart_backend_services_invalid_jwt
|
83
|
+
|
84
|
+
test from: :smart_backend_services_auth_request_success
|
85
|
+
|
86
|
+
test from: :smart_backend_services_auth_response_body
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'json/jwt'
|
2
|
+
require_relative 'client_assertion_builder'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class BackendServicesAuthorizationRequestBuilder
|
6
|
+
def self.build(...)
|
7
|
+
new(...).authorization_request
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :encryption_method, :scope, :iss, :sub, :aud, :content_type, :grant_type, :client_assertion_type, :exp,
|
11
|
+
:jti, :kid
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
encryption_method:,
|
15
|
+
scope:,
|
16
|
+
iss:,
|
17
|
+
sub:,
|
18
|
+
aud:,
|
19
|
+
content_type: 'application/x-www-form-urlencoded',
|
20
|
+
grant_type: 'client_credentials',
|
21
|
+
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
|
22
|
+
exp: 5.minutes.from_now,
|
23
|
+
jti: SecureRandom.hex(32),
|
24
|
+
kid: nil
|
25
|
+
)
|
26
|
+
@encryption_method = encryption_method
|
27
|
+
@scope = scope
|
28
|
+
@iss = iss
|
29
|
+
@sub = sub
|
30
|
+
@aud = aud
|
31
|
+
@content_type = content_type
|
32
|
+
@grant_type = grant_type
|
33
|
+
@client_assertion_type = client_assertion_type
|
34
|
+
@exp = exp
|
35
|
+
@jti = jti
|
36
|
+
@kid = kid
|
37
|
+
end
|
38
|
+
|
39
|
+
def authorization_request_headers
|
40
|
+
{
|
41
|
+
content_type:,
|
42
|
+
accept: 'application/json'
|
43
|
+
}.compact
|
44
|
+
end
|
45
|
+
|
46
|
+
def authorization_request_query_values
|
47
|
+
{
|
48
|
+
'scope' => scope,
|
49
|
+
'grant_type' => grant_type,
|
50
|
+
'client_assertion_type' => client_assertion_type,
|
51
|
+
'client_assertion' => client_assertion.to_s
|
52
|
+
}.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def client_assertion
|
56
|
+
@client_assertion ||= ClientAssertionBuilder.build(
|
57
|
+
client_auth_encryption_method: encryption_method,
|
58
|
+
iss: iss,
|
59
|
+
sub: sub,
|
60
|
+
aud: aud,
|
61
|
+
exp: exp.to_i,
|
62
|
+
jti: jti,
|
63
|
+
kid: kid
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def authorization_request
|
68
|
+
uri = Addressable::URI.new
|
69
|
+
uri.query_values = authorization_request_query_values
|
70
|
+
|
71
|
+
{ body: uri.query, headers: authorization_request_headers }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'backend_services_authorization_request_builder'
|
2
|
+
require_relative 'backend_services_authorization_group'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class BackendServicesAuthorizationRequestSuccessTest < Inferno::Test
|
6
|
+
id :smart_backend_services_auth_request_success
|
7
|
+
title 'Authorization request succeeds when supplied correct information'
|
8
|
+
description <<~DESCRIPTION
|
9
|
+
The [SMART App Launch 2.0.0 IG specification for Backend Services](https://hl7.org/fhir/smart-app-launch/STU2/backend-services.html#issue-access-token)
|
10
|
+
states "If the access token request is valid and authorized, the authorization server SHALL issue an access token in response."
|
11
|
+
DESCRIPTION
|
12
|
+
|
13
|
+
input :client_auth_encryption_method,
|
14
|
+
:backend_services_requested_scope,
|
15
|
+
:backend_services_client_id,
|
16
|
+
:smart_token_url,
|
17
|
+
:backend_services_jwks_kid
|
18
|
+
|
19
|
+
output :authentication_response
|
20
|
+
|
21
|
+
http_client :token_endpoint do
|
22
|
+
url :smart_token_url
|
23
|
+
end
|
24
|
+
|
25
|
+
run do
|
26
|
+
post_request_content = BackendServicesAuthorizationRequestBuilder.build(encryption_method: client_auth_encryption_method,
|
27
|
+
scope: backend_services_requested_scope,
|
28
|
+
iss: backend_services_client_id,
|
29
|
+
sub: backend_services_client_id,
|
30
|
+
aud: smart_token_url,
|
31
|
+
kid: backend_services_jwks_kid)
|
32
|
+
|
33
|
+
authentication_response = post(**{ client: :token_endpoint }.merge(post_request_content))
|
34
|
+
|
35
|
+
assert_response_status([200, 201])
|
36
|
+
|
37
|
+
output authentication_response: authentication_response.response_body
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative 'backend_services_authorization_request_builder'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class BackendServicesAuthorizationResponseBodyTest < Inferno::Test
|
5
|
+
id :smart_backend_services_auth_response_body
|
6
|
+
title 'Authorization request response body contains required information encoded in JSON'
|
7
|
+
description <<~DESCRIPTION
|
8
|
+
The [SMART App Launch 2.0.0 IG specification for Backend Services](https://hl7.org/fhir/smart-app-launch/STU2/backend-services.html#issue-access-token)
|
9
|
+
states The access token response SHALL be a JSON object with the following properties:
|
10
|
+
|
11
|
+
| Token Property | Required? | Description |
|
12
|
+
| --- | --- | --- |
|
13
|
+
| `access_token` | required | The access token issued by the authorization server. |
|
14
|
+
| `token_type` | required | Fixed value: `bearer`. |
|
15
|
+
| `expires_in` | required | The lifetime in seconds of the access token. The recommended value is `300`, for a five-minute token lifetime. |
|
16
|
+
| `scope` | required | Scope of access authorized. Note that this can be different from the scopes requested by the app. |
|
17
|
+
DESCRIPTION
|
18
|
+
|
19
|
+
input :authentication_response
|
20
|
+
output :bearer_token
|
21
|
+
|
22
|
+
run do
|
23
|
+
skip_if authentication_response.blank?, 'No authentication response received.'
|
24
|
+
|
25
|
+
assert_valid_json(authentication_response)
|
26
|
+
response_body = JSON.parse(authentication_response)
|
27
|
+
|
28
|
+
access_token = response_body['access_token']
|
29
|
+
assert access_token.present?, 'Token response did not contain access_token as required'
|
30
|
+
|
31
|
+
output bearer_token: access_token
|
32
|
+
|
33
|
+
required_keys = ['token_type', 'expires_in', 'scope']
|
34
|
+
|
35
|
+
required_keys.each do |key|
|
36
|
+
assert response_body[key].present?, "Token response did not contain #{key} as required"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'backend_services_authorization_request_builder'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class BackendServicesInvalidClientAssertionTest < Inferno::Test
|
5
|
+
id :smart_backend_services_invalid_client_assertion
|
6
|
+
title 'Authorization request fails when supplied invalid client_assertion_type'
|
7
|
+
description <<~DESCRIPTION
|
8
|
+
The [SMART App Launch 2.0.0 IG specification for Backend Services](https://hl7.org/fhir/smart-app-launch/STU2/backend-services.html#request-1)
|
9
|
+
defines the required fields for the authorization request, made via HTTP POST to authorization
|
10
|
+
token endpoint.
|
11
|
+
This includes the `client_assertion_type` parameter, where the value must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`.
|
12
|
+
|
13
|
+
The [OAuth 2.0 Authorization Framework Section 4.3.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3.3)
|
14
|
+
describes the proper response for an invalid request in the client credentials grant flow:
|
15
|
+
|
16
|
+
"If the request failed client authentication or is invalid, the authorization server returns an
|
17
|
+
error response as described in [Section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2)."
|
18
|
+
DESCRIPTION
|
19
|
+
|
20
|
+
input :client_auth_encryption_method,
|
21
|
+
:backend_services_requested_scope,
|
22
|
+
:backend_services_client_id,
|
23
|
+
:smart_token_url,
|
24
|
+
:backend_services_jwks_kid
|
25
|
+
|
26
|
+
http_client :token_endpoint do
|
27
|
+
url :smart_token_url
|
28
|
+
end
|
29
|
+
|
30
|
+
run do
|
31
|
+
post_request_content = BackendServicesAuthorizationRequestBuilder.build(encryption_method: client_auth_encryption_method,
|
32
|
+
scope: backend_services_requested_scope,
|
33
|
+
iss: backend_services_client_id,
|
34
|
+
sub: backend_services_client_id,
|
35
|
+
aud: smart_token_url,
|
36
|
+
client_assertion_type: 'not_an_assertion_type',
|
37
|
+
kid: backend_services_jwks_kid)
|
38
|
+
|
39
|
+
post(**{ client: :token_endpoint }.merge(post_request_content))
|
40
|
+
|
41
|
+
assert_response_status(400)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'backend_services_authorization_request_builder'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class BackendServicesInvalidGrantTypeTest < Inferno::Test
|
5
|
+
id :smart_backend_services_invalid_grant_type
|
6
|
+
title 'Authorization request fails when client supplies invalid grant_type'
|
7
|
+
description <<~DESCRIPTION
|
8
|
+
The [SMART App Launch 2.0.0 IG section on Backend Services](https://hl7.org/fhir/smart-app-launch/STU2/backend-services.html#request-1)
|
9
|
+
defines the required fields for the authorization request, made via HTTP POST to authorization
|
10
|
+
token endpoint.
|
11
|
+
This includes the `grant_type` parameter, where the value must be `client_credentials`.
|
12
|
+
|
13
|
+
The [OAuth 2.0 Authorization Framework Section 4.3.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3.3)
|
14
|
+
describes the proper response for an invalid request in the client credentials grant flow:
|
15
|
+
|
16
|
+
"If the request failed client authentication or is invalid, the authorization server returns an
|
17
|
+
error response as described in [Section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2)."
|
18
|
+
DESCRIPTION
|
19
|
+
|
20
|
+
input :client_auth_encryption_method,
|
21
|
+
:backend_services_requested_scope,
|
22
|
+
:backend_services_client_id,
|
23
|
+
:smart_token_url,
|
24
|
+
:backend_services_jwks_kid
|
25
|
+
|
26
|
+
http_client :token_endpoint do
|
27
|
+
url :smart_token_url
|
28
|
+
end
|
29
|
+
|
30
|
+
run do
|
31
|
+
post_request_content = BackendServicesAuthorizationRequestBuilder.build(encryption_method: client_auth_encryption_method,
|
32
|
+
scope: backend_services_requested_scope,
|
33
|
+
iss: backend_services_client_id,
|
34
|
+
sub: backend_services_client_id,
|
35
|
+
aud: smart_token_url,
|
36
|
+
grant_type: 'not_a_grant_type',
|
37
|
+
kid: backend_services_jwks_kid)
|
38
|
+
|
39
|
+
post(**{ client: :token_endpoint }.merge(post_request_content))
|
40
|
+
|
41
|
+
assert_response_status(400)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'backend_services_authorization_request_builder'
|
2
|
+
|
3
|
+
module SMARTAppLaunch
|
4
|
+
class BackendServicesInvalidJWTTest < Inferno::Test
|
5
|
+
id :smart_backend_services_invalid_jwt
|
6
|
+
title 'Authorization request fails when client supplies invalid JWT token'
|
7
|
+
description <<~DESCRIPTION
|
8
|
+
The [SMART App Launch 2.0.0 IG section on Backend Services](https://hl7.org/fhir/smart-app-launch/STU2/backend-services.html#request-1)
|
9
|
+
defines the required fields for the authorization request, made via HTTP POST to authorization
|
10
|
+
token endpoint.
|
11
|
+
This includes the `client_assertion` parameter, where the value must be
|
12
|
+
a valid JWT as specified in
|
13
|
+
[Asymmetric (public key) Client Authentication](https://hl7.org/fhir/smart-app-launch/STU2/client-confidential-asymmetric.html#authenticating-to-the-token-endpoint)
|
14
|
+
The JWT SHALL include the following claims, and SHALL be signed with the client’s private key.
|
15
|
+
|
16
|
+
| JWT Claim | Required? | Description |
|
17
|
+
| --- | --- | --- |
|
18
|
+
| `iss` | required | Issuer of the JWT -- the client's `client_id`, as determined during registration with the FHIR authorization server (note that this is the same as the value for the sub claim) |
|
19
|
+
| `sub` | required | The service's `client_id`, as determined during registration with the FHIR authorization server (note that this is the same as the value for the `iss` claim) |
|
20
|
+
| `aud` | required | The FHIR authorization server's "token URL" (the same URL to which this authentication JWT will be posted) |
|
21
|
+
| `exp` | required | Expiration time integer for this authentication JWT, expressed in seconds since the "Epoch" (1970-01-01T00:00:00Z UTC). This time SHALL be no more than five minutes in the future. |
|
22
|
+
| `jti` | required | A nonce string value that uniquely identifies this authentication JWT. |
|
23
|
+
|
24
|
+
The [OAuth 2.0 Authorization Framework Section 4.3.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3.3)
|
25
|
+
describes the proper response for an invalid request in the client credentials grant flow:
|
26
|
+
|
27
|
+
"If the request failed client authentication or is invalid, the authorization server returns an
|
28
|
+
error response as described in [Section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2)."
|
29
|
+
DESCRIPTION
|
30
|
+
|
31
|
+
input :client_auth_encryption_method,
|
32
|
+
:backend_services_requested_scope,
|
33
|
+
:backend_services_client_id,
|
34
|
+
:smart_token_url,
|
35
|
+
:backend_services_jwks_kid
|
36
|
+
|
37
|
+
http_client :token_endpoint do
|
38
|
+
url :smart_token_url
|
39
|
+
end
|
40
|
+
|
41
|
+
run do
|
42
|
+
post_request_content = BackendServicesAuthorizationRequestBuilder.build(encryption_method: client_auth_encryption_method,
|
43
|
+
scope: backend_services_requested_scope,
|
44
|
+
iss: backend_services_client_id,
|
45
|
+
sub: backend_services_client_id,
|
46
|
+
aud: smart_token_url,
|
47
|
+
client_assertion_type: 'not_an_assertion_type',
|
48
|
+
kid: backend_services_jwks_kid)
|
49
|
+
|
50
|
+
post(**{ client: :token_endpoint }.merge(post_request_content))
|
51
|
+
|
52
|
+
assert_response_status(400)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -16,7 +16,8 @@ module SMARTAppLaunch
|
|
16
16
|
:grant_type,
|
17
17
|
:iss,
|
18
18
|
:jti,
|
19
|
-
:sub
|
19
|
+
:sub,
|
20
|
+
:kid
|
20
21
|
|
21
22
|
def initialize(
|
22
23
|
client_auth_encryption_method:,
|
@@ -24,7 +25,8 @@ module SMARTAppLaunch
|
|
24
25
|
sub:,
|
25
26
|
aud:,
|
26
27
|
exp: 5.minutes.from_now.to_i,
|
27
|
-
jti: SecureRandom.hex(32)
|
28
|
+
jti: SecureRandom.hex(32),
|
29
|
+
kid: nil
|
28
30
|
)
|
29
31
|
@client_auth_encryption_method = client_auth_encryption_method
|
30
32
|
@iss = iss
|
@@ -35,29 +37,35 @@ module SMARTAppLaunch
|
|
35
37
|
@client_assertion_type = client_assertion_type
|
36
38
|
@exp = exp
|
37
39
|
@jti = jti
|
40
|
+
@kid = kid
|
38
41
|
end
|
39
42
|
|
40
43
|
def private_key
|
41
|
-
@private_key ||=
|
42
|
-
|
43
|
-
|
44
|
+
@private_key ||= JWKS.jwks
|
45
|
+
.select { |key| key[:key_ops]&.include?('sign') }
|
46
|
+
.select { |key| key[:alg] == client_auth_encryption_method }
|
47
|
+
.find { |key| !kid || key[:kid] == kid }
|
44
48
|
end
|
45
49
|
|
46
50
|
def jwt_payload
|
47
51
|
{ iss:, sub:, aud:, exp:, jti: }.compact
|
48
52
|
end
|
49
53
|
|
50
|
-
def
|
51
|
-
private_key
|
54
|
+
def signing_key
|
55
|
+
private_key()
|
56
|
+
if @private_key.nil?
|
57
|
+
raise Inferno::Exceptions::AssertionException, "No signing key found for inputs: encryption method = '#{client_auth_encryption_method}' and kid = '#{kid}'"
|
58
|
+
end
|
59
|
+
return @private_key.signing_key
|
52
60
|
end
|
53
61
|
|
54
|
-
def
|
55
|
-
private_key
|
62
|
+
def key_id
|
63
|
+
@private_key['kid']
|
56
64
|
end
|
57
65
|
|
58
66
|
def client_assertion
|
59
67
|
@client_assertion ||=
|
60
|
-
JWT.encode jwt_payload, signing_key, client_auth_encryption_method, { alg: client_auth_encryption_method, kid
|
68
|
+
JWT.encode jwt_payload, signing_key, client_auth_encryption_method, { alg: client_auth_encryption_method, kid: key_id, typ: 'JWT' }
|
61
69
|
end
|
62
70
|
end
|
63
71
|
end
|
@@ -8,6 +8,7 @@ require_relative 'ehr_launch_group_stu2'
|
|
8
8
|
require_relative 'openid_connect_group'
|
9
9
|
require_relative 'token_introspection_group'
|
10
10
|
require_relative 'token_refresh_group'
|
11
|
+
require_relative 'backend_services_authorization_group'
|
11
12
|
|
12
13
|
module SMARTAppLaunch
|
13
14
|
class SMARTSTU2Suite < Inferno::TestSuite
|
@@ -220,6 +221,23 @@ module SMARTAppLaunch
|
|
220
221
|
}
|
221
222
|
end
|
222
223
|
|
224
|
+
group do
|
225
|
+
title 'Backend Services'
|
226
|
+
id :smart_backend_services
|
227
|
+
|
228
|
+
input_instructions <<~INSTRUCTIONS
|
229
|
+
Please register the Inferno client with the authorization services with the
|
230
|
+
following JWK Set URL:
|
231
|
+
|
232
|
+
* `#{Inferno::Application[:base_url]}/custom/smart_stu2/.well-known/jwks.json`
|
233
|
+
INSTRUCTIONS
|
234
|
+
|
235
|
+
run_as_group
|
236
|
+
|
237
|
+
group from: :smart_discovery_stu2
|
238
|
+
group from: :backend_services_authorization
|
239
|
+
end
|
240
|
+
|
223
241
|
group from: :smart_token_introspection
|
224
242
|
|
225
243
|
end
|
@@ -14,21 +14,21 @@ module SMARTAppLaunch
|
|
14
14
|
id :smart_token_exchange_stu2
|
15
15
|
|
16
16
|
input :client_auth_encryption_method,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
30
|
-
]
|
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'
|
31
29
|
}
|
30
|
+
]
|
31
|
+
}
|
32
32
|
|
33
33
|
input :client_auth_type,
|
34
34
|
title: 'Client Authentication Method',
|
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.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen MacVicar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inferno_core
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.4.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json-jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.15.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.15.3
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: jwt
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +148,13 @@ files:
|
|
134
148
|
- lib/smart_app_launch/app_launch_test.rb
|
135
149
|
- lib/smart_app_launch/app_redirect_test.rb
|
136
150
|
- lib/smart_app_launch/app_redirect_test_stu2.rb
|
151
|
+
- lib/smart_app_launch/backend_services_authorization_group.rb
|
152
|
+
- lib/smart_app_launch/backend_services_authorization_request_builder.rb
|
153
|
+
- lib/smart_app_launch/backend_services_authorization_request_success_test.rb
|
154
|
+
- lib/smart_app_launch/backend_services_authorization_response_body_test.rb
|
155
|
+
- lib/smart_app_launch/backend_services_invalid_client_assertion_test.rb
|
156
|
+
- lib/smart_app_launch/backend_services_invalid_grant_type_test.rb
|
157
|
+
- lib/smart_app_launch/backend_services_invalid_jwt_test.rb
|
137
158
|
- lib/smart_app_launch/client_assertion_builder.rb
|
138
159
|
- lib/smart_app_launch/code_received_test.rb
|
139
160
|
- lib/smart_app_launch/discovery_stu1_group.rb
|