generated from inferno-framework/inferno-template
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FI-2247 backend services migration (#59)
- Loading branch information
1 parent
ad032d4
commit c4340a8
Showing
20 changed files
with
915 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
lib/smart_app_launch/backend_services_authorization_group.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
require_relative 'backend_services_authorization_request_builder' | ||
require_relative 'backend_services_invalid_grant_type_test' | ||
require_relative 'backend_services_invalid_client_assertion_test' | ||
require_relative 'backend_services_invalid_jwt_test' | ||
require_relative 'backend_services_authorization_request_success_test' | ||
require_relative 'backend_services_authorization_response_body_test' | ||
require_relative 'token_exchange_stu2_test' | ||
|
||
module SMARTAppLaunch | ||
class BackendServicesAuthorizationGroup < Inferno::TestGroup | ||
title 'SMART Backend Services Authorization' | ||
short_description 'Demonstrate SMART Backend Services Authorization' | ||
|
||
id :backend_services_authorization | ||
|
||
input :smart_token_url, | ||
title: 'Backend Services Token Endpoint', | ||
description: <<~DESCRIPTION | ||
The OAuth 2.0 Token Endpoint used by the Backend Services specification to provide bearer tokens. | ||
DESCRIPTION | ||
|
||
input :backend_services_client_id, | ||
title: 'Backend Services Client ID', | ||
description: 'Client ID provided at registration to the Inferno application.' | ||
input :backend_services_requested_scope, | ||
title: 'Backend Services Requested Scopes', | ||
description: 'Backend Services Scopes provided at registration to the Inferno application; will be `system/` scopes', | ||
default: 'system/*.read' | ||
|
||
input :client_auth_encryption_method, | ||
title: 'Encryption Method for Asymmetric Confidential Client Authorization', | ||
description: <<~DESCRIPTION, | ||
The server is required to suport either ES384 or RS384 encryption methods for JWT signature verification. | ||
Select which method to use. | ||
DESCRIPTION | ||
type: 'radio', | ||
default: 'ES384', | ||
options: { | ||
list_options: [ | ||
{ | ||
label: 'ES384', | ||
value: 'ES384' | ||
}, | ||
{ | ||
label: 'RS384', | ||
value: 'RS384' | ||
} | ||
] | ||
} | ||
input :backend_services_jwks_kid, | ||
title: 'Backend Services JWKS kid', | ||
description: <<~DESCRIPTION, | ||
The key ID of the JWKS private key to use for signing the client assertion when fetching an auth token. | ||
Defaults to the first JWK in the list if no kid is supplied. | ||
DESCRIPTION | ||
optional: true | ||
|
||
output :bearer_token | ||
|
||
test from: :tls_version_test do | ||
title 'Authorization service token endpoint secured by transport layer security' | ||
description <<~DESCRIPTION | ||
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) | ||
states "the client SHALL use the Transport Layer Security (TLS) Protocol Version 1.2 (RFC5246) | ||
or a more recent version of TLS to authenticate the identity of the FHIR authorization server and to | ||
establish an encrypted, integrity-protected link for securing all exchanges between the client and the | ||
FHIR authorization server’s token endpoint. All exchanges described herein between the client and the | ||
FHIR server SHALL be secured using TLS V1.2 or a more recent version of TLS." | ||
DESCRIPTION | ||
id :smart_backend_services_token_tls_version | ||
|
||
config( | ||
inputs: { url: { name: :smart_token_url } }, | ||
options: { minimum_allowed_version: OpenSSL::SSL::TLS1_2_VERSION } | ||
) | ||
end | ||
|
||
test from: :smart_backend_services_invalid_grant_type | ||
|
||
test from: :smart_backend_services_invalid_client_assertion | ||
|
||
test from: :smart_backend_services_invalid_jwt | ||
|
||
test from: :smart_backend_services_auth_request_success | ||
|
||
test from: :smart_backend_services_auth_response_body | ||
end | ||
end |
74 changes: 74 additions & 0 deletions
74
lib/smart_app_launch/backend_services_authorization_request_builder.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
require 'json/jwt' | ||
require_relative 'client_assertion_builder' | ||
|
||
module SMARTAppLaunch | ||
class BackendServicesAuthorizationRequestBuilder | ||
def self.build(...) | ||
new(...).authorization_request | ||
end | ||
|
||
attr_reader :encryption_method, :scope, :iss, :sub, :aud, :content_type, :grant_type, :client_assertion_type, :exp, | ||
:jti, :kid | ||
|
||
def initialize( | ||
encryption_method:, | ||
scope:, | ||
iss:, | ||
sub:, | ||
aud:, | ||
content_type: 'application/x-www-form-urlencoded', | ||
grant_type: 'client_credentials', | ||
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', | ||
exp: 5.minutes.from_now, | ||
jti: SecureRandom.hex(32), | ||
kid: nil | ||
) | ||
@encryption_method = encryption_method | ||
@scope = scope | ||
@iss = iss | ||
@sub = sub | ||
@aud = aud | ||
@content_type = content_type | ||
@grant_type = grant_type | ||
@client_assertion_type = client_assertion_type | ||
@exp = exp | ||
@jti = jti | ||
@kid = kid | ||
end | ||
|
||
def authorization_request_headers | ||
{ | ||
content_type:, | ||
accept: 'application/json' | ||
}.compact | ||
end | ||
|
||
def authorization_request_query_values | ||
{ | ||
'scope' => scope, | ||
'grant_type' => grant_type, | ||
'client_assertion_type' => client_assertion_type, | ||
'client_assertion' => client_assertion.to_s | ||
}.compact | ||
end | ||
|
||
def client_assertion | ||
@client_assertion ||= ClientAssertionBuilder.build( | ||
client_auth_encryption_method: encryption_method, | ||
iss: iss, | ||
sub: sub, | ||
aud: aud, | ||
exp: exp.to_i, | ||
jti: jti, | ||
kid: kid | ||
) | ||
end | ||
|
||
def authorization_request | ||
uri = Addressable::URI.new | ||
uri.query_values = authorization_request_query_values | ||
|
||
{ body: uri.query, headers: authorization_request_headers } | ||
end | ||
end | ||
end |
40 changes: 40 additions & 0 deletions
40
lib/smart_app_launch/backend_services_authorization_request_success_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
require_relative 'backend_services_authorization_request_builder' | ||
require_relative 'backend_services_authorization_group' | ||
|
||
module SMARTAppLaunch | ||
class BackendServicesAuthorizationRequestSuccessTest < Inferno::Test | ||
id :smart_backend_services_auth_request_success | ||
title 'Authorization request succeeds when supplied correct information' | ||
description <<~DESCRIPTION | ||
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) | ||
states "If the access token request is valid and authorized, the authorization server SHALL issue an access token in response." | ||
DESCRIPTION | ||
|
||
input :client_auth_encryption_method, | ||
:backend_services_requested_scope, | ||
:backend_services_client_id, | ||
:smart_token_url, | ||
:backend_services_jwks_kid | ||
|
||
output :authentication_response | ||
|
||
http_client :token_endpoint do | ||
url :smart_token_url | ||
end | ||
|
||
run do | ||
post_request_content = BackendServicesAuthorizationRequestBuilder.build(encryption_method: client_auth_encryption_method, | ||
scope: backend_services_requested_scope, | ||
iss: backend_services_client_id, | ||
sub: backend_services_client_id, | ||
aud: smart_token_url, | ||
kid: backend_services_jwks_kid) | ||
|
||
authentication_response = post(**{ client: :token_endpoint }.merge(post_request_content)) | ||
|
||
assert_response_status([200, 201]) | ||
|
||
output authentication_response: authentication_response.response_body | ||
end | ||
end | ||
end |
40 changes: 40 additions & 0 deletions
40
lib/smart_app_launch/backend_services_authorization_response_body_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
require_relative 'backend_services_authorization_request_builder' | ||
|
||
module SMARTAppLaunch | ||
class BackendServicesAuthorizationResponseBodyTest < Inferno::Test | ||
id :smart_backend_services_auth_response_body | ||
title 'Authorization request response body contains required information encoded in JSON' | ||
description <<~DESCRIPTION | ||
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) | ||
states The access token response SHALL be a JSON object with the following properties: | ||
| Token Property | Required? | Description | | ||
| --- | --- | --- | | ||
| `access_token` | required | The access token issued by the authorization server. | | ||
| `token_type` | required | Fixed value: `bearer`. | | ||
| `expires_in` | required | The lifetime in seconds of the access token. The recommended value is `300`, for a five-minute token lifetime. | | ||
| `scope` | required | Scope of access authorized. Note that this can be different from the scopes requested by the app. | | ||
DESCRIPTION | ||
|
||
input :authentication_response | ||
output :bearer_token | ||
|
||
run do | ||
skip_if authentication_response.blank?, 'No authentication response received.' | ||
|
||
assert_valid_json(authentication_response) | ||
response_body = JSON.parse(authentication_response) | ||
|
||
access_token = response_body['access_token'] | ||
assert access_token.present?, 'Token response did not contain access_token as required' | ||
|
||
output bearer_token: access_token | ||
|
||
required_keys = ['token_type', 'expires_in', 'scope'] | ||
|
||
required_keys.each do |key| | ||
assert response_body[key].present?, "Token response did not contain #{key} as required" | ||
end | ||
end | ||
end | ||
end |
44 changes: 44 additions & 0 deletions
44
lib/smart_app_launch/backend_services_invalid_client_assertion_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
require_relative 'backend_services_authorization_request_builder' | ||
|
||
module SMARTAppLaunch | ||
class BackendServicesInvalidClientAssertionTest < Inferno::Test | ||
id :smart_backend_services_invalid_client_assertion | ||
title 'Authorization request fails when supplied invalid client_assertion_type' | ||
description <<~DESCRIPTION | ||
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) | ||
defines the required fields for the authorization request, made via HTTP POST to authorization | ||
token endpoint. | ||
This includes the `client_assertion_type` parameter, where the value must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`. | ||
The [OAuth 2.0 Authorization Framework Section 4.3.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3.3) | ||
describes the proper response for an invalid request in the client credentials grant flow: | ||
"If the request failed client authentication or is invalid, the authorization server returns an | ||
error response as described in [Section 5.2](https://tools.ietf.org/html/rfc6749#section-5.2)." | ||
DESCRIPTION | ||
|
||
input :client_auth_encryption_method, | ||
:backend_services_requested_scope, | ||
:backend_services_client_id, | ||
:smart_token_url, | ||
:backend_services_jwks_kid | ||
|
||
http_client :token_endpoint do | ||
url :smart_token_url | ||
end | ||
|
||
run do | ||
post_request_content = BackendServicesAuthorizationRequestBuilder.build(encryption_method: client_auth_encryption_method, | ||
scope: backend_services_requested_scope, | ||
iss: backend_services_client_id, | ||
sub: backend_services_client_id, | ||
aud: smart_token_url, | ||
client_assertion_type: 'not_an_assertion_type', | ||
kid: backend_services_jwks_kid) | ||
|
||
post(**{ client: :token_endpoint }.merge(post_request_content)) | ||
|
||
assert_response_status(400) | ||
end | ||
end | ||
end |
Oops, something went wrong.