Class: OAuth2::AccessToken

Inherits:
Object
  • Object
show all
Includes:
FilteredAttributes
Defined in:
lib/oauth2/access_token.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

TOKEN_KEYS_STR =
%w[access_token id_token token accessToken idToken].freeze
TOKEN_KEYS_SYM =
%i[access_token id_token token accessToken idToken].freeze
TOKEN_KEY_LOOKUP =
TOKEN_KEYS_STR + TOKEN_KEYS_SYM

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from FilteredAttributes

included, #inspect

Constructor Details

#initialize(client, token, opts = {}) ⇒ AccessToken

Note:

For “soon-to-expire”/”clock-skew” functionality see the :expires_latency option.

Note:

If no token is provided, the AccessToken will be considered invalid.
This is to prevent the possibility of a token being accidentally
created with no token value.
If you want to create an AccessToken with no token value,
you can pass in an empty string or nil for the token value.
If you want to create an AccessToken with no token value and
no refresh token, you can pass in an empty string or nil for the
token value and nil for the refresh token, and raise_errors: false.

Initialize an AccessToken

Parameters:

  • client (Client)

    the OAuth2::Client instance

  • token (String)

    the Access Token value (optional, may not be used in refresh flows)

  • opts (Hash) (defaults to: {})

    the options to create the Access Token with

Options Hash (opts):

  • :refresh_token (String) — default: nil

    the refresh_token value

  • :expires_in (FixNum, String) — default: nil

    the number of seconds in which the AccessToken will expire

  • :expires_at (FixNum, String) — default: nil

    the epoch time in seconds in which AccessToken will expire

  • :expires_latency (FixNum, String) — default: nil

    the number of seconds by which AccessToken validity will be reduced to offset latency, @version 2.0+

  • :mode (Symbol) — default: :header

    the transmission mode of the Access Token parameter value
    one of :header, :body or :query

  • :header_format (String) — default: 'Bearer %s'

    the string format to use for the Authorization header

  • :param_name (String) — default: 'access_token'

    the parameter name to use for transmission of the
    Access Token value in :body or :query transmission mode

  • :token_name (String) — default: nil

    the name of the response parameter that identifies the access token
    When nil one of TOKEN_KEY_LOOKUP will be used



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
# File 'lib/oauth2/access_token.rb', line 142

def initialize(client, token, opts = {})
  @client = client
  @token = token.to_s
  opts = opts.dup
  %i[refresh_token expires_in expires_at expires_latency].each do |arg|
    instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
  end
  no_tokens = (@token.nil? || @token.empty?) && (@refresh_token.nil? || @refresh_token.empty?)
  if no_tokens
    if @client.options[:raise_errors]
      raise Error.new({
        error: "OAuth2::AccessToken has no token",
        error_description: "Options are: #{opts.inspect}",
      })
    elsif !OAuth2.config.silence_no_tokens_warning
      warn("OAuth2::AccessToken has no token")
    end
  end
  # @option opts [Fixnum, String] :expires is deprecated
  @expires_in ||= opts.delete("expires")
  @expires_in &&= @expires_in.to_i
  @expires_at &&= convert_expires_at(@expires_at)
  @expires_latency &&= @expires_latency.to_i
  @expires_at ||= Time.now.to_i + @expires_in if @expires_in && !@expires_in.zero?
  @expires_at -= @expires_latency if @expires_latency
  @options = {
    mode: opts.delete(:mode) || :header,
    header_format: opts.delete(:header_format) || "Bearer %s",
    param_name: opts.delete(:param_name) || "access_token",
  }
  @options[:token_name] = opts.delete(:token_name) if opts.key?(:token_name)

  @params = opts
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



26
27
28
# File 'lib/oauth2/access_token.rb', line 26

def client
  @client
end

#expires_atObject (readonly)

Returns the value of attribute expires_at.



26
27
28
# File 'lib/oauth2/access_token.rb', line 26

def expires_at
  @expires_at
end

#expires_inObject (readonly)

Returns the value of attribute expires_in.



26
27
28
# File 'lib/oauth2/access_token.rb', line 26

def expires_in
  @expires_in
end

#expires_latencyObject (readonly)

Returns the value of attribute expires_latency.



26
27
28
# File 'lib/oauth2/access_token.rb', line 26

def expires_latency
  @expires_latency
end

#optionsObject

Returns the value of attribute options.



27
28
29
# File 'lib/oauth2/access_token.rb', line 27

def options
  @options
end

#paramsObject (readonly)

Returns the value of attribute params.



26
27
28
# File 'lib/oauth2/access_token.rb', line 26

def params
  @params
end

#refresh_tokenObject

Returns the value of attribute refresh_token.



27
28
29
# File 'lib/oauth2/access_token.rb', line 27

def refresh_token
  @refresh_token
end

#responseObject

Returns the value of attribute response.



27
28
29
# File 'lib/oauth2/access_token.rb', line 27

def response
  @response
end

#tokenObject (readonly)

Returns the value of attribute token.



26
27
28
# File 'lib/oauth2/access_token.rb', line 26

def token
  @token
end

Class Method Details

