smart_app_launch_test_kit 0.4.0 → 0.4.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.
- 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
|