udap_security_test_kit 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/udap_security_test_kit/authorization_code_authentication_group.rb +44 -0
  4. data/lib/udap_security_test_kit/authorization_code_group.rb +103 -0
  5. data/lib/udap_security_test_kit/authorization_code_received_test.rb +31 -0
  6. data/lib/udap_security_test_kit/authorization_code_redirect_test.rb +74 -0
  7. data/lib/udap_security_test_kit/authorization_code_token_exchange_test.rb +103 -0
  8. data/lib/udap_security_test_kit/authorization_endpoint_field_test.rb +43 -0
  9. data/lib/udap_security_test_kit/certs/InfernoCA.key +52 -0
  10. data/lib/udap_security_test_kit/certs/InfernoCA.pem +35 -0
  11. data/lib/udap_security_test_kit/certs/TestClient.pem +32 -0
  12. data/lib/udap_security_test_kit/certs/TestClientPrivateKey.key +28 -0
  13. data/lib/udap_security_test_kit/client_credentials_authentication_group.rb +40 -0
  14. data/lib/udap_security_test_kit/client_credentials_group.rb +105 -0
  15. data/lib/udap_security_test_kit/client_credentials_token_exchange_test.rb +130 -0
  16. data/lib/udap_security_test_kit/common_assertions.rb +16 -0
  17. data/lib/udap_security_test_kit/default_cert_file_loader.rb +27 -0
  18. data/lib/udap_security_test_kit/discovery_group.rb +90 -0
  19. data/lib/udap_security_test_kit/dynamic_client_registration_group.rb +129 -0
  20. data/lib/udap_security_test_kit/generate_client_certs_test.rb +60 -0
  21. data/lib/udap_security_test_kit/grant_types_supported_field_test.rb +53 -0
  22. data/lib/udap_security_test_kit/reg_endpoint_jwt_signing_alg_values_supported_field_test.rb +29 -0
  23. data/lib/udap_security_test_kit/registration_endpoint_field_test.rb +30 -0
  24. data/lib/udap_security_test_kit/registration_failure_invalid_contents_test.rb +68 -0
  25. data/lib/udap_security_test_kit/registration_failure_invalid_jwt_signature_test.rb +70 -0
  26. data/lib/udap_security_test_kit/registration_success_contents_test.rb +64 -0
  27. data/lib/udap_security_test_kit/registration_success_test.rb +68 -0
  28. data/lib/udap_security_test_kit/scopes_supported_field_test.rb +26 -0
  29. data/lib/udap_security_test_kit/signed_metadata_contents_test.rb +89 -0
  30. data/lib/udap_security_test_kit/signed_metadata_field_test.rb +31 -0
  31. data/lib/udap_security_test_kit/signed_metadata_trust_verification_test.rb +54 -0
  32. data/lib/udap_security_test_kit/software_statement_builder.rb +32 -0
  33. data/lib/udap_security_test_kit/token_endpoint_auth_methods_supported_field_test.rb +22 -0
  34. data/lib/udap_security_test_kit/token_endpoint_auth_signing_alg_values_supported_field_test.rb +32 -0
  35. data/lib/udap_security_test_kit/token_endpoint_field_test.rb +30 -0
  36. data/lib/udap_security_test_kit/token_exchange_response_body_test.rb +30 -0
  37. data/lib/udap_security_test_kit/token_exchange_response_headers_test.rb +30 -0
  38. data/lib/udap_security_test_kit/udap_auth_extensions_required_field_test.rb +38 -0
  39. data/lib/udap_security_test_kit/udap_auth_extensions_supported_field_test.rb +31 -0
  40. data/lib/udap_security_test_kit/udap_certifications_required_field_test.rb +45 -0
  41. data/lib/udap_security_test_kit/udap_certifications_supported_field_test.rb +33 -0
  42. data/lib/udap_security_test_kit/udap_client_assertion_payload_builder.rb +15 -0
  43. data/lib/udap_security_test_kit/udap_jwt_builder.rb +30 -0
  44. data/lib/udap_security_test_kit/udap_jwt_validator.rb +71 -0
  45. data/lib/udap_security_test_kit/udap_profiles_supported_field_test.rb +47 -0
  46. data/lib/udap_security_test_kit/udap_request_builder.rb +43 -0
  47. data/lib/udap_security_test_kit/udap_versions_supported_field_test.rb +21 -0
  48. data/lib/udap_security_test_kit/udap_x509_certificate.rb +42 -0
  49. data/lib/udap_security_test_kit/version.rb +3 -0
  50. data/lib/udap_security_test_kit/well_known_endpoint_test.rb +31 -0
  51. data/lib/udap_security_test_kit.rb +63 -0
  52. metadata +124 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bd0f7eeeda6734c1bd79c8ed30ea10dae1a74b7869cb1f2ec3a3a86255eb72df