.from_hash(client, hash) ⇒ OAuth2::AccessToken

Note:

The method will use the first found token key in the following order:
‘access_token’, ‘id_token’, ‘token’ (or their symbolic versions)

Note:

If multiple token keys are present, a warning will be issued unless
OAuth2.config.silence_extra_tokens_warning is true

Note:

If no token keys are present, a warning will be issued unless
OAuth2.config.silence_no_tokens_warning is true

Note:

For “soon-to-expire”/”clock-skew” functionality see the :expires_latency option.

Note:

If snaky key conversion is being used, token_name needs to match the converted key.

Initializes an AccessToken from a Hash

Examples:

hash = { 'access_token' => 'token_value', 'refresh_token' => 'refresh_value' }
access_token = OAuth2::AccessToken.from_hash(client, hash)

Parameters:

  • client (OAuth2::Client)

    the OAuth2::Client instance

  • hash (Hash)

    a hash containing the token and other properties

Options Hash (hash):

  • 'access_token' (String)

    the access token value

  • 'id_token' (String)

    alternative key for the access token value

  • 'token' (String)

    alternative key for the access token value

  • 'refresh_token' (String) — default: optional

    the refresh token value

  • 'expires_in' (Integer, String) — default: optional

    number of seconds until token expires

  • 'expires_at' (Integer, String) — default: optional

    epoch time in seconds when token expires

  • 'expires_latency' (Integer, String) — default: optional

    seconds to reduce token validity by

Returns:



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
# File 'lib/oauth2/access_token.rb', line 57

def from_hash(client, hash)
  fresh = hash.dup
  # If token_name is present, then use that key name
  key =
    if fresh.key?(:token_name)
      t_key = fresh[:token_name]
      no_tokens_warning(fresh, t_key)
      t_key
    else
      # Otherwise, if one of the supported default keys is present, use whichever has precedence
      supported_keys = TOKEN_KEY_LOOKUP & fresh.keys
      t_key = supported_keys[0]
      extra_tokens_warning(supported_keys, t_key)
      t_key
    end
  # :nocov:
  # TODO: Get rid of this branching logic when dropping Hashie < v3.2
  token = if !defined?(Hashie::VERSION) # i.e. <= "1.1.0"; the first Hashie to ship with a VERSION constant
    warn("snaky_hash and oauth2 will drop support for Hashie v0 in the next major version. Please upgrade to a modern Hashie.")
    # There is a bug in Hashie v0, which is accounts for.
    fresh.delete(key) || fresh[key] || ""
  else
    fresh.delete(key) || ""
  end
  # :nocov:
  new(client, token, fresh)
end

.from_kvform(client, kvform) ⇒ AccessToken

Initializes an AccessToken from a key/value application/x-www-form-urlencoded string

Parameters:

  • client (Client)

    the OAuth2::Client instance

  • kvform (String)

    the application/x-www-form-urlencoded string

Returns:



90
91
92
# File 'lib/oauth2/access_token.rb', line 90

def from_kvform(client, kvform)
  from_hash(client, Rack::Utils.parse_query(kvform))
end

Instance Method Details

#[](key) ⇒ Object

Indexer to additional params present in token response

Parameters:

  • key (String)

    entry key to Hash



180
181
182
# File 'lib/oauth2/access_token.rb', line 180

def [](key)
  @params[key]
end

#delete(path, opts = {}, &block) ⇒ Object

Make a DELETE request with the Access Token

See Also:



362
363
364
# File 'lib/oauth2/access_token.rb', line 362

def delete(path, opts = {}, &block)
  request(:delete, path, opts, &block)
end

#expired?Boolean

Check if token is expired

Returns:

  • (Boolean)

    true if the token is expired, false otherwise



194
195
196
# File 'lib/oauth2/access_token.rb', line 194

def expired?
  expires? && (expires_at <= Time.now.to_i)
end

#expires?Boolean

Whether the token expires

Returns:

  • (Boolean)


187
188
189
# File 'lib/oauth2/access_token.rb', line 187

def expires?
  !!@expires_at
end

#get(path, opts = {}, &block) ⇒ Object

Make a GET request with the Access Token

See Also:



334
335
336
# File 'lib/oauth2/access_token.rb', line 334

def get(path, opts = {}, &block)
  request(:get, path, opts, &block)
end

#headersObject

Get the headers hash (includes Authorization token)



367
368
369
# File 'lib/oauth2/access_token.rb', line 367

def headers
  {"Authorization" => options[:header_format] % token}
end

#patch(path, opts = {}, &block) ⇒ Object

Make a PATCH request with the Access Token

See Also:



355
356
357
# File 'lib/oauth2/access_token.rb', line 355

def patch(path, opts = {}, &block)
  request(:patch, path, opts, &block)
end

#post(path, opts = {}, &block) ⇒ Object

Make a POST request with the Access Token

See Also:



341
342
343
# File 'lib/oauth2/access_token.rb', line 341

def post(path, opts = {}, &block)
  request(:post, path, opts, &block)
end

#put(path, opts = {}, &block) ⇒ Object

Make a PUT request with the Access Token

See Also:



