Class: SimpleOAuth::OAuth2::Token

Inherits:
Object
  • Object
show all
Defined in:
lib/simple_oauth/oauth2/token.rb,
sig/simple_oauth/oauth2.rbs

Overview

An access token from a successful token response

Constant Summary collapse

NO_ACCESS_TOKEN =

The description of a token response that carries no usable access token

Returns:

  • (String)
"token response has no access_token"
INVALID_EXPIRES_IN =

The description of a token response whose lifetime is not a number of seconds

Returns:

  • (String)
"token response has an invalid expires_in"
INVALID_ACCESS_TOKEN =

The error message for an access token that cannot be used

Returns:

  • (String)
"The access_token must be a non-empty String"
INVALID_LIFETIME =

The error message for a token lifetime that is not a number of seconds

Returns:

  • (String)
"The expires_in must be a number of seconds"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params, issued_at: Time.now) ⇒ Token

Initialize a token from the parameters of a token response

Examples:

SimpleOAuth::OAuth2::Token.new({"access_token" => "abc", "expires_in" => 3600})

Parameters:

  • params (Hash)

    the token response parameters

  • issued_at (Time) (defaults to: Time.now)

    when the token was issued, used to compute its expiration

  • issued_at: (Time) (defaults to: Time.now)

Raises:

  • (KeyError)

    if the parameters have no access_token

  • (ArgumentError)

    if the access token cannot be used, or the lifetime is not a number of seconds



146
147
148
149
150
151
152
153
154
155
# File 'lib/simple_oauth/oauth2/token.rb', line 146

def initialize(params, issued_at: Time.now)
  @params = params.transform_keys(&:to_s).freeze
  @access_token = validated_access_token
  @token_type = @params["token_type"]
  @expires_in = validated_expires_in
  @refresh_token = @params["refresh_token"]
  @scope = @params["scope"]
  @expires_at = @expires_in&.then { |seconds| issued_at + seconds }
  freeze
end

Instance Attribute Details

#access_tokenString (readonly)

The access token

Examples:

token.access_token # => "2YotnFZFEjr1zCsicMWpAA"

Returns:

  • (String)

    the access token



30
31
32
# File 'lib/simple_oauth/oauth2/token.rb', line 30

def access_token
  @access_token
end

#expires_atTime? (readonly)

The time when the access token expires

Examples:

token.expires_at # => 2026-09-11 13:00:00 UTC

Returns:

  • (Time, nil)

    the expiration time



70
71
72
# File 'lib/simple_oauth/oauth2/token.rb', line 70

def expires_at
  @expires_at
end

#expires_inInteger? (readonly)

The lifetime of the access token in seconds

Examples:

token.expires_in # => 3600

Returns:

  • (Integer, nil)

    the lifetime in seconds



46
47
48
# File 'lib/simple_oauth/oauth2/token.rb', line 46

def expires_in
  @expires_in
end

#paramsHash{String => Object} (readonly)

Every parameter of the token response, including nonstandard ones

Examples:

token.params["example_parameter"] # => "example_value"

Returns:

  • (Hash{String => Object})

    the parameters



78
79
80
# File 'lib/simple_oauth/oauth2/token.rb', line 78

def params
  @params
end

#refresh_tokenString? (readonly)

The refresh token, if one was issued

Examples:

token.refresh_token # => "tGzv3JOkF0XG5Qx2TlKWIA"

Returns:

  • (String, nil)

    the refresh token



54
55
56
# File 'lib/simple_oauth/oauth2/token.rb', line 54

def refresh_token
  @refresh_token
end

#scopeString? (readonly)

The granted scope, as a space-delimited string

Examples:

token.scope # => "tweet.read users.read"

Returns:

  • (String, nil)

    the granted scope



62
63
64
# File 'lib/simple_oauth/oauth2/token.rb', line 62

def scope
  @scope
end

#token_typeString? (readonly)

The token type, such as bearer

Examples:

token.token_type # => "bearer"

Returns:

  • (String, nil)

    the token type



38
39
40
# File 'lib/simple_oauth/oauth2/token.rb', line 38

def token_type
  @token_type
end

Class Method Details

.access_token?(value) ⇒ 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.

Check whether a value is usable as an access token (RFC 6749 Section A.12)

Examples:

SimpleOAuth::OAuth2::Token.access_token?("2YotnFZFEjr1zCsicMWpAA") # => true

Parameters:

  • value (Object)

    the value from the token response

Returns:

  • (Boolean)

    true if the value is a non-empty String



87
88
89
# File 'lib/simple_oauth/oauth2/token.rb', line 87

def self.access_token?(value)
  value.is_a?(String) && !value.empty?
end

.expires_in?(value) ⇒ 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.

Check whether a value is usable as a token lifetime (RFC 6749 Section 5.1)

Examples:

SimpleOAuth::OAuth2::Token.expires_in?(3600) # => true

Parameters:

  • value (Object)

    the value from the token response

Returns:

  • (Boolean)

    true if the value is absent, or a number of seconds



98
99
100
# File 'lib/simple_oauth/oauth2/token.rb', line 98

def self.expires_in?(value)
  value.nil? || !Integer(value, exception: false).nil?
end

.from_response(status:, body:, issued_at: Time.now) ⇒ Token

Parse a token response, raising the endpoint's error if it failed

Examples:

SimpleOAuth::OAuth2::Token.from_response(status: 200, body: '{"access_token":"abc","token_type":"bearer"}')

Parameters:

  • status (Integer, String)

    the HTTP status of the response

  • body (String, nil)

    the response body

  • issued_at (Time) (defaults to: Time.now)

    when the token was issued, used to compute its expiration

  • status: (Integer, String)
  • body: (String, nil)
  • issued_at: (Time) (defaults to: Time.now)

Returns:

Raises:

  • (Error)

    if the response is not successful, or carries no usable token

  • (ArgumentError)

    if the status is not an HTTP status



126
127
128
129
130
131
132
133
134
135
# File 'lib/simple_oauth/oauth2/token.rb', line 126

def self.from_response(status:, body:, issued_at: Time.now)
  code = Error.http_status(status)
  raise Error.from_response(status:, body:) unless (200..299).cover?(code)

  params = ResponseBody.parse(body)
  reason = rejection_reason(params)
  raise Error.new(code: nil, description: reason, status: code) if reason

  new(params, issued_at:)
end

.rejection_reason(params) ⇒ String?

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.

The reason the parameters of a token response cannot be used, if there is one

Examples:

SimpleOAuth::OAuth2::Token.rejection_reason({"access_token" => "abc"}) # => nil

Parameters:

  • params (Hash)

    the token response parameters

Returns:

  • (String, nil)

    the reason, or nil if the response is usable



109
110
111
112
113
# File 'lib/simple_oauth/oauth2/token.rb', line 109

def self.rejection_reason(params)
  return NO_ACCESS_TOKEN unless access_token?(params["access_token"])

  INVALID_EXPIRES_IN unless expires_in?(params["expires_in"])
end

Instance Method Details

#expired?(leeway: 0, now: Time.now) ⇒ Boolean

Check whether the access token has expired, or will within a leeway

Examples:

Refresh a token that expires within 30 seconds

token.expired?(leeway: 30)

Parameters:

  • leeway (Numeric) (defaults to: 0)

    seconds before expiration to treat the token as expired

  • now (Time) (defaults to: Time.now)

    the current time

  • leeway: (Numeric) (defaults to: 0)
  • now: (Time) (defaults to: Time.now)

Returns:

  • (Boolean)

    true if the token has expired; false if it has not or never expires



175
176
177
178
179
# File 'lib/simple_oauth/oauth2/token.rb', line 175

def expired?(leeway: 0, now: Time.now)
  return false if expires_at.nil?

  now >= expires_at - leeway
end

#scopesArray<String>

The granted scopes

Examples:

token.scopes # => ["tweet.read", "users.read"]

Returns:

  • (Array<String>)

    the granted scopes



163
164
165
# File 'lib/simple_oauth/oauth2/token.rb', line 163

def scopes
  scope.to_s.split
end

#validated_access_tokenString

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.

The access token from the parameters, which must be usable

Returns:

  • (String)

    the access token

Raises:

  • (KeyError)

    if the parameters have no access_token

  • (ArgumentError)

    if the access token is not a non-empty String



189
190
191
192
193
194
# File 'lib/simple_oauth/oauth2/token.rb', line 189

def validated_access_token
  token = params.fetch("access_token")
  raise ArgumentError, INVALID_ACCESS_TOKEN unless self.class.access_token?(token)

  token
end

#validated_expires_inInteger?

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.

The lifetime from the parameters, which must be a number of seconds

Returns:

  • (Integer, nil)

    the lifetime in seconds, or nil if the response carries none

Raises:

  • (ArgumentError)

    if the lifetime is not a number of seconds



201
202
203
204
205
206
# File 'lib/simple_oauth/oauth2/token.rb', line 201

def validated_expires_in
  seconds = params["expires_in"]
  raise ArgumentError, INVALID_LIFETIME unless self.class.expires_in?(seconds)

  seconds && Integer(seconds)
end