Class: Inferno::DSL::AuthInfo
- Inherits:
-
Object
- Object
- Inferno::DSL::AuthInfo
- Includes:
- Entities::Attributes
- Defined in:
- lib/inferno/dsl/auth_info.rb
Overview
AuthInfo provides 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` - Confidential symmetric (i.e., with a static client id and
secret)
- `asymmetric` - Confidential asymmetric (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.
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 ⇒ Object
The http method which will be used to perform the request to the authorization endpoint.
-
#auth_type ⇒ Object
The type of authorization to be performed.
-
#auth_url ⇒ Object
The url of the authorization endpoint.
-
#client ⇒ Object
Returns the value of attribute client.
- #client_id ⇒ Object
- #client_secret ⇒ Object
-
#encryption_algorithm ⇒ Object
The encryption algorithm which will be used to sign the JWT client credentials.
-
#expires_in ⇒ Object
The lifetime of the access token in seconds.
-
#issue_time ⇒ Object
An iso8601 formatted string representing the time the access token was issued.
-
#jwks ⇒ Object
A JWKS (including private keys) which will be used instead of Inferno’s default JWKS if provided.
-
#kid ⇒ Object
The key id for the keys to be used to sign the JWT client credentials.
- #name ⇒ Object
-
#pkce_code_challenge_method ⇒ Object
Either ‘S256` (default) or `plain`.
-
#pkce_support ⇒ Object
Whether PKCE will be used during authorization.
- #redirect_url ⇒ Object
- #refresh_token ⇒ Object
-
#requested_scopes ⇒ Object
The scopes which will be requested during authorization.
-
#token_url ⇒ Object
The url of the auth server’s token endpoint.
Class Method Summary collapse
-
.default_auth_type_component ⇒ Hash
Returns the default configuration for the “auth_type” component.
-
.default_auth_type_component_without_backend_services ⇒ Hash
Returns the default configuration for the “auth_type” component without the option for backend services auth.
Instance Method Summary collapse
- #able_to_refresh? ⇒ Boolean
- #add_to_client(client) ⇒ Object
-
#asymmetric_auth? ⇒ Boolean
Returns true when using confidential asymmetric auth.
- #asymmetric_auth_refresh_params ⇒ Object
- #auth_jwt_claims ⇒ Object
- #auth_jwt_header ⇒ Object
- #backend_services? ⇒ Boolean
-
#backend_services_auth? ⇒ Boolean
Returns true when using backend services auth.
- #backend_services_auth_refresh_params ⇒ Object
- #client_assertion ⇒ Object
-
#get_auth_request? ⇒ Boolean
Returns true when using GET as the authorization request method.
-
#initialize(raw_attributes_hash) ⇒ AuthInfo
constructor
A new instance of AuthInfo.
- #need_to_refresh? ⇒ Boolean
- #oauth2_refresh_headers ⇒ Object
- #oauth2_refresh_params ⇒ Object
-
#pkce_enabled? ⇒ Boolean
Returns true when pkce is enabled.
-
#plain_code_challenge_method? ⇒ Boolean
Returns true when using the palin pkce code challenge method.
-
#post_auth_request? ⇒ Boolean
Returns true when using POST as the authorization request method.
- #private_key ⇒ Object
-
#public_auth? ⇒ Boolean
Returns true when using public auth.
- #public_auth_refresh_params ⇒ Object
-
#s256_code_challenge_method? ⇒ Boolean
Returns true when using the S256 pkce code challenge method.
- #signing_key ⇒ Object
-
#symmetric_auth? ⇒ Boolean
Returns true when using confidential symmetric auth.
- #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.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/inferno/dsl/auth_info.rb', line 135 def initialize(raw_attributes_hash) # rubocop:disable Metrics/CyclomaticComplexity 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) rescue Date::Error Inferno::Application['logger'].error("Received invalid date: #{value.inspect}") 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 100
|
#auth_request_method ⇒ Object
The http method which will be used to perform the request to the authorization endpoint. Either ‘get` (default) or `post`
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#auth_type ⇒ Object
The type of authorization to be performed. One of ‘public`, `symmetric`, `asymmetric`, or `backend_services`
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#auth_url ⇒ Object
The url of the authorization endpoint
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#client ⇒ Object
Returns the value of attribute client.
98 99 100 |
# File 'lib/inferno/dsl/auth_info.rb', line 98 def client @client end |
#client_id ⇒ Object
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#client_secret ⇒ Object
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#encryption_algorithm ⇒ Object
The encryption algorithm which will be used to sign the JWT client credentials. Either ‘es384` (default) or `rs384`
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#expires_in ⇒ Object
The lifetime of the access token in seconds
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#issue_time ⇒ Object
An iso8601 formatted string representing the time the access token was issued
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#jwks ⇒ Object
A JWKS (including private keys) which will be used instead of Inferno’s default JWKS if provided
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#kid ⇒ Object
The key id for the keys to be used to sign the JWT client credentials. When blank, the first key for the selected encryption algorithm will be used
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#name ⇒ Object
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#pkce_code_challenge_method ⇒ Object
Either ‘S256` (default) or `plain`
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#pkce_support ⇒ Object
Whether PKCE will be used during authorization. Either ‘enabled` or `disabled`.
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#redirect_url ⇒ Object
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#refresh_token ⇒ Object
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#requested_scopes ⇒ Object
The scopes which will be requested during authorization
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
#token_url ⇒ Object
The url of the auth server’s token endpoint
|
# File 'lib/inferno/dsl/auth_info.rb', line 100
|
Class Method Details
.default_auth_type_component ⇒ Hash
Returns the default configuration for the “auth_type” component
318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/inferno/dsl/auth_info.rb', line 318 def self.default_auth_type_component { name: :auth_type, options: { list_options: [ { label: 'Public', value: 'public' }, { label: 'Confidential Symmetric', value: 'symmetric' }, { label: 'Confidential Asymmetric', value: 'asymmetric' }, { label: 'Backend Services', value: 'backend_services' } ] } } end |
.default_auth_type_component_without_backend_services ⇒ Hash
Returns the default configuration for the “auth_type” component without the option for backend services auth
335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/inferno/dsl/auth_info.rb', line 335 def self.default_auth_type_component_without_backend_services { name: :auth_type, options: { list_options: [ { label: 'Public', value: 'public' }, { label: 'Confidential Symmetric', value: 'symmetric' }, { label: 'Confidential Asymmetric', value: 'asymmetric' } ] } } end |
Instance Method Details
#able_to_refresh? ⇒ Boolean
190 191 192 |
# File 'lib/inferno/dsl/auth_info.rb', line 190 def able_to_refresh? token_url.present? && (backend_services? || refresh_token.present?) end |
#add_to_client(client) ⇒ Object
171 172 173 174 175 176 177 178 |
# File 'lib/inferno/dsl/auth_info.rb', line 171 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? ⇒ Boolean
Returns true when using confidential asymmetric auth
362 363 364 |
# File 'lib/inferno/dsl/auth_info.rb', line 362 def asymmetric_auth? auth_type&.casecmp? 'asymmetric' end |
#asymmetric_auth_refresh_params ⇒ Object
227 228 229 230 231 232 |
# File 'lib/inferno/dsl/auth_info.rb', line 227 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
286 287 288 289 290 291 292 293 294 |
# File 'lib/inferno/dsl/auth_info.rb', line 286 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
276 277 278 279 280 281 282 283 |
# File 'lib/inferno/dsl/auth_info.rb', line 276 def auth_jwt_header { 'alg' => encryption_algorithm, 'kid' => private_key['kid'], 'typ' => 'JWT', 'jku' => Inferno::Application['jwks_url'] } end |
#backend_services? ⇒ Boolean
195 196 197 |
# File 'lib/inferno/dsl/auth_info.rb', line 195 def backend_services? auth_type == 'backend_services' end |
#backend_services_auth? ⇒ Boolean
Returns true when using backend services auth
368 369 370 |
# File 'lib/inferno/dsl/auth_info.rb', line 368 def backend_services_auth? auth_type&.casecmp? 'backend_services' end |
#backend_services_auth_refresh_params ⇒ Object
235 236 237 238 239 240 241 242 |
# File 'lib/inferno/dsl/auth_info.rb', line 235 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
297 298 299 |
# File 'lib/inferno/dsl/auth_info.rb', line 297 def client_assertion JWT.encode auth_jwt_claims, signing_key, encryption_algorithm, auth_jwt_header end |
#get_auth_request? ⇒ Boolean
Returns true when using GET as the authorization request method
374 375 376 |
# File 'lib/inferno/dsl/auth_info.rb', line 374 def get_auth_request? auth_request_method&.casecmp? 'get' end |
#need_to_refresh? ⇒ Boolean
181 182 183 184 185 186 187 |
# File 'lib/inferno/dsl/auth_info.rb', line 181 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
245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/inferno/dsl/auth_info.rb', line 245 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
200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/inferno/dsl/auth_info.rb', line 200 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 |
#pkce_enabled? ⇒ Boolean
Returns true when pkce is enabled
386 387 388 |
# File 'lib/inferno/dsl/auth_info.rb', line 386 def pkce_enabled? pkce_support&.casecmp? 'enabled' end |
#plain_code_challenge_method? ⇒ Boolean
Returns true when using the palin pkce code challenge method
398 399 400 |
# File 'lib/inferno/dsl/auth_info.rb', line 398 def plain_code_challenge_method? pkce_code_challenge_method&.casecmp? 'plain' end |
#post_auth_request? ⇒ Boolean
Returns true when using POST as the authorization request method
380 381 382 |
# File 'lib/inferno/dsl/auth_info.rb', line 380 def post_auth_request? auth_request_method&.casecmp? 'post' end |
#private_key ⇒ Object
258 259 260 261 262 263 |
# File 'lib/inferno/dsl/auth_info.rb', line 258 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? ⇒ Boolean
Returns true when using public auth
350 351 352 |
# File 'lib/inferno/dsl/auth_info.rb', line 350 def public_auth? auth_type&.casecmp? 'public' end |
#public_auth_refresh_params ⇒ Object
222 223 224 |
# File 'lib/inferno/dsl/auth_info.rb', line 222 def public_auth_refresh_params symmetric_auth_refresh_params.merge('client_id' => client_id) end |
#s256_code_challenge_method? ⇒ Boolean
Returns true when using the S256 pkce code challenge method
392 393 394 |
# File 'lib/inferno/dsl/auth_info.rb', line 392 def s256_code_challenge_method? pkce_code_challenge_method&.casecmp? 'S256' end |
#signing_key ⇒ Object
266 267 268 269 270 271 272 273 |
# File 'lib/inferno/dsl/auth_info.rb', line 266 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? ⇒ Boolean
Returns true when using confidential symmetric auth
356 357 358 |
# File 'lib/inferno/dsl/auth_info.rb', line 356 def symmetric_auth? auth_type&.casecmp? 'symmetric' end |
#symmetric_auth_refresh_params ⇒ Object
214 215 216 217 218 219 |
# File 'lib/inferno/dsl/auth_info.rb', line 214 def symmetric_auth_refresh_params { 'grant_type' => 'refresh_token', 'refresh_token' => refresh_token } end |
#to_hash ⇒ Object
154 155 156 157 158 159 160 161 162 163 |
# File 'lib/inferno/dsl/auth_info.rb', line 154 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
166 167 168 |
# File 'lib/inferno/dsl/auth_info.rb', line 166 def to_s JSON.generate(to_hash) end |
#update_from_response_body(request) ⇒ Object
302 303 304 305 306 307 308 309 310 311 312 313 314 |
# File 'lib/inferno/dsl/auth_info.rb', line 302 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) if client self end |