Class: X::OAuth2Tokens
- Inherits:
-
Object
- Object
- X::OAuth2Tokens
- Defined in:
- x-core/lib/x/core/oauth2_tokens.rb
Overview
The OAuth 2.0 tokens one refresh issued, or the exchange of an authorization code, which save_tokens is passed to store
It is frozen, and taken while the refresh holds its lock, so it holds the tokens of the refresh it reports, whatever refreshes follow on other threads, where the authenticator holds the tokens of the latest one.
Instance Attribute Summary collapse
-
#access_token ⇒ String
readonly
The OAuth 2.0 access token the refresh issued.
-
#expires_at ⇒ Time?
readonly
The time the access token expires, or nil when the refresh reported no lifetime.
-
#refresh_token ⇒ String?
readonly
The OAuth 2.0 refresh token the refresh issued.
-
#scopes ⇒ Array<String>?
readonly
The scopes X granted the access token.
Class Method Summary collapse
-
.from_json(json) ⇒ OAuth2Tokens
Read tokens back from the JSON that to_json wrote, or the Hash that as_json gave.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Check whether other tokens are the same tokens.
-
#as_json ⇒ Hash{String => Integer, String, Array<String>, nil}
The tokens as JSON writes them, led by the number of their format.
-
#encode_with(coder) ⇒ void
Write the state Marshal writes as YAML.
-
#hash ⇒ Integer
The hash of the tokens, for use as a Hash key.
-
#init_with(coder) ⇒ void
Restore tokens YAML read, frozen, as Marshal restores them.
-
#initialize(access_token:, refresh_token: nil, expires_at: nil, scopes: nil) ⇒ OAuth2Tokens
constructor
Initialize the tokens of a refresh.
-
#inspect ⇒ String
Summarize the tokens for the console without revealing them.
-
#marshal_dump ⇒ Array(Integer, Hash{Symbol => String, Time, Array<String>, nil})
The state Marshal writes.
-
#marshal_load(state) ⇒ void
Restore tokens Marshal read, built as the constructor builds them, frozen.
-
#to_h ⇒ Hash{Symbol => String, Time, Array<String>, nil}
The tokens as a Hash, to store, and to build a client of.
-
#to_json(state = nil) ⇒ String
The tokens as JSON, which from_json reads back.
Constructor Details
#initialize(access_token:, refresh_token: nil, expires_at: nil, scopes: nil) ⇒ OAuth2Tokens
Initialize the tokens of a refresh
148 149 150 151 152 153 154 155 156 157 158 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 148 def initialize(access_token:, refresh_token: nil, expires_at: nil, scopes: nil) raise ArgumentError, format(NOT_A_STRING, :access_token, access_token.class) if access_token.nil? raise ArgumentError, format(NOT_A_STRING_OR_NIL, :refresh_token, refresh_token.class) unless refresh_token.nil? || refresh_token.is_a?(String) CredentialValidator.validate_required!({access_token:}, {refresh_token:, expires_at:, scopes:}) @access_token = SettingValidator.frozen(access_token) @refresh_token = SettingValidator.frozen(refresh_token) @expires_at = expires_at @scopes = CredentialValidator.frozen_scopes(scopes) freeze end |
Instance Attribute Details
#access_token ⇒ String (readonly)
The OAuth 2.0 access token the refresh issued
100 101 102 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 100 def access_token @access_token end |
#expires_at ⇒ Time? (readonly)
The time the access token expires, or nil when the refresh reported no lifetime
119 120 121 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 119 def expires_at @expires_at end |
#refresh_token ⇒ String? (readonly)
The OAuth 2.0 refresh token the refresh issued
It is the refresh token the refresh was sent with when X issued none. An authorization without the offline.access scope issues none, so the tokens of its exchange hold nil, and the access token acts for the user until it expires, with nothing to refresh it.
112 113 114 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 112 def refresh_token @refresh_token end |
#scopes ⇒ Array<String>? (readonly)
The scopes X granted the access token
They are the scopes the user authorized, which may be fewer than the app asked for, since a user can leave some out on the consent screen, so an app that needs a scope checks for it here rather than learn of it from the 403 Forbidden of a request. A refresh that names no scopes keeps those it refreshed, as OAuth 2.0 has it.
131 132 133 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 131 def scopes @scopes end |
Class Method Details
.from_json(json) ⇒ OAuth2Tokens
Read tokens back from the JSON that to_json wrote, or the Hash that as_json gave
A store that holds JSON, such as Redis or a JSON column, holds the tokens as as_json gives them: the number of their format, each token, and the expiration time as an ISO 8601 String, which this reads back as a Time. The Hash may be keyed by Symbol, as JSON.parse(json, symbolize_names: true) and many caches give it back.
57 58 59 60 61 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 57 def self.from_json(json) state = json_state(json) new(access_token: state["access_token"], refresh_token: state["refresh_token"], expires_at: json_time(state["expires_at"]), scopes: state["scopes"]) end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Check whether other tokens are the same tokens
179 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 179 def ==(other) = other.instance_of?(self.class) && to_h.eql?(other.to_h) |
#as_json ⇒ Hash{String => Integer, String, Array<String>, nil}
The tokens as JSON writes them, led by the number of their format
JSON holds no Time, so the expiration time is written as an ISO 8601 String in UTC, to the nanosecond, which from_json reads back as a Time equal to it. It holds the tokens themselves, since tokens are written as JSON to be stored, so what JSON wrote is kept as secret as the tokens are.
201 202 203 204 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 201 def as_json(*) {"format" => MARSHAL_FORMAT, "access_token" => access_token, "refresh_token" => refresh_token, "expires_at" => expires_at&.getutc&.iso8601(FRACTION_DIGITS), "scopes" => scopes} end |
#encode_with(coder) ⇒ void
This method returns an undefined value.
Write the state Marshal writes as YAML
YAML would write the instance variables of the tokens, and read them back into tokens that are not frozen, so they say how they are written: the number of their format, then each of them, under the name to_h gives it.
261 262 263 264 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 261 def encode_with(coder) coder["format"] = MARSHAL_FORMAT to_h.each { |key, value| coder[key.to_s] = value } end |
#hash ⇒ Integer
The hash of the tokens, for use as a Hash key
188 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 188 def hash = [self.class, to_h].hash |
#init_with(coder) ⇒ void
This method returns an undefined value.
Restore tokens YAML read, frozen, as Marshal restores them
274 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 274 def init_with(coder) = marshal_load([coder["format"], coder.map.transform_keys(&:to_sym)]) |
#inspect ⇒ String
Summarize the tokens for the console without revealing them
221 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 221 def inspect = "#<#{self.class} expires_at=#{expires_at.inspect}>" |
#marshal_dump ⇒ Array(Integer, Hash{Symbol => String, Time, Array<String>, nil})
The state Marshal writes
What is written is plain data, led by the number of its format, so that tokens written by one release of 1.x are read by a later one: the tokens, their expiration time, and their scopes, as to_h gives them. It holds the tokens themselves, since tokens are marshalled to be stored, so what Marshal wrote is kept as secret as the tokens are.
234 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 234 def marshal_dump = [MARSHAL_FORMAT, to_h] |
#marshal_load(state) ⇒ void
This method returns an undefined value.
Restore tokens Marshal read, built as the constructor builds them, frozen
244 245 246 247 248 249 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 244 def marshal_load(state) format, tokens = state #: [Integer, {access_token: String, refresh_token: String?, expires_at: Time?, scopes: Array[String]?}] raise UnsupportedFormat, "#{self.class} reads format #{MARSHAL_FORMAT} of Marshal, not #{format.inspect}" unless MARSHAL_FORMAT.eql?(format) initialize(**tokens.slice(:access_token, :refresh_token, :expires_at, :scopes)) # steep:ignore InsufficientKeywordArguments end |
#to_h ⇒ Hash{Symbol => String, Time, Array<String>, nil}
The tokens as a Hash, to store, and to build a client of
Its keys are keywords X::Client.new and X::OAuth2Authenticator.new take, so that the tokens build a client, as in X::Client.new(client_id:, **tokens.to_h), and a later release of 1.x adds a key to it only as both take it.
170 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 170 def to_h = {access_token:, refresh_token:, expires_at:, scopes:} |
#to_json(state = nil) ⇒ String
The tokens as JSON, which from_json reads back
213 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 213 def to_json(state = nil) = as_json.to_json(state) |