smart_app_launch_test_kit 0.1.0 → 0.1.3
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/app_redirect_test.rb +24 -17
- data/lib/smart_app_launch/discovery_group.rb +2 -2
- data/lib/smart_app_launch/ehr_launch_group.rb +2 -2
- data/lib/smart_app_launch/openid_connect_group.rb +4 -4
- data/lib/smart_app_launch/openid_required_configuration_fields_test.rb +1 -1
- data/lib/smart_app_launch/openid_token_header_test.rb +2 -2
- data/lib/smart_app_launch/openid_token_payload_test.rb +6 -1
- data/lib/smart_app_launch/standalone_launch_group.rb +3 -3
- data/lib/smart_app_launch/token_payload_validation.rb +6 -0
- data/lib/smart_app_launch/token_refresh_body_test.rb +3 -2
- data/lib/smart_app_launch/token_refresh_group.rb +3 -3
- data/lib/smart_app_launch/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 758773637de38a995aa7943da1291685f655e97f829c2e11d7adfc54d129ad42
|
4
|
+
data.tar.gz: b0c2adfcf695418617999e414a0d85766e440e943787d811ffb54218810fee2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b9aabc2d05bf3b3a3fe49f35fe0be2e634f7c774d86d3d6a0129aa1cd424f1c5cebd03c84c24855445a0baf95ac23c730d614e9f628a946b5ac8d5ca0b6f8fa
|
7
|
+
data.tar.gz: b287eaf62940d60d904af996c0cbbd90d59ba68ce79cdbe6400a20515b9eccb6aa0ff6c094bd07cfd86b928a11c3b6e7e740c896d125c62f5da65a857450ed38
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
1
3
|
module SMARTAppLaunch
|
2
4
|
class AppRedirectTest < Inferno::Test
|
3
5
|
title 'OAuth server redirects client browser to app redirect URI'
|
@@ -63,8 +65,18 @@ module SMARTAppLaunch
|
|
63
65
|
)
|
64
66
|
end
|
65
67
|
|
68
|
+
def authorization_url_builder(url, params)
|
69
|
+
uri = URI(url)
|
70
|
+
|
71
|
+
# because the URL might have paramters on it
|
72
|
+
original_parameters = Hash[URI.decode_www_form(uri.query || '')]
|
73
|
+
new_params = original_parameters.merge(params)
|
74
|
+
|
75
|
+
uri.query = URI.encode_www_form(new_params)
|
76
|
+
uri.to_s
|
77
|
+
end
|
78
|
+
|
66
79
|
run do
|
67
|
-
info(config.options[:redirect_uri])
|
68
80
|
assert_valid_http_uri(
|
69
81
|
smart_authorization_url,
|
70
82
|
"OAuth2 Authorization Endpoint '#{smart_authorization_url}' is not a valid URI"
|
@@ -81,10 +93,15 @@ module SMARTAppLaunch
|
|
81
93
|
'aud' => aud
|
82
94
|
}
|
83
95
|
|
84
|
-
|
96
|
+
if config.options[:launch]
|
97
|
+
oauth2_params['launch'] = config.options[:launch]
|
98
|
+
elsif self.class.inputs.include?(:launch)
|
99
|
+
oauth2_params['launch'] = launch
|
100
|
+
end
|
85
101
|
|
86
102
|
if use_pkce == 'true'
|
87
|
-
|
103
|
+
# code verifier must be between 43 and 128 characters
|
104
|
+
code_verifier = SecureRandom.uuid + '-' + SecureRandom.uuid
|
88
105
|
code_challenge =
|
89
106
|
if pkce_code_challenge_method == 'S256'
|
90
107
|
self.class.calculate_s256_challenge(code_verifier)
|
@@ -97,20 +114,10 @@ module SMARTAppLaunch
|
|
97
114
|
oauth2_params.merge!('code_challenge' => code_challenge, 'code_challenge_method' => pkce_code_challenge_method)
|
98
115
|
end
|
99
116
|
|
100
|
-
authorization_url =
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
'&'
|
105
|
-
else
|
106
|
-
'?'
|
107
|
-
end
|
108
|
-
|
109
|
-
oauth2_params.each do |key, value|
|
110
|
-
authorization_url += "#{key}=#{CGI.escape(value)}&"
|
111
|
-
end
|
112
|
-
|
113
|
-
authorization_url.chomp!('&')
|
117
|
+
authorization_url = authorization_url_builder(
|
118
|
+
smart_authorization_url,
|
119
|
+
oauth2_params
|
120
|
+
)
|
114
121
|
|
115
122
|
wait(
|
116
123
|
identifier: state,
|
@@ -8,7 +8,7 @@ module SMARTAppLaunch
|
|
8
8
|
|
9
9
|
The #{title} Sequence test looks for authorization endpoints and SMART
|
10
10
|
capabilities as described by the [SMART App Launch
|
11
|
-
Framework](
|
11
|
+
Framework](https://www.hl7.org/fhir/smart-app-launch/1.0.0/conformance/index.html).
|
12
12
|
The SMART launch framework uses OAuth 2.0 to *authorize* apps, like
|
13
13
|
Inferno, to access certain information on a FHIR server. The
|
14
14
|
authorization service accessed at the endpoint allows users to give
|
@@ -31,7 +31,7 @@ module SMARTAppLaunch
|
|
31
31
|
|
32
32
|
For more information see:
|
33
33
|
|
34
|
-
* [SMART App Launch Framework](
|
34
|
+
* [SMART App Launch Framework](https://www.hl7.org/fhir/smart-app-launch/1.0.0/conformance/index.html)
|
35
35
|
* [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
36
36
|
* [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html)
|
37
37
|
)
|
@@ -16,7 +16,7 @@ module SMARTAppLaunch
|
|
16
16
|
# Background
|
17
17
|
|
18
18
|
The [EHR
|
19
|
-
Launch](
|
19
|
+
Launch](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html#ehr-launch-sequence)
|
20
20
|
is one of two ways in which an app can be launched, the other being
|
21
21
|
Standalone launch. In an EHR launch, the app is launched from an
|
22
22
|
existing EHR session or portal by a redirect to the registered launch
|
@@ -35,7 +35,7 @@ module SMARTAppLaunch
|
|
35
35
|
|
36
36
|
For more information on the #{title} see:
|
37
37
|
|
38
|
-
* [SMART EHR Launch Sequence](
|
38
|
+
* [SMART EHR Launch Sequence](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html#ehr-launch-sequence)
|
39
39
|
)
|
40
40
|
|
41
41
|
config(
|
@@ -18,7 +18,7 @@ module SMARTAppLaunch
|
|
18
18
|
|
19
19
|
OpenID Connect (OIDC) provides the ability to verify the identity of the
|
20
20
|
authorizing user. Within the [SMART App Launch
|
21
|
-
Framework](
|
21
|
+
Framework](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html), Applications can
|
22
22
|
request an `id_token` be provided with by including the `openid fhirUser`
|
23
23
|
scopes when requesting authorization.
|
24
24
|
|
@@ -36,9 +36,9 @@ module SMARTAppLaunch
|
|
36
36
|
|
37
37
|
For more information see:
|
38
38
|
|
39
|
-
* [SMART App Launch Framework](
|
40
|
-
* [Scopes for requesting identity data](
|
41
|
-
* [Apps Requesting Authorization](
|
39
|
+
* [SMART App Launch Framework](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html)
|
40
|
+
* [Scopes for requesting identity data](https://www.hl7.org/fhir/smart-app-launch/1.0.0/scopes-and-launch-context/index.html#scopes-for-requesting-identity-data)
|
41
|
+
* [Apps Requesting Authorization](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html#step-1-app-asks-for-authorization)
|
42
42
|
* [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html)
|
43
43
|
)
|
44
44
|
|
@@ -9,7 +9,7 @@ module SMARTAppLaunch
|
|
9
9
|
`id_token_signing_alg_values_supported`.
|
10
10
|
|
11
11
|
Additionally, the [SMART App Launch
|
12
|
-
Framework](
|
12
|
+
Framework](https://www.hl7.org/fhir/smart-app-launch/1.0.0/scopes-and-launch-context/index.html#scopes-for-requesting-identity-data)
|
13
13
|
requires that the RSA SHA-256 signing algorithm be supported.
|
14
14
|
)
|
15
15
|
|
@@ -3,9 +3,9 @@ module SMARTAppLaunch
|
|
3
3
|
id :smart_openid_token_header
|
4
4
|
title 'ID token header contains required information'
|
5
5
|
description %(
|
6
|
-
Verify that the id token header indicates that the
|
6
|
+
Verify that the id token header indicates that the token is signed using
|
7
7
|
RSA SHA-256 [as required by the SMART app launch
|
8
|
-
framework](
|
8
|
+
framework](https://www.hl7.org/fhir/smart-app-launch/1.0.0/scopes-and-launch-context/index.html#scopes-for-requesting-identity-data)
|
9
9
|
and that the key used to sign the token can be identified in the JWKS.
|
10
10
|
)
|
11
11
|
|
@@ -13,6 +13,7 @@ module SMARTAppLaunch
|
|
13
13
|
configuration
|
14
14
|
- `aud` must match the client ID
|
15
15
|
- `exp` must represent a time in the future
|
16
|
+
- `sub` must be a non-blank string not exceeding 255 characters in length
|
16
17
|
)
|
17
18
|
|
18
19
|
REQUIRED_CLAIMS = ['iss', 'sub', 'aud', 'exp', 'iat'].freeze
|
@@ -47,7 +48,7 @@ module SMARTAppLaunch
|
|
47
48
|
verify_not_before: false,
|
48
49
|
verify_iat: false,
|
49
50
|
verify_jti: false,
|
50
|
-
verify_sub:
|
51
|
+
verify_sub: true,
|
51
52
|
verify_iss: true,
|
52
53
|
verify_aud: true
|
53
54
|
)
|
@@ -55,6 +56,10 @@ module SMARTAppLaunch
|
|
55
56
|
assert false, "Token validation error: #{e.message}"
|
56
57
|
end
|
57
58
|
|
59
|
+
sub_value = payload['sub']
|
60
|
+
assert !sub_value.blank?, "ID token `sub` claim is blank"
|
61
|
+
assert sub_value.length < 256, "ID token `sub` claim exceeds 255 characters in length"
|
62
|
+
|
58
63
|
missing_claims = required_claims - payload.keys
|
59
64
|
missing_claims_string = missing_claims.map { |claim| "`#{claim}`" }.join(', ')
|
60
65
|
|
@@ -14,8 +14,8 @@ module SMARTAppLaunch
|
|
14
14
|
# Background
|
15
15
|
|
16
16
|
The [Standalone
|
17
|
-
Launch](
|
18
|
-
|
17
|
+
Launch Sequence](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html#standalone-launch-sequence)
|
18
|
+
allows an app, like Inferno, to be launched independent of an
|
19
19
|
existing EHR session. It is one of the two launch methods described in
|
20
20
|
the SMART App Launch Framework alongside EHR Launch. The app will
|
21
21
|
request authorization for the provided scope from the authorization
|
@@ -31,7 +31,7 @@ module SMARTAppLaunch
|
|
31
31
|
|
32
32
|
For more information on the #{title}:
|
33
33
|
|
34
|
-
* [Standalone Launch Sequence](
|
34
|
+
* [Standalone Launch Sequence](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html#standalone-launch-sequence)
|
35
35
|
)
|
36
36
|
|
37
37
|
config(
|
@@ -28,6 +28,12 @@ module SMARTAppLaunch
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
def validate_scope_subset(received_scopes, original_scopes)
|
32
|
+
extra_scopes = received_scopes.split - original_scopes.split
|
33
|
+
assert extra_scopes.empty?, "Token response contained scopes which are not a subset of the scope granted to the "\
|
34
|
+
"original access token: #{extra_scopes.join(', ')}"
|
35
|
+
end
|
36
|
+
|
31
37
|
def validate_token_field_types(body)
|
32
38
|
STRING_FIELDS
|
33
39
|
.select { |field| body[field].present? }
|
@@ -11,6 +11,8 @@ module SMARTAppLaunch
|
|
11
11
|
an access token or a message indicating that the authorization request
|
12
12
|
has been denied. `access_token`, `expires_in`, `token_type`, and `scope` are
|
13
13
|
required. `access_token` must be `Bearer`.
|
14
|
+
|
15
|
+
Scopes returned must be a strict subset of the scopes granted in the original launch.
|
14
16
|
)
|
15
17
|
input :received_scopes
|
16
18
|
output :refresh_token, :access_token, :token_retrieval_time, :expires_in, :received_scopes
|
@@ -36,8 +38,7 @@ module SMARTAppLaunch
|
|
36
38
|
validate_token_field_types(body)
|
37
39
|
validate_token_type(body)
|
38
40
|
|
39
|
-
|
40
|
-
'Received scopes not equal to originally granted scopes'
|
41
|
+
validate_scope_subset(received_scopes, old_received_scopes)
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
@@ -10,7 +10,7 @@ module SMARTAppLaunch
|
|
10
10
|
description %(
|
11
11
|
# Background
|
12
12
|
|
13
|
-
The #{title} Sequence tests the ability of the system to
|
13
|
+
The #{title} Sequence tests the ability of the system to successfully
|
14
14
|
exchange a refresh token for an access token. Refresh tokens are typically
|
15
15
|
longer lived than access tokens and allow client applications to obtain a
|
16
16
|
new access token Refresh tokens themselves cannot provide access to
|
@@ -18,7 +18,7 @@ module SMARTAppLaunch
|
|
18
18
|
|
19
19
|
Token refreshes are accomplished through a `POST` request to the token
|
20
20
|
exchange endpoint as described in the [SMART App Launch
|
21
|
-
Framework](
|
21
|
+
Framework](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html#step-5-later-app-uses-a-refresh-token-to-obtain-a-new-access-token).
|
22
22
|
|
23
23
|
# Test Methodology
|
24
24
|
|
@@ -31,7 +31,7 @@ module SMARTAppLaunch
|
|
31
31
|
* [The OAuth 2.0 Authorization
|
32
32
|
Framework](https://tools.ietf.org/html/rfc6749)
|
33
33
|
* [Using a refresh token to obtain a new access
|
34
|
-
token](
|
34
|
+
token](https://www.hl7.org/fhir/smart-app-launch/1.0.0/index.html#step-5-later-app-uses-a-refresh-token-to-obtain-a-new-access-token)
|
35
35
|
)
|
36
36
|
|
37
37
|
test from: :smart_token_refresh
|
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.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen MacVicar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inferno_core
|