From 621866b459b1f662ffe47bf82e30165b76d6abea Mon Sep 17 00:00:00 2001 From: Emily Semple <59289146+emichaud998@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:35:34 -0400 Subject: [PATCH] FI-3018: Allow multi-line custom headers in token introspection request (#77) --- .../token_introspection_group.rb | 3 +- .../token_introspection_request_group.rb | 72 +++++++++---------- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/lib/smart_app_launch/token_introspection_group.rb b/lib/smart_app_launch/token_introspection_group.rb index 04f126c..4ba8cc6 100644 --- a/lib/smart_app_launch/token_introspection_group.rb +++ b/lib/smart_app_launch/token_introspection_group.rb @@ -57,13 +57,12 @@ class SMARTTokenIntrospectionGroup < Inferno::TestGroup If the introspection endpoint is protected, testers must enter their own HTTP Authorization header for the introspection request. See [RFC 7616 The 'Basic' HTTP Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc7617) for the most common - approach that uses client credentials. Testers may also provide any additional parameters needed for their authorization + approach that uses client credentials. Testers may also provide any additional parameters needed for their authorization server to complete the introspection request. **Note:** For both the Authorization header and request parameters, user-input values will be sent exactly as entered and therefore the tester must URI-encode any appropriate values. ) - end end diff --git a/lib/smart_app_launch/token_introspection_request_group.rb b/lib/smart_app_launch/token_introspection_request_group.rb index af6e091..03b1531 100644 --- a/lib/smart_app_launch/token_introspection_request_group.rb +++ b/lib/smart_app_launch/token_introspection_request_group.rb @@ -11,42 +11,44 @@ class SMARTTokenIntrospectionRequestGroup < Inferno::TestGroup id :smart_token_introspection_request_group description %( This group of tests executes the token introspection requests and ensures the correct HTTP response is returned - but does not validate the contents of the token introspection response. + but does not validate the contents of the token introspection response. - If Inferno cannot reasonably be configured to be authorized to access the token introspection endpoint, these tests + If Inferno cannot reasonably be configured to be authorized to access the token introspection endpoint, these tests can be skipped. Instead, an out-of-band token introspection request must be completed and the response body manually provided as input for the Validate Introspection Response test group. ) input_instructions %( - If the Request New Access Token group was executed, the access token input will auto-populate with that token. - Otherwise an active access token needs to be obtained out-of-band and input. - - Per [RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662#section-2), "the definition of an active token is - currently dependent upon the authorization server, but this is commonly a token that has been issued by this - authorization server, is not expired, has not been revoked, and is valid for use at the protected resource making + If the Request New Access Token group was executed, the access token input will auto-populate with that token. + Otherwise an active access token needs to be obtained out-of-band and input. + + Per [RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662#section-2), "the definition of an active token is + currently dependent upon the authorization server, but this is commonly a token that has been issued by this + authorization server, is not expired, has not been revoked, and is valid for use at the protected resource making the introspection call." If the introspection endpoint is protected, testers must enter their own HTTP Authorization header for the introspection request. See [RFC 7616 The 'Basic' HTTP Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc7617) for the most common - approach that uses client credentials. Testers may also provide any additional parameters needed for their authorization + approach that uses client credentials. Testers may also provide any additional parameters needed for their authorization server to complete the introspection request. **Note:** For both the Authorization header and request parameters, user-input values will be sent exactly as entered and therefore the tester must URI-encode any appropriate values. ) - input :well_known_introspection_url, - title: 'Token Introspection Endpoint URL', + input :well_known_introspection_url, + title: 'Token Introspection Endpoint URL', description: 'The complete URL of the token introspection endpoint.' input :custom_authorization_header, - title: 'HTTP Authorization Header for Introspection Request', + title: 'Custom HTTP Headers for Introspection Request', type: 'textarea', optional: true, description: %( - Include header name, auth scheme, and auth parameters. - Ex: 'Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW' + Add custom headers for the introspection request by adding each header's name and value with a new line + between each header. + Ex: +
: ) input :optional_introspection_request_params, @@ -54,69 +56,67 @@ class SMARTTokenIntrospectionRequestGroup < Inferno::TestGroup type: 'textarea', optional: true, description: %( - Any additional parameters to append to the request body, separated by &. Example: 'param1=abc¶m2=def' + Any additional parameters to append to the request body, separated by &. Example: 'param1=abc¶m2=def' ) test do title 'Token introspection endpoint returns a response when provided an active token' description %( This test will execute a token introspection request for an active token and ensure a 200 status and valid JSON - body are returned in the response. + body are returned in the response. ) - input :standalone_access_token, + input :standalone_access_token, title: 'Access Token', description: 'The access token to be introspected. MUST be active.' - output :active_token_introspection_response_body run do - # If this is being chained from an earlier test, it might be blank if not present in the well-known endpoint skip_if well_known_introspection_url.nil?, 'No introspection URL present in SMART well-known endpoint.' - headers = {'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded'} + headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded' } body = "token=#{standalone_access_token}" if custom_authorization_header.present? - parsed_header = custom_authorization_header.split(':', 2) - assert parsed_header.length == 2, "Incorrect custom HTTP header format input, expected: '
:
'" - headers[parsed_header[0]] = parsed_header[1].strip + custom_headers = custom_authorization_header.split("\n") + custom_headers.each do |custom_header| + parsed_header = custom_header.split(':', 2) + assert parsed_header.length == 2, + 'Incorrect custom HTTP header format input, expected: "
:
"' + headers[parsed_header[0]] = parsed_header[1].strip + end end - if optional_introspection_request_params.present? - body += "&#{optional_introspection_request_params}" - end + body += "&#{optional_introspection_request_params}" if optional_introspection_request_params.present? - post(well_known_introspection_url, body: body, headers: headers) + post(well_known_introspection_url, body:, headers:) assert_response_status(200) output active_token_introspection_response_body: request.response_body end - end - test do + test do title 'Token introspection endpoint returns a response when provided an invalid token' description %( This test will execute a token introspection request for an invalid token and ensure a 200 status and valid JSON - body are returned in response. + body are returned in response. ) output :invalid_token_introspection_response_body run do - # If this is being chained from an earlier test, it might be blank if not present in the well-known endpoint skip_if well_known_introspection_url.nil?, 'No introspection URL present in SMART well-known endpoint.' - headers = {'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded'} - body = "token=invalid_token_value" - post(well_known_introspection_url, body: body, headers: headers) - + headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded' } + body = 'token=invalid_token_value' + post(well_known_introspection_url, body:, headers:) + assert_response_status(200) output invalid_token_introspection_response_body: request.response_body end end end -end \ No newline at end of file +end