Class: XeroKiwi::Token

Inherits:
Object
  • Object
show all
Defined in:
lib/xero_kiwi/token.rb

Overview

Immutable value object representing an OAuth2 token pair, plus the surrounding metadata Xero returns at refresh time (id_token, scope, etc).

token = XeroKiwi::Token.new(
  access_token:  "ya29...",
  refresh_token: "1//...",
  expires_at:    Time.now + 1800
)

token.expired?         # => false
token.expiring_soon?   # => false (default 60s window)
token.refreshable?     # => true

Use .from_oauth_response to build a Token from a raw Xero token-endpoint response — it converts Xero’s ‘expires_in` (seconds-from-now) into an absolute `expires_at` Time, anchored at the moment the request was made.

Constant Summary collapse

DEFAULT_EXPIRY_WINDOW =
60

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(access_token:, refresh_token: nil, expires_at: nil, token_type: "Bearer", id_token: nil, scope: nil) ⇒ Token

Returns a new instance of Token.



39
40
41
42
43
44
45
46
47
# File 'lib/xero_kiwi/token.rb', line 39

def initialize(access_token:, refresh_token: nil, expires_at: nil,
               token_type: "Bearer", id_token: nil, scope: nil)
  @access_token  = access_token
  @refresh_token = refresh_token
  @expires_at    = expires_at
  @token_type    = token_type
  @id_token      = id_token
  @scope         = scope
end

Instance Attribute Details

#access_tokenObject (readonly)

Returns the value of attribute access_token.



23
24
25
# File 'lib/xero_kiwi/token.rb', line 23

def access_token
  @access_token
end

#expires_atObject (readonly)

Returns the value of attribute expires_at.



23
24
25
# File 'lib/xero_kiwi/token.rb', line 23

def expires_at
  @expires_at
end

#id_tokenObject (readonly)

Returns the value of attribute id_token.



23
24
25
# File 'lib/xero_kiwi/token.rb', line 23

def id_token
  @id_token
end

#refresh_tokenObject (readonly)

Returns the value of attribute refresh_token.



23
24
25
# File 'lib/xero_kiwi/token.rb', line 23

def refresh_token
  @refresh_token
end

#scopeObject (readonly)

Returns the value of attribute scope.



23
24
25
# File 'lib/xero_kiwi/token.rb', line 23

def scope
  @scope
end

#token_typeObject (readonly)

Returns the value of attribute token_type.



23
24
25
# File 'lib/xero_kiwi/token.rb', line 23

def token_type
  @token_type
end

Class Method Details

.from_oauth_response(payload, requested_at: Time.now) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/xero_kiwi/token.rb', line 25

def self.from_oauth_response(payload, requested_at: Time.now)
  payload    = payload.transform_keys(&:to_s)
  expires_in = payload["expires_in"]

  new(
    access_token:  payload["access_token"],
    refresh_token: payload["refresh_token"],
    expires_at:    expires_in ? requested_at + expires_in.to_i : nil,
    token_type:    payload["token_type"] || "Bearer",
    id_token:      payload["id_token"],
    scope:         payload["scope"]
  )
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



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

def ==(other)
  other.is_a?(Token) && other.to_h == to_h
end

#expired?(now: Time.now) ⇒ Boolean

True if expires_at is in the past. Returns false when expires_at is unknown — without an expiry we have no signal to act on, so callers should fall through to reactive (on-401) handling.

Returns:

  • (Boolean)


52
53
54
55
56
# File 'lib/xero_kiwi/token.rb', line 52

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

  now >= expires_at
end

#expiring_soon?(within: DEFAULT_EXPIRY_WINDOW, now: Time.now) ⇒ Boolean

True if the token expires within ‘within` seconds from `now`. Used by the client to decide whether to refresh proactively before a request. The default 60s window is wide enough to absorb network round-trip races without being so wide that we burn refresh tokens unnecessarily.

Returns:

  • (Boolean)


62
63
64
65
66
# File 'lib/xero_kiwi/token.rb', line 62

def expiring_soon?(within: DEFAULT_EXPIRY_WINDOW, now: Time.now)
  return false if expires_at.nil?

  now + within >= expires_at
end

#hashObject



92
# File 'lib/xero_kiwi/token.rb', line 92

def hash = to_h.hash

#inspectObject



94
95
96
97
# File 'lib/xero_kiwi/token.rb', line 94

def inspect
  "#<#{self.class} access_token=#{access_token && "[FILTERED]"} " \
    "refreshable=#{refreshable?} expires_at=#{expires_at.inspect}>"
end

#refreshable?Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/xero_kiwi/token.rb', line 72

def refreshable?
  !refresh_token.nil? && !refresh_token.empty?
end

#to_hObject



76
77
78
79
80
81
82
83
84
85
# File 'lib/xero_kiwi/token.rb', line 76

def to_h
  {
    access_token:  access_token,
    refresh_token: refresh_token,
    expires_at:    expires_at,
    token_type:    token_type,
    id_token:      id_token,
    scope:         scope
  }
end

#valid?(now: Time.now) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/xero_kiwi/token.rb', line 68

def valid?(now: Time.now)
  !access_token.nil? && !access_token.empty? && !expired?(now: now)
end