348
349
350
# File 'lib/oauth2/access_token.rb', line 348

def put(path, opts = {}, &block)
  request(:put, path, opts, &block)
end

#refresh(params = {}, access_token_opts = {}) {|opts| ... } ⇒ OAuth2::AccessToken Also known as: refresh!

Note:

current token’s options are carried over to the new AccessToken

Refreshes the current Access Token

Parameters:

  • params (Hash) (defaults to: {})

    additional params to pass to the refresh token request

  • access_token_opts (Hash) (defaults to: {})

    options that will be passed to the AccessToken initialization

Yields:

  • (opts)

    The block to modify the refresh token request options

Yield Parameters:

  • opts (Hash)

    The options hash that can be modified

Returns:

Raises:



209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/oauth2/access_token.rb', line 209

def refresh(params = {}, access_token_opts = {}, &block)
  raise OAuth2::Error.new({error: "A refresh_token is not available"}) unless refresh_token

  params[:grant_type] = "refresh_token"
  params[:refresh_token] = refresh_token
  new_token = @client.get_token(params, access_token_opts, &block)
  new_token.options = options
  if new_token.refresh_token
    # Keep it if there is one
  else
    new_token.refresh_token = refresh_token
  end
  new_token
end

#request(verb, path, opts = {}) {|req| ... } ⇒ OAuth2::Response

Make a request with the Access Token

Parameters:

  • verb (Symbol)

    the HTTP request method

  • path (String)

    the HTTP URL path of the request

  • opts (Hash) (defaults to: {})

    the options to make the request with

Options Hash (opts):

  • :params (Hash)

    additional URL parameters

  • :body (Hash, String)

    the request body

  • :headers (Hash)

    request headers

Yields:

  • (req)

    The block to modify the request

Yield Parameters:

  • req (Faraday::Request)

    The request object that can be modified

Returns:

See Also:



326
327
328
329
# File 'lib/oauth2/access_token.rb', line 326

def request(verb, path, opts = {}, &block)
  configure_authentication!(opts)
  @client.request(verb, path, opts, &block)
end

#revoke(params = {}) {|req| ... } ⇒ OAuth2::Response Also known as: revoke!

Note:

If the token passed to the request
is an access token, the server MAY revoke the respective refresh
token as well.

Note:

If the token passed to the request
is a refresh token and the authorization server supports the
revocation of access tokens, then the authorization server SHOULD
also invalidate all access tokens based on the same authorization
grant

Note:

If the server responds with HTTP status code 503, your code must
assume the token still exists and may retry after a reasonable delay.
The server may include a “Retry-After” header in the response to
indicate how long the service is expected to be unavailable to the
requesting client.

Revokes the token at the authorization server

Parameters:

  • params (Hash) (defaults to: {})

    additional parameters to be sent during revocation

Options Hash (params):

  • :token_type_hint (String, Symbol, nil) — default: 'access_token' or 'refresh_token'

    hint about which token to revoke

  • :token_method (Symbol) — default: :post_with_query_string

    overrides OAuth2::Client#options[:token_method]

Yields:

  • (req)

    The block is passed the request being made, allowing customization

Yield Parameters:

  • req (Faraday::Request)

    The request object that can be modified

Returns:

Raises:

  • (OAuth2::Error)

    if token_type_hint is invalid or the specified token is not available

See Also:



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/oauth2/access_token.rb', line 258

def revoke(params = {}, &block)
  token_type_hint_orig = params.delete(:token_type_hint)
  token_type_hint = nil
  revoke_token = case token_type_hint_orig
  when "access_token", :access_token
    token_type_hint = "access_token"
    token
  when "refresh_token", :refresh_token
    token_type_hint = "refresh_token"
    refresh_token
  when nil
    if token
      token_type_hint = "access_token"
      token
    elsif refresh_token
      token_type_hint = "refresh_token"
      refresh_token
    end
  else
    raise OAuth2::Error.new({error: "token_type_hint must be one of [nil, :refresh_token, :access_token], so if you need something else consider using a subclass or entirely custom AccessToken class."})
  end
  raise OAuth2::Error.new({error: "#{token_type_hint || "unknown token type"} is not available for revoking"}) unless revoke_token && !revoke_token.empty?

  @client.revoke_token(revoke_token, token_type_hint, params, &block)
end

#to_hashHash

Note:

Don’t return expires_latency because it has already been deducted from expires_at

Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash

Returns:

  • (Hash)

    a hash of AccessToken property values



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/oauth2/access_token.rb', line 292

def to_hash
  hsh = {
    access_token: token,
    refresh_token: refresh_token,
    expires_at: expires_at,
    mode: options[:mode],
    header_format: options[:header_format],
    param_name: options[:param_name],
  }
  hsh[:token_name] = options[:token_name] if options.key?(:token_name)
  # TODO: Switch when dropping Ruby < 2.5 support
  # params.transform_keys(&:to_sym) # Ruby 2.5 only
  # Old Ruby transform_keys alternative:
  sheesh = @params.each_with_object({}) { |(k, v), memo|
    memo[k.to_sym] = v
  }
  sheesh.merge(hsh)
end