4
+ data.tar.gz: 7d9a2c235883ef6cce3b273099a780a9a710738731026de95a9aac587c9f313a
5
+ SHA512:
6
+ metadata.gz: a434c3c7b537520605b670bcf61c92679c2fc4658ff8d07111b1e43609dbabf72f5adf46287f2ee6c05f57349ffb5a3a1901cd20a576c3b4f31e002311462b73
7
+ data.tar.gz: ffed51ee343dab385f3e9cea5ce72e43b30a50111b780489a3208edd6d2401cc05eb421aa548ab733eec918afcfb4098cb64794ff4e1c992f4df7f3a357e5988
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright {yyyy} {name of copyright owner}
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,44 @@
1
+ require_relative 'authorization_code_redirect_test'
2
+ require_relative 'authorization_code_received_test'
3
+ require_relative 'authorization_code_token_exchange_test'
4
+ require_relative 'token_exchange_response_body_test'
5
+ require_relative 'token_exchange_response_headers_test'
6
+ module UDAPSecurityTestKit
7
+ class AuthorizationCodeAuthenticationGroup < Inferno::TestGroup
8
+ title 'UDAP Authorization Code Authorization & Authentication'
9
+ description %(
10
+ This group tests the use of the authorization_code grant type to receive an authorization code from the
11
+ authorization server and exchange it for an access token, as described
12
+ in the [consumer-facing](https://hl7.org/fhir/us/udap-security/STU1/consumer.html) and
13
+ [business-to-business (B2B)](https://hl7.org/fhir/us/udap-security/STU1/b2b.html) profiles requirements.
14
+ )
15
+ id :udap_authorization_code_authentication_group
16
+
17
+ test from: :udap_authorization_code_redirect
18
+ test from: :udap_authorization_code_received
19
+ test from: :udap_authorization_code_token_exchange,
20
+ config: {
21
+ requests: {
22
+ token_exchange: {
23
+ name: :authorization_code_token_exchange
24
+ }
25
+ }
26
+ }
27
+ test from: :udap_token_exchange_response_body,
28
+ config: {
29
+ inputs: {
30
+ token_response_body: {
31
+ name: :authorization_code_token_response_body
32
+ }
33
+ }
34
+ }
35
+ test from: :udap_token_exchange_response_headers,
36
+ config: {
37
+ requests: {
38
+ token_exchange: {
39
+ name: :authorization_code_token_exchange
40
+ }
41
+ }
42
+ }
43
+ end
44
+ end
@@ -0,0 +1,103 @@
1
+ require_relative 'dynamic_client_registration_group'
2
+ require_relative 'discovery_group'
3
+ require_relative 'authorization_code_authentication_group'
4
+ module UDAPSecurityTestKit
5
+ class AuthorizationCodeGroup < Inferno::TestGroup
6
+ title 'UDAP Authorization Code Flow'
7
+ description %(
8
+ This group tests UDAP servers that support JWT authentication using an OAuth2.0 authorization_code grant flow and
9
+ includes the following sub-groups.
10
+
11
+ 1. Discovery Group
12
+ 2. Dynamic Client Registration
13
+ 3. Authorization and Authentication - supports both [Consumer Facing](https://hl7.org/fhir/us/udap-security/STU1/consumer.html)
14
+ and [Business-to-Business (B2B)](https://hl7.org/fhir/us/udap-security/STU1/b2b.html) profiles in the UDAP IG
15
+ )
16
+ id :udap_authorization_code_group
17
+
18
+ input_instructions %(
19
+ **Discovery Tests**
20
+
21
+ #{DiscoveryGroup.discovery_group_input_instructions}
22
+
23
+ **Dynamic Client Registration Tests**
24
+
25
+ #{DynamicClientRegistrationGroup.dynamic_client_registration_input_instructions}
26
+ )
27
+
28
+ group from: :udap_discovery_group,
29
+ id: :auth_code_discovery_group,
30
+ run_as_group: true,
31
+ config: {
32
+ inputs: {
33
+ required_flow_type: {
34
+ name: :flow_type_auth_code,
35
+ title: 'Required OAuth2.0 Flow Type for Authorization Code Workflow',
36
+ optional: false,
37
+ default: ['authorization_code'],
38
+ locked: true
39
+ }
40
+ }
41
+ }
42
+ group from: :udap_dynamic_client_registration_group,
43
+ id: :auth_code_dcr_group,
44
+ run_as_group: true,
45
+ config: {
46
+ inputs: {
47
+ udap_registration_grant_type: {
48
+ name: :reg_grant_type_auth_code,
49
+ default: 'authorization_code',
50
+ locked: true
51
+ },
52
+ udap_client_cert_pem: {
53
+ name: :udap_client_cert_pem_auth_code_flow,
54
+ title: 'Authorization Code Client Certificate(s) (PEM Format)'
55
+ },
56
+ udap_client_private_key_pem: {
57
+ name: :udap_client_private_key_auth_code_flow,
58
+ title: 'Authorization Code Client Private Key (PEM Format)'
59
+ },
60
+ udap_cert_iss: {
61
+ name: :udap_cert_iss_auth_code_flow,
62
+ title: 'Authorization Code JWT Issuer (iss) Claim'
63
+ },
64
+ udap_registration_requested_scope: {
65
+ name: :udap_registration_scope_auth_code_flow,
66
+ title: 'Authorization Code Registration Requested Scope(s)',
67
+ description: %(
68
+ String containing a space delimited list of scopes requested by the client application for use in
69
+ subsequent requests. The Authorization Server MAY consider this list when deciding the scopes that it
70
+ will allow the application to subsequently request. Apps requesting the "authorization_code" grant
71
+ type SHOULD request user or patient scopes.
72
+ )
73
+ },
74
+ udap_registration_certifications: {
75
+ name: :udap_registration_certifications_auth_code_flow,
76
+ title: 'Authorization Code UDAP Registration Certifications'
77
+ }
78
+ },
79
+ outputs: {
80
+ udap_client_cert_pem: {
81
+ name: :udap_client_cert_pem_auth_code_flow
82
+ },
83
+ udap_client_private_key_pem: {
84
+ name: :udap_client_private_key_auth_code_flow
85
+ },
86
+ udap_cert_iss: {
87
+ name: :udap_cert_iss_auth_code_flow
88
+ }
89
+ }
90
+ } do
91
+ input_order :udap_registration_endpoint,
92
+ :reg_grant_type_auth_code,
93
+ :udap_client_cert_pem_auth_code_flow,
94
+ :udap_client_private_key_auth_code_flow,
95
+ :udap_cert_iss_auth_code_flow,
96
+ :udap_registration_scope_auth_code_flow,
97
+ :udap_jwt_signing_alg, :udap_registration_certifications_auth_code_flow
98
+ end
99
+
100
+ group from: :udap_authorization_code_authentication_group,
101
+ run_as_group: true
102
+ end
103
+ end
@@ -0,0 +1,31 @@
1
+ module UDAPSecurityTestKit
2
+ class AuthorizationCodeReceivedTest < Inferno::Test
3
+ title 'Authorization server sends code parameter'
4
+ description %(
5
+ Code is a required querystring parameter on the redirect.
6
+ )
7
+ id :udap_authorization_code_received
8
+
9
+ output :udap_authorization_code
10
+ uses_request :redirect
11
+
12
+ run do
13
+ code = request.query_parameters['code']
14
+ output udap_authorization_code: code
15
+
16
+ assert code.present?, 'No `code` parameter received'
17
+
18
+ error = request.query_parameters['error']
19
+
20
+ pass_if error.blank?
21
+
22
+ error_message = "Error returned from authorization server. code: '#{error}'"
23
+ error_description = request.query_parameters['error_description']
24
+ error_uri = request.query_parameters['error_uri']
25
+ error_message += ", description: '#{error_description}'" if error_description.present?
26
+ error_message += ", uri: #{error_uri}" if error_uri.present?
27
+
28
+ assert false, error_message
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,74 @@
1
+ module UDAPSecurityTestKit
2
+ class AuthorizationCodeRedirectTest < Inferno::Test
3
+ title 'Authorization server redirects client to redirect URI'
4
+ id :udap_authorization_code_redirect
5
+ description %(
6
+ Per [RFC 6749 OAuth 2.0 Authorization Framework Section 4.1.1](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1),
7
+ once the server validates the client's authorization request, the authorization server directs the user-agent to
8
+ the provided client redirection URI using an HTTP redirection response.
9
+ )
10
+
11
+ input :udap_authorization_endpoint,
12
+ title: 'Authorization Endpoint',
13
+ description: 'The full URL from which Inferno will request an authorization code.'
14
+
15
+ input :udap_client_id,
16
+ title: 'Client ID',
17
+ description: 'Client ID as registered with the authorization server.'
18
+
19
+ output :udap_authorization_code_state
20
+
21
+ receives_request :redirect
22
+
23
+ config options: { redirect_uri: "#{Inferno::Application['base_url']}/custom/udap_security_test_kit/redirect" }
24
+
25
+ def wait_message(auth_url)
26
+ if config.options[:redirect_message_proc].present?
27
+ return instance_exec(auth_url, &config.options[:redirect_message_proc])
28
+ end
29
+
30
+ %(
31
+ ### #{self.class.parent&.parent&.title}
32
+
33
+ [Follow this link to authorize with the auth server](#{auth_url}).
34
+
35
+ Tests will resume once Inferno receives a request at
36
+ `#{config.options[:redirect_uri]}` with a state of `#{udap_authorization_code_state}`.
37
+ )
38
+ end
39
+
40
+ def authorization_url_builder(url, params)
41
+ uri = URI(url)
42
+
43
+ # because the URL might have parameters on it
44
+ original_parameters = URI.decode_www_form(uri.query || '').to_h
45
+ new_params = original_parameters.merge(params)
46
+
47
+ uri.query = URI.encode_www_form(new_params)
48
+ uri.to_s
49
+ end
50
+
51
+ run do
52
+ output udap_authorization_code_state: SecureRandom.uuid
53
+
54
+ oauth2_params = {
55
+ 'response_type' => 'code',
56
+ 'client_id' => udap_client_id,
57
+ 'redirect_uri' => config.options[:redirect_uri],
58
+ 'state' => udap_authorization_code_state
59
+ }.compact
60
+
61
+ authorization_url = authorization_url_builder(
62
+ udap_authorization_endpoint,
63
+ oauth2_params
64
+ )
65
+
66
+ info("Inferno redirecting browser to #{authorization_url}.")
67
+
68
+ wait(
69
+ identifier: udap_authorization_code_state,
70
+ message: wait_message(authorization_url)
71
+ )
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,103 @@
1
+ require_relative 'udap_client_assertion_payload_builder'
2
+
3
+ module UDAPSecurityTestKit
4
+ class AuthorizationCodeTokenExchangeTest < Inferno::Test
5
+ title 'OAuth token exchange request succeeds when supplied correct information'
6
+ description %(
7
+ The [UDAP Security IG Section 4.2 on Obtaining an Access Token](https://hl7.org/fhir/us/udap-security/STU1/consumer.html#obtaining-an-access-token)
8
+ states the following:
9
+ - Client applications SHALL exchange authorization codes for access
10
+ tokens as per Section 4.1.3 of RFC 6749
11
+ - Client applications authenticating with a private key and
12
+ Authentication Token ... SHALL submit a POST request to the Authorization Server’s token endpoint
13
+ - An Authorization Server receiving token requests containing
14
+ Authentication Tokens as above SHALL validate and respond to the request, as per Sections 6 and 7 of [UDAP
15
+ JWT-Based Client Authentication](https://www.udap.org/udap-jwt-client-auth.html).
16
+ )
17
+ id :udap_authorization_code_token_exchange
18
+
19
+ input :udap_authorization_code,
20
+ :udap_client_id
21
+
22
+ input :udap_token_endpoint,
23
+ title: 'Token Endpoint',
24
+ description: 'The full URL from which Inferno will request an access token'
25
+
26
+ input :udap_client_cert_pem_auth_code_flow,
27
+ title: 'X.509 Client Certificate (PEM Format)',
28
+ type: 'textarea',
29
+ description: %(
30
+ A list of one or more X.509 certificates in PEM format separated by a newline.
31
+ The first (leaf) certificate MUST
32
+ represent the client entity Inferno registered as,
33
+ and the trust chain that will be built from the provided certificate(s) must resolve to a CA trusted by the
34
+ authorization server under test.
35
+ )
36
+
37
+ input :udap_client_private_key_auth_code_flow,
38
+ type: 'textarea',
39
+ title: 'Client Private Key (PEM Format)',
40
+ description: 'The private key corresponding to the X.509 client certificate'
41
+
42
+ input :udap_jwt_signing_alg,
43
+ title: 'JWT Signing Algorithm',
44
+ description: %(
45
+ Algorithm used to sign UDAP JSON Web Tokens (JWTs). UDAP Implementations SHALL support
46
+ RS256.
47
+ ),
48
+ type: 'radio',
49
+ options: {
50
+ list_options: [
51
+ {
52
+ label: 'RS256',
53
+ value: 'RS256'
54
+ }
55
+ ]
56
+ },
57
+ default: 'RS256',
58
+ locked: true
59
+
60
+ output :token_retrieval_time
61
+ output :authorization_code_token_response_body
62
+ makes_request :token_exchange
63
+
64
+ config options: { redirect_uri: "#{Inferno::Application['base_url']}/custom/udap_security_test_kit/redirect" }
65
+
66
+ run do
67
+ client_assertion_payload = UDAPClientAssertionPayloadBuilder.build(
68
+ udap_client_id,
69
+ udap_token_endpoint,
70
+ nil
71
+ )
72
+
73
+ x5c_certs = UDAPJWTBuilder.split_user_input_cert_string(udap_client_cert_pem_auth_code_flow)
74
+
75
+ client_assertion_jwt = UDAPJWTBuilder.encode_jwt_with_x5c_header(
76
+ client_assertion_payload,
77
+ udap_client_private_key_auth_code_flow,
78
+ udap_jwt_signing_alg,
79
+ x5c_certs
80
+ )
81
+
82
+ token_exchange_headers, token_exchange_body =
83
+ UDAPRequestBuilder.build_token_exchange_request(
84
+ client_assertion_jwt,
85
+ 'authorization_code',
86
+ udap_authorization_code,
87
+ config.options[:redirect_uri]
88
+ )
89
+
90
+ post(udap_token_endpoint,
91
+ body: token_exchange_body,
92
+ name: :token_exchange,
93
+ headers: token_exchange_headers)
94
+
95
+ assert_response_status(200)
96
+ assert_valid_json(request.response_body)
97
+
98
+ output token_retrieval_time: Time.now.iso8601
99
+
100
+ output authorization_code_token_response_body: request.response_body
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,43 @@
1
+ module UDAPSecurityTestKit
2
+ class AuthorizationEndpointFieldTest < Inferno::Test
3
+ include Inferno::DSL::Assertions
4
+
5
+ title 'authorization_endpoint field'
6
+ id :udap_authorization_endpoint_field
7
+ description %(
8
+ `authorization_endpoint` is a string containing the absolute URL of the Authorization Server's authorization
9
+ endpoint. This parameter SHALL be present if the value of the
10
+ grant_types_supported parameter includes the string "authorization_code"
11
+ )
12
+
13
+ input :udap_well_known_metadata_json
14
+ output :udap_authorization_endpoint
15
+
16
+ run do
17
+ assert_valid_json(udap_well_known_metadata_json)
18
+ config = JSON.parse(udap_well_known_metadata_json)
19
+
20
+ skip_if !config.key?('grant_types_supported'),
21
+ 'Cannot access data needed to assess `authorization_endpoint` field:
22
+ `grant_types_supported` field not present'
23
+
24
+ skip_if !config['grant_types_supported'].is_a?(Array),
25
+ 'Cannot access data needed to assess `authorization_endpoint` field: `grant_types_supported` field is not
26
+ correctly formatted'
27
+
28
+ omit_if !config['grant_types_supported'].include?('authorization_code'),
29
+ '`authorization_endpoint` field is only required if `authorization_code` is a supported grant type'
30
+
31
+ assert config.key?('authorization_endpoint'),
32
+ '`authorization_endpoint` field is required if `authorization_endpoint` is a supported grant type'
33
+
34
+ endpoint = config['authorization_endpoint']
35
+
36
+ assert endpoint.is_a?(String),
37
+ "`authorization_endpoint` should be a String, but found #{endpoint.class.name}"
38
+ assert_valid_http_uri(endpoint, "`#{endpoint}` is not a valid URI")
39
+
40
+ output udap_authorization_endpoint: endpoint
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,52 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ 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
51
+ u2lflAnEGDoIiMauYMdOCHjrq/l7Jw==
52
+ -----END PRIVATE KEY-----