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
147 148 149 150 151 152 153 154 155 156 157 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 147 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 = access_token @refresh_token = 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
99 100 101 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 99 def access_token @access_token end |
#expires_at ⇒ Time? (readonly)
The time the access token expires, or nil when the refresh reported no lifetime
118 119 120 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 118 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.
111 112 113 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 111 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.
130 131 132 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 130 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.
56 57 58 59 60 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 56 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
178 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 178 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.
200 201 202 203 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 200 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.
260 261 262 263 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 260 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
187 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 187 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
273 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 273 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
220 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 220 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.
233 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 233 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
243 244 245 246 247 248 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 243 def marshal_load(state) format, tokens = state #: [Integer, {access_token: String, refresh_token: String?, expires_at: Time?, scopes: Array[String]?}] raise UnsupportedMarshalFormat, "#{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.
169 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 169 def to_h = {access_token:, refresh_token:, expires_at:, scopes:} |
#to_json(state = nil) ⇒ String
The tokens as JSON, which from_json reads back
212 |
# File 'x-core/lib/x/core/oauth2_tokens.rb', line 212 def to_json(state = nil) = as_json.to_json(state) |