Class: Inferno::DSL::AuthInfo
- Inherits:
-
Object
- Object
- Inferno::DSL::AuthInfo
- Includes:
- Entities::Attributes
- Defined in:
- lib/inferno/dsl/auth_info.rb
Overview
AuthInfo provide a user with a single input which contains the information needed for a fhir client to perform authorization and refresh an access token when necessary.
AuthInfo supports the following ‘auth_type`:
-
‘public` - client id only
-
‘symmetric` - Symmetric confidential (i.e., with a static client id and secret)
-
‘asymmetric` - Symmetric confidential (i.e., a client id with a signed JWT rather than a client secret)
-
‘backend_services`
When configuring an AuthInfo input, the invdidual fields are exposed as ‘components` in the input’s options, and can be configured there similar to normal inputs.
The AuthInfo input type supports two different modes in the UI. Different fields will be presented to the user depending on which mode is selected.
-
‘auth` - This presents the inputs needed to perform authorization, and is appropriate to use as an input to test groups which perform authorization
-
‘access` - This presents the inputs needed to access resources assuming that authorization has already happened, and is appropriate to use as an input to test groups which access resources using previously granted authorization
class AuthInfoExampleSuite < Inferno::TestSuite
input :url,
title: 'Base FHIR url'
group do
title 'Perform public authorization'
input :fhir_auth,
type: :auth_info,
options: {
mode: 'auth',
components: [
{
name: :auth_type,
default: 'public',
locked: true
}
]
}
# Some tests here to perform authorization
end
group do
title 'FHIR API Tests'
input :fhir_auth,
type: :auth_info,
options: {
mode: 'access'
}
fhir_client do
url :url
auth_info :fhir_auth # NOT YET IMPLEMENTED
end
# Some tests here to access FHIR API
end
end
Constant Summary collapse
- ATTRIBUTES =
[ :auth_type, :use_discovery, :token_url, :auth_url, :requested_scopes, :client_id, :client_secret, :redirect_url, # TODO: does this belong here? :pkce_support, :pkce_code_challenge_method, :auth_request_method, :encryption_algorithm, :kid, :jwks, :access_token, :refresh_token, :issue_time, :expires_in, :name ].freeze
Instance Attribute Summary collapse
- #access_token ⇒ Object
-
#auth_request_method The http method which will be used(Thehttpmethodwhichwillbeused) ⇒ Object
to perform the request to the authorization endpoint.
-
#auth_type The type of authorization to be performed.(Thetypeofauthorizationtobeperformed.) ⇒ Object
One of ‘public`, `symmetric`, `asymmetric`, or `backend_services`.
- #auth_url The url of the authorization endpoint(Theurloftheauthorizationendpoint) ⇒ Object
-
#client ⇒ Object
Returns the value of attribute client.
- #client_id ⇒ Object
- #client_secret ⇒ Object
-
#encryption_algorithm The encryption algorithm which(Theencryptionalgorithmwhich) ⇒ Object
will be used to sign the JWT client credentials.
- #expires_in The lifetime of the access token in seconds(Thelifetimeoftheaccesstoken) ⇒ Object
-
#issue_time An iso8601 formatted string representing the(Aniso8601formattedstringrepresentingthe) ⇒ Object
time the access token was issued.
-
#jwks A JWKS (including private keys) which will be used(AJWKS(including private keys)) ⇒ Object
instead of Inferno’s default JWKS if provided.
-
#kid The key id for the keys to be used to sign the JWT(Thekeyid) ⇒ Object
client credentials.
- #name ⇒ Object
-
#pkce_code_challenge_method Either `S256` (default) or(Either`S256`(default)) ⇒ Object
‘plain`.
-
#pkce_support Whether PKCE will be used during(WhetherPKCEwillbeusedduring) ⇒ Object
authorization.
- #redirect_url ⇒ Object
- #refresh_token ⇒ Object
-
#requested_scopes The scopes which will be requested(Thescopeswhichwillberequested) ⇒ Object
during authorization.
- #token_url The url of the auth server's token endpoint(Theurloftheauthserver's token endpoint) ⇒ Object
Instance Method Summary collapse
- #able_to_refresh? ⇒ Boolean
- #add_to_client(client) ⇒ Object
- #asymmetric_auth_refresh_params ⇒ Object
- #auth_jwt_claims ⇒ Object
- #auth_jwt_header ⇒ Object
- #backend_services? ⇒ Boolean
- #backend_services_auth_refresh_params ⇒ Object
- #client_assertion ⇒ Object
-
#initialize(raw_attributes_hash) ⇒ AuthInfo
constructor
A new instance of AuthInfo.
- #need_to_refresh? ⇒ Boolean
- #oauth2_refresh_headers ⇒ Object
- #oauth2_refresh_params ⇒ Object
- #private_key ⇒ Object
- #public_auth_refresh_params ⇒ Object
- #signing_key ⇒ Object
- #symmetric_auth_refresh_params ⇒ Object
- #to_hash ⇒ Object
- #to_s ⇒ Object
- #update_from_response_body(request) ⇒ Object
Methods included from Entities::Attributes
Constructor Details
#initialize(raw_attributes_hash) ⇒ AuthInfo
Returns a new instance of AuthInfo.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/inferno/dsl/auth_info.rb', line 130 def initialize(raw_attributes_hash) attributes_hash = raw_attributes_hash.symbolize_keys invalid_keys = attributes_hash.keys - ATTRIBUTES raise Exceptions::UnknownAttributeException.new(invalid_keys, self.class) if invalid_keys.present? attributes_hash.each do |name, value| value = DateTime.parse(value) if name == :issue_time && value.is_a?(String) instance_variable_set(:"@#{name}", value) end self.issue_time = DateTime.now if access_token.present? && issue_time.blank? end |
Instance Attribute Details
#access_token ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#auth_request_method The http method which will be used(Thehttpmethodwhichwillbeused) ⇒ Object
to perform the request to the authorization endpoint. Either ‘get` (default) or `post`
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#auth_type The type of authorization to be performed.(Thetypeofauthorizationtobeperformed.) ⇒ Object
One of ‘public`, `symmetric`, `asymmetric`, or `backend_services`
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#auth_url The url of the authorization endpoint(Theurloftheauthorizationendpoint) ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#client ⇒ Object
Returns the value of attribute client.
96 97 98 |
# File 'lib/inferno/dsl/auth_info.rb', line 96 def client @client end |
#client_id ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#client_secret ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#encryption_algorithm The encryption algorithm which(Theencryptionalgorithmwhich) ⇒ Object
will be used to sign the JWT client credentials. Either ‘es384` (default) or `rs384`
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#expires_in The lifetime of the access token in seconds(Thelifetimeoftheaccesstoken) ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#issue_time An iso8601 formatted string representing the(Aniso8601formattedstringrepresentingthe) ⇒ Object
time the access token was issued
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#jwks A JWKS (including private keys) which will be used(AJWKS(including private keys)) ⇒ Object
instead of Inferno’s default JWKS if provided
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#kid The key id for the keys to be used to sign the JWT(Thekeyid) ⇒ Object
client credentials. When blank, the first key for the selected encryption algorithm will be used
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#name ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#pkce_code_challenge_method Either `S256` (default) or(Either`S256`(default)) ⇒ Object
‘plain`
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#pkce_support Whether PKCE will be used during(WhetherPKCEwillbeusedduring) ⇒ Object
authorization. Either ‘enabled` or `disabled`.
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#redirect_url ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#refresh_token ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#requested_scopes The scopes which will be requested(Thescopeswhichwillberequested) ⇒ Object
during authorization
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
#token_url The url of the auth server's token endpoint(Theurloftheauthserver's token endpoint) ⇒ Object
|
|
# File 'lib/inferno/dsl/auth_info.rb', line 98
|
Instance Method Details
#able_to_refresh? ⇒ Boolean
183 184 185 |
# File 'lib/inferno/dsl/auth_info.rb', line 183 def able_to_refresh? token_url.present? && (backend_services? || refresh_token.present?) end |
#add_to_client(client) ⇒ Object
164 165 166 167 168 169 170 171 |
# File 'lib/inferno/dsl/auth_info.rb', line 164 def add_to_client(client) client.auth_info = self self.client = client # TODO: do we want to perform authorization if no access_token or rely on SMART/ other auth tests? return unless access_token.present? client.set_bearer_token(access_token) end |
#asymmetric_auth_refresh_params ⇒ Object
220 221 222 223 224 225 |
# File 'lib/inferno/dsl/auth_info.rb', line 220 def asymmetric_auth_refresh_params symmetric_auth_refresh_params.merge( 'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', 'client_assertion' => client_assertion ) end |
#auth_jwt_claims ⇒ Object
279 280 281 282 283 284 285 286 287 |
# File 'lib/inferno/dsl/auth_info.rb', line 279 def auth_jwt_claims { 'iss' => client_id, 'sub' => client_id, 'aud' => token_url, 'exp' => 5.minutes.from_now.to_i, 'jti' => SecureRandom.hex(32) } end |
#auth_jwt_header ⇒ Object
269 270 271 272 273 274 275 276 |
# File 'lib/inferno/dsl/auth_info.rb', line 269 def auth_jwt_header { 'alg' => encryption_algorithm, 'kid' => private_key['kid'], 'typ' => 'JWT', 'jku' => Inferno::Application['jwks_url'] } end |
#backend_services? ⇒ Boolean
188 189 190 |
# File 'lib/inferno/dsl/auth_info.rb', line 188 def backend_services? auth_type == 'backend_services' end |
#backend_services_auth_refresh_params ⇒ Object
228 229 230 231 232 233 234 235 |
# File 'lib/inferno/dsl/auth_info.rb', line 228 def backend_services_auth_refresh_params { 'grant_type' => 'client_credentials', 'scope' => requested_scopes, 'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', 'client_assertion' => client_assertion } end |
#client_assertion ⇒ Object
290 291 292 |
# File 'lib/inferno/dsl/auth_info.rb', line 290 def client_assertion JWT.encode auth_jwt_claims, signing_key, encryption_algorithm, auth_jwt_header end |
#need_to_refresh? ⇒ Boolean
174 175 176 177 178 179 180 |
# File 'lib/inferno/dsl/auth_info.rb', line 174 def need_to_refresh? return false if access_token.blank? || (!backend_services? && refresh_token.blank?) return true if expires_in.blank? issue_time.to_i + expires_in.to_i - DateTime.now.to_i < 60 end |
#oauth2_refresh_headers ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/inferno/dsl/auth_info.rb', line 238 def oauth2_refresh_headers base_headers = { 'Content-Type' => 'application/x-www-form-urlencoded' } return base_headers unless auth_type == 'symmetric' credentials = "#{client_id}:#{client_secret}" base_headers.merge( 'Authorization' => "Basic #{Base64.strict_encode64(credentials)}" ) end |
#oauth2_refresh_params ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/inferno/dsl/auth_info.rb', line 193 def oauth2_refresh_params case auth_type when 'public' public_auth_refresh_params when 'symmetric' symmetric_auth_refresh_params when 'asymmetric' asymmetric_auth_refresh_params when 'backend_services' backend_services_auth_refresh_params end end |
#private_key ⇒ Object
251 252 253 254 255 256 |
# File 'lib/inferno/dsl/auth_info.rb', line 251 def private_key @private_key ||= JWKS.jwks(user_jwks: jwks) .select { |key| key[:key_ops]&.include?('sign') } .select { |key| key[:alg] == encryption_algorithm } .find { |key| !kid || key[:kid] == kid } end |
#public_auth_refresh_params ⇒ Object
215 216 217 |
# File 'lib/inferno/dsl/auth_info.rb', line 215 def public_auth_refresh_params symmetric_auth_refresh_params.merge('client_id' => client_id) end |
#signing_key ⇒ Object
259 260 261 262 263 264 265 266 |
# File 'lib/inferno/dsl/auth_info.rb', line 259 def signing_key if private_key.nil? raise Inferno::Exceptions::AssertionException, "No signing key found for inputs: encryption method = '#{encryption_algorithm}' and kid = '#{kid}'" end @private_key.signing_key end |
#symmetric_auth_refresh_params ⇒ Object
207 208 209 210 211 212 |
# File 'lib/inferno/dsl/auth_info.rb', line 207 def symmetric_auth_refresh_params { 'grant_type' => 'refresh_token', 'refresh_token' => refresh_token } end |
#to_hash ⇒ Object
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/inferno/dsl/auth_info.rb', line 147 def to_hash self.class::ATTRIBUTES.each_with_object({}) do |attribute, hash| value = send(attribute) next if value.nil? value = issue_time.iso8601 if attribute == :issue_time hash[attribute] = value end end |
#to_s ⇒ Object
159 160 161 |
# File 'lib/inferno/dsl/auth_info.rb', line 159 def to_s JSON.generate(to_hash) end |
#update_from_response_body(request) ⇒ Object
295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/inferno/dsl/auth_info.rb', line 295 def update_from_response_body(request) token_response_body = JSON.parse(request.response_body) expires_in = token_response_body['expires_in'].is_a?(Numeric) ? token_response_body['expires_in'] : nil self.access_token = token_response_body['access_token'] self.refresh_token = token_response_body['refresh_token'] if token_response_body['refresh_token'].present? self.expires_in = expires_in self.issue_time = DateTime.now add_to_client(client) self end |