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.
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.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/inferno/dsl/auth_info.rb', line 135 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 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
|
Instance Method Details
#able_to_refresh? ⇒ Boolean
188 189 190 |
# File 'lib/inferno/dsl/auth_info.rb', line 188 def able_to_refresh? token_url.present? && (backend_services? || refresh_token.present?) end |
#add_to_client(client) ⇒ Object
169 170 171 172 173 174 175 176 |
# File 'lib/inferno/dsl/auth_info.rb', line 169 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
225 226 227 228 229 230 |
# File 'lib/inferno/dsl/auth_info.rb', line 225 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
284 285 286 287 288 289 290 291 292 |
# File 'lib/inferno/dsl/auth_info.rb', line 284 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
274 275 276 277 278 279 280 281 |
# File 'lib/inferno/dsl/auth_info.rb', line 274 def auth_jwt_header { 'alg' => encryption_algorithm, 'kid' => private_key['kid'], 'typ' => 'JWT', 'jku' => Inferno::Application['jwks_url'] } end |
#backend_services? ⇒ Boolean
193 194 195 |
# File 'lib/inferno/dsl/auth_info.rb', line 193 def backend_services? auth_type == 'backend_services' end |
#backend_services_auth_refresh_params ⇒ Object
233 234 235 236 237 238 239 240 |
# File 'lib/inferno/dsl/auth_info.rb', line 233 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
295 296 297 |
# File 'lib/inferno/dsl/auth_info.rb', line 295 def client_assertion JWT.encode auth_jwt_claims, signing_key, encryption_algorithm, auth_jwt_header end |
#need_to_refresh? ⇒ Boolean
179 180 181 182 183 184 185 |
# File 'lib/inferno/dsl/auth_info.rb', line 179 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
243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/inferno/dsl/auth_info.rb', line 243 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
198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/inferno/dsl/auth_info.rb', line 198 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
256 257 258 259 260 261 |
# File 'lib/inferno/dsl/auth_info.rb', line 256 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
220 221 222 |
# File 'lib/inferno/dsl/auth_info.rb', line 220 def public_auth_refresh_params symmetric_auth_refresh_params.merge('client_id' => client_id) end |
#signing_key ⇒ Object
264 265 266 267 268 269 270 271 |
# File 'lib/inferno/dsl/auth_info.rb', line 264 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
212 213 214 215 216 217 |
# File 'lib/inferno/dsl/auth_info.rb', line 212 def symmetric_auth_refresh_params { 'grant_type' => 'refresh_token', 'refresh_token' => refresh_token } end |
#to_hash ⇒ Object
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/inferno/dsl/auth_info.rb', line 152 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
164 165 166 |
# File 'lib/inferno/dsl/auth_info.rb', line 164 def to_s JSON.generate(to_hash) end |
#update_from_response_body(request) ⇒ Object
300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/inferno/dsl/auth_info.rb', line 300 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 |