Class: Ably::Auth
- Inherits:
-
Object
- Object
- Ably::Auth
- Includes:
- Modules::Conversions, Modules::HttpHelpers
- Defined in:
- lib/ably/auth.rb
Overview
Creates Ably Models::TokenRequest objects and obtains Ably Tokens from Ably to subsequently issue to less trusted clients.
Constant Summary collapse
- TOKEN_DEFAULTS =
Default capability Hash object and TTL in seconds for issued tokens
{ renew_token_buffer: 10 # buffer to allow a token to be reissued before the token is considered expired (Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER) }.freeze
- API_KEY_REGEX =
/^[\w-]{2,}\.[\w-]{2,}:[\w-]{2,}$/
- AUTH_OPTIONS_KEYS =
Supported AuthOption keys, see www.ably.com/docs/realtime/types#auth-options TODO: Review client_id usage embedded incorrectly within AuthOptions.
This is legacy code to configure a client with a client_id from the ClientOptions
TODO: Review inclusion of use_token_auth, ttl, token_params in auth options
%w( auth_callback auth_url auth_method auth_headers auth_params client_id key key_name key_secret query_time token token_details token_params ttl use_token_auth )
Instance Attribute Summary collapse
-
#current_token_details ⇒ Object
readonly
Returns the value of attribute current_token_details.
-
#options ⇒ Object
(also: #auth_options)
readonly
Returns the value of attribute options.
-
#token_params ⇒ Object
readonly
Returns the value of attribute token_params.
Instance Method Summary collapse
-
#auth_header ⇒ String
Auth header string used in HTTP requests to Ably Will reauthorize implicitly if required and capable.
-
#auth_params ⇒ Hash
Auth params used in URI endpoint for Realtime connections Will reauthorize implicitly if required and capable.
-
#authentication_security_requirements_met? ⇒ Boolean
Returns false when attempting to send an API Key over a non-secure connection Token auth must be used for non-secure connections.
-
#authorise(*args, &block) ⇒ Object
deprecated
Deprecated.
Use #authorize instead
-
#authorize(token_params = nil, auth_options = nil) ⇒ Models::TokenRequest
Instructs the library to get a new token immediately.
-
#can_assume_client_id?(assumed_client_id) ⇒ Boolean
private
True if assumed_client_id is compatible with the client’s configured or Ably assigned
client_id
. - #client_id ⇒ Object
-
#client_id_for_request ⇒ Object
ClientId that needs to be included with every rest/realtime request spec - RSA7e.
-
#client_id_validated? ⇒ Boolean
When a client has authenticated with Ably and the client is either anonymous (cannot assume a
client_id
) or has an assignedclient_id
(implicit in all operations), then this client has a validatedclient_id
, even if that client_id isnil
(anonymous). -
#configure_client_id(new_client_id) ⇒ Object
private
Configures the client ID for this client Typically this occurs following an Auth or receiving a Models::ProtocolMessage with a
client_id
in the Models::ConnectionDetails. -
#create_token_request(token_params = {}, auth_options = {}) ⇒ Models::TokenRequest
Creates and signs an Models::TokenRequest based on the specified (or if none specified, the client library stored) ‘token_params` and `auth_options`.
-
#extra_auth_headers ⇒ Hash
Extra headers that may be used during authentication.
-
#has_client_id? ⇒ Boolean
private
True when a client_id other than a wildcard is configured for Auth.
-
#initialize(client, token_params, auth_options) ⇒ Auth
constructor
Creates an Auth object.
- #key ⇒ Object
- #key_name ⇒ Object
- #key_secret ⇒ Object
-
#request_token(token_params = {}, auth_options = {}) ⇒ Ably::Models::TokenDetails
Calls the requestToken REST API endpoint to obtain an Ably Token according to the specified ‘token_params` and `auth_options`.
-
#token_client_id_allowed?(token_client_id) ⇒ Boolean
private
True if token provided client_id is compatible with the client’s configured
client_id
, when applicable. -
#token_renewable? ⇒ Boolean
True if prerequisites for creating a new token request are present.
-
#using_basic_auth? ⇒ Boolean
True when Basic Auth is being used to authenticate with Ably.
-
#using_token_auth? ⇒ Boolean
True when Token Auth is being used to authenticate with Ably.
Constructor Details
#initialize(client, token_params, auth_options) ⇒ Auth
Creates an Auth object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/ably/auth.rb', line 53 def initialize(client, token_params, ) unless .kind_of?(Hash) raise ArgumentError, 'Expected auth_options to be a Hash' end unless token_params.kind_of?(Hash) raise ArgumentError, 'Expected token_params to be a Hash' end # Ensure instance variables are defined @client_id = nil @client_id_validated = nil ensure_valid_auth_attributes @client = client @options = .dup @token_params = token_params.dup @token_option = [:token] || [:token_details] if [:key] && ([:key_secret] || [:key_name]) raise ArgumentError, 'key and key_name or key_secret are mutually exclusive. Provider either a key or key_name & key_secret' end split_api_key_into_key_and_secret! if [:key] if using_basic_auth? && !api_key_present? raise ArgumentError, 'key is missing. Either an API key, token, or token auth method must be provided' end if [:client_id] == '*' raise ArgumentError, 'A client cannot be configured with a wildcard client_id, only a token can have a wildcard client_id privilege' end if has_client_id? && !token_creatable_externally? && !token_option @client_id = ensure_utf_8(:client_id, client_id) if client_id end # If a token details object or token string is provided in the initializer # then the client can be authorized immediately using this token if token_option token_details = convert_to_token_details(token_option) if token_details begin token_details = (token_details) logger.debug { "Auth: new token passed in to the initializer: #{token_details}" } rescue StandardError => e logger.error { "Auth: Implicit authorization using the provided token failed: #{e}" } end end end @options.freeze @token_params.freeze end |
Instance Attribute Details
#current_token_details ⇒ Object (readonly)
Returns the value of attribute current_token_details.
43 44 45 |
# File 'lib/ably/auth.rb', line 43 def current_token_details @current_token_details end |
#options ⇒ Object (readonly) Also known as: auth_options
Returns the value of attribute options.
43 44 45 |
# File 'lib/ably/auth.rb', line 43 def @options end |
#token_params ⇒ Object (readonly)
Returns the value of attribute token_params.
43 44 45 |
# File 'lib/ably/auth.rb', line 43 def token_params @token_params end |
Instance Method Details
#auth_header ⇒ String
Auth header string used in HTTP requests to Ably Will reauthorize implicitly if required and capable
405 406 407 408 409 410 411 |
# File 'lib/ably/auth.rb', line 405 def auth_header if using_token_auth? token_auth_header else basic_auth_header end end |
#auth_params ⇒ Hash
Auth params used in URI endpoint for Realtime connections Will reauthorize implicitly if required and capable
435 436 437 438 439 440 441 |
# File 'lib/ably/auth.rb', line 435 def auth_params if using_token_auth? token_auth_params else basic_auth_params end end |
#authentication_security_requirements_met? ⇒ Boolean
Returns false when attempting to send an API Key over a non-secure connection Token auth must be used for non-secure connections
459 460 461 |
# File 'lib/ably/auth.rb', line 459 def authentication_security_requirements_met? client.use_tls? || using_token_auth? end |
#authorise(*args, &block) ⇒ Object
Use #authorize instead
190 191 192 193 |
# File 'lib/ably/auth.rb', line 190 def (*args, &block) logger.warn { "Auth#authorise is deprecated and will be removed in 1.0. Please use Auth#authorize instead" } (*args, &block) end |
#authorize(token_params = nil, auth_options = nil) ⇒ Models::TokenRequest
Instructs the library to get a new token immediately. When using the realtime client, it upgrades the current realtime connection to use the new token, or if not connected, initiates a connection to Ably, once the new token has been obtained. Also stores any ‘token_params` and `auth_options` passed in as the new defaults, to be used for all subsequent implicit or explicit token requests. Any `token_params` and `auth_options` objects passed in entirely replace, as opposed to being merged with, the current client library saved values.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/ably/auth.rb', line 135 def (token_params = nil, = nil) if .nil? = # Use default options if .has_key?(:query_time) @options = .dup # Query the server time only happens once # the options remain in auth_options though so they are passed to request_token @options.delete(:query_time) @options.freeze end else ensure_valid_auth_attributes = .dup if [:token_params] token_params = .delete(:token_params).merge(token_params || {}) end # If basic credentials are provided then overwrite existing options # otherwise we need to retain the existing credentials in the auth options split_api_key_into_key_and_secret! if [:key] if [:key_name] && [:key_secret] end @options = .dup # Query the server time only happens once # the options remain in auth_options though so they are passed to request_token @options.delete(:query_time) @options.freeze end # Unless provided, defaults are used unless token_params.nil? @token_params = token_params.dup # Timestamp is only valid for this request @token_params.delete(:timestamp) @token_params.freeze end (request_token(token_params || @token_params, )).tap do |new_token_details| logger.debug { "Auth: new token following authorisation: #{new_token_details}" } # If authorize the realtime library required auth, then yield the token in a block if block_given? yield new_token_details end end end |
#can_assume_client_id?(assumed_client_id) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
True if assumed_client_id is compatible with the client’s configured or Ably assigned client_id
477 478 479 480 481 482 483 484 485 |
# File 'lib/ably/auth.rb', line 477 def can_assume_client_id?(assumed_client_id) if client_id_validated? client_id == '*' || (client_id == assumed_client_id) elsif ![:client_id] || [:client_id] == '*' true # client ID is unknown else [:client_id] == assumed_client_id end end |
#client_id ⇒ Object
385 386 387 |
# File 'lib/ably/auth.rb', line 385 def client_id @client_id || [:client_id] end |
#client_id_for_request ⇒ Object
ClientId that needs to be included with every rest/realtime request spec - RSA7e
427 428 429 |
# File 'lib/ably/auth.rb', line 427 def client_id_for_request [:client_id] end |
#client_id_validated? ⇒ Boolean
When a client has authenticated with Ably and the client is either anonymous (cannot assume a client_id
) or has an assigned client_id
(implicit in all operations), then this client has a validated client_id
, even if that client_id is nil
(anonymous)
Once validated by Ably, the client library will enforce the use of the client_id
identity provided by Ably, rejecting messages with an invalid client_id
immediately
397 398 399 |
# File 'lib/ably/auth.rb', line 397 def client_id_validated? !!@client_id_validated end |
#configure_client_id(new_client_id) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Configures the client ID for this client Typically this occurs following an Auth or receiving a Models::ProtocolMessage with a client_id
in the Models::ConnectionDetails
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
# File 'lib/ably/auth.rb', line 491 def configure_client_id(new_client_id) if has_client_id? # If new client ID from Ably is a wildcard, but preconfigured clientId is set, then keep the existing clientId if new_client_id == "*" @client_id_validated = true return end # If client_id is defined and not a wildcard, prevent it changing, this is not supported if new_client_id != client_id raise Ably::Exceptions::IncompatibleClientId.new("Client ID is immutable once configured for a client. Client ID cannot be changed to '#{new_client_id}'") end end @client_id_validated = true @client_id = new_client_id end |
#create_token_request(token_params = {}, auth_options = {}) ⇒ Models::TokenRequest
Creates and signs an Models::TokenRequest based on the specified (or if none specified, the client library stored) ‘token_params` and `auth_options`. Note this can only be used when the API key value is available locally. Otherwise, the Models::TokenRequest must be obtained from the key owner. Use this to generate an Models::TokenRequest in order to implement an Ably Token request callback for use by other clients. Both `token_params` and `auth_options` are optional. When omitted or null, the default token parameters and authentication options for the client library are used, as specified in the `client_options` when the client library was instantiated, or later updated with an explicit authorize request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Models::TokenRequest may be issued to clients in favor of a token, see Token Authentication explained.
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/ably/auth.rb', line 309 def create_token_request(token_params = {}, = {}) ensure_valid_auth_attributes = .dup token_params = ([:token_params] || {}).merge(token_params) split_api_key_into_key_and_secret! if [:key] request_key_name = .delete(:key_name) || key_name request_key_secret = .delete(:key_secret) || key_secret raise Ably::Exceptions::TokenRequestFailed, 'Key Name and Key Secret are required to generate a new token request' unless request_key_name && request_key_secret ensure_current_time_is_based_on_server_time if [:query_time] = token_params.delete(:timestamp) || current_time = Time.at() if .kind_of?(Integer) token_request = { keyName: request_key_name, timestamp: (.to_f * 1000).round, nonce: token_params[:nonce] || SecureRandom.hex.force_encoding('UTF-8') } token_client_id = token_params[:client_id] || [:client_id] || client_id token_request[:clientId] = token_client_id if token_client_id if token_params[:ttl] token_ttl = [ token_params[:ttl], Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER + TOKEN_DEFAULTS.fetch(:renew_token_buffer) # never issue a token that will be immediately considered expired due to the buffer ].max token_request[:ttl] = (token_ttl * 1000).to_i end token_request[:capability] = token_params[:capability] if token_params[:capability] if token_request[:capability].is_a?(Hash) lexicographic_ordered_capabilities = Hash[ token_request[:capability].sort_by { |key, value| key }.map do |key, value| [key, value.sort] end ] token_request[:capability] = JSON.dump(lexicographic_ordered_capabilities) end token_request[:mac] = sign_params(token_request, request_key_secret) # Undocumented feature to request a persisted token token_request[:persisted] = token_params[:persisted] if token_params[:persisted] Models::TokenRequest.new(token_request) end |
#extra_auth_headers ⇒ Hash
Extra headers that may be used during authentication
416 417 418 419 420 421 422 |
# File 'lib/ably/auth.rb', line 416 def extra_auth_headers if client_id_for_request { 'X-Ably-ClientId' => Base64.urlsafe_encode64(client_id_for_request) } else {} end end |
#has_client_id? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
True when a client_id other than a wildcard is configured for Auth
510 511 512 |
# File 'lib/ably/auth.rb', line 510 def has_client_id? client_id && (client_id != '*') end |
#key ⇒ Object
362 363 364 |
# File 'lib/ably/auth.rb', line 362 def key "#{key_name}:#{key_secret}" if api_key_present? end |
#key_name ⇒ Object
366 367 368 |
# File 'lib/ably/auth.rb', line 366 def key_name @key_name end |
#key_secret ⇒ Object
370 371 372 |
# File 'lib/ably/auth.rb', line 370 def key_secret @key_secret end |
#request_token(token_params = {}, auth_options = {}) ⇒ Ably::Models::TokenDetails
Calls the requestToken REST API endpoint to obtain an Ably Token according to the specified ‘token_params` and `auth_options`. Both `token_params` and `auth_options` are optional. When omitted or null, the default token parameters and authentication options for the client library are used, as specified in the `client_options` when the client library was instantiated, or later updated with an explicit authorize request. Values passed in are used instead of, rather than being merged with, the default values. To understand why an Ably Models::TokenRequest may be issued to clients in favor of a token, see Token Authentication explained.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/ably/auth.rb', line 229 def request_token(token_params = {}, = {}) ensure_valid_auth_attributes # Token param precedence (lowest to highest): # Auth default => client_id => auth_options[:token_params] arg => token_params arg token_params = self.token_params.merge( (client_id ? { client_id: client_id } : {}). merge([:token_params] || {}). merge(token_params) ) = self..merge() token_request = if auth_callback = .delete(:auth_callback) begin Timeout::timeout(client.auth_request_timeout) do auth_callback.call(token_params) end rescue StandardError => err raise Ably::Exceptions::AuthenticationFailed.new("auth_callback failed: #{err.}", nil, nil, err, fallback_status: 500, fallback_code: Ably::Exceptions::Codes::CONNECTION_NOT_ESTABLISHED_NO_TRANSPORT_HANDLE) end elsif auth_url = .delete(:auth_url) begin Timeout::timeout(client.auth_request_timeout) do token_request_from_auth_url(auth_url, , token_params) end rescue StandardError => err raise Ably::Exceptions::AuthenticationFailed.new("auth_url failed: #{err.}", nil, nil, err, fallback_status: 500, fallback_code: Ably::Exceptions::Codes::CONNECTION_NOT_ESTABLISHED_NO_TRANSPORT_HANDLE) end else create_token_request(token_params, ) end convert_to_token_details(token_request).tap do |token_details| return token_details if token_details end send_token_request(token_request) end |
#token_client_id_allowed?(token_client_id) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
True if token provided client_id is compatible with the client’s configured client_id
, when applicable
467 468 469 470 471 |
# File 'lib/ably/auth.rb', line 467 def token_client_id_allowed?(token_client_id) return true if client_id.nil? # no explicit client_id specified for this client return true if client_id == '*' || token_client_id == '*' # wildcard supported always token_client_id == client_id end |
#token_renewable? ⇒ Boolean
True if prerequisites for creating a new token request are present
One of the following criterion must be met:
-
Valid API key and token option not provided as token options cannot be determined
-
Authentication callback for new token requests
-
Authentication URL for new token requests
451 452 453 |
# File 'lib/ably/auth.rb', line 451 def token_renewable? token_creatable_externally? || (api_key_present? && !token_option) end |
#using_basic_auth? ⇒ Boolean
True when Basic Auth is being used to authenticate with Ably
375 376 377 |
# File 'lib/ably/auth.rb', line 375 def using_basic_auth? !using_token_auth? end |
#using_token_auth? ⇒ Boolean
True when Token Auth is being used to authenticate with Ably
380 381 382 383 |
# File 'lib/ably/auth.rb', line 380 def using_token_auth? return [:use_token_auth] if .has_key?(:use_token_auth) !!(token_option || current_token_details || token_creatable_externally?) end |