Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .envrc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export K_SOUP_COV_DO=true # Means you want code coverage
# Available formats are html, xml, rcov, lcov, json, tty
export K_SOUP_COV_COMMAND_NAME="RSpec Coverage"
export K_SOUP_COV_FORMATTERS="html,tty"
export K_SOUP_COV_MIN_BRANCH=99 # Means you want to enforce X% branch coverage
export K_SOUP_COV_MIN_BRANCH=100 # Means you want to enforce X% branch coverage
export K_SOUP_COV_MIN_LINE=100 # Means you want to enforce X% line coverage
export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met
export K_SOUP_COV_MULTI_FORMATTERS=true
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Test Coverage

env:
K_SOUP_COV_MIN_BRANCH: 98
K_SOUP_COV_MIN_LINE: 98
K_SOUP_COV_MIN_BRANCH: 100
K_SOUP_COV_MIN_LINE: 100
K_SOUP_COV_MIN_HARD: true
K_SOUP_COV_FORMATTERS: "html,rcov,lcov,json,tty"
K_SOUP_COV_DO: true
Expand Down
4 changes: 2 additions & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ variables:
K_SOUP_COV_DEBUG: true
K_SOUP_COV_DO: true
K_SOUP_COV_HARD: true
K_SOUP_COV_MIN_BRANCH: 98
K_SOUP_COV_MIN_LINE: 98
K_SOUP_COV_MIN_BRANCH: 100
K_SOUP_COV_MIN_LINE: 100
K_SOUP_COV_VERBOSE: true
K_SOUP_COV_FORMATTERS: "html,xml,rcov,lcov,json,tty"
K_SOUP_COV_MULTI_FORMATTERS: true
Expand Down
36 changes: 18 additions & 18 deletions .rubocop_gradual.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
[9, 9, 25, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2012823020],
[13, 9, 25, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2012823020]
],
"lib/oauth2/response.rb:877496664": [
"lib/oauth2/response.rb:355921218": [
[35, 5, 204, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 996912427]
],
"oauth2.gemspec:290828046": [
Expand All @@ -31,11 +31,11 @@
[130, 3, 52, "Gemspec/DependencyVersion: Dependency version specification is required.", 3163430777],
[131, 3, 48, "Gemspec/DependencyVersion: Dependency version specification is required.", 425065368]
],
"spec/oauth2/access_token_spec.rb:1576666213": [
"spec/oauth2/access_token_spec.rb:388877639": [
[3, 1, 34, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/access_token*_spec.rb`.", 1972107547],
[590, 13, 25, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 770233088],
[660, 9, 101, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3022740639],
[664, 9, 79, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2507338967]
[594, 13, 25, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 770233088],
[664, 9, 101, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3022740639],
[668, 9, 79, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2507338967]
],
"spec/oauth2/authenticator_spec.rb:853320290": [
[3, 1, 36, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/authenticator*_spec.rb`.", 819808017],
Expand All @@ -44,26 +44,26 @@
[69, 15, 38, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1480816240],
[79, 13, 23, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2314399065]
],
"spec/oauth2/client_spec.rb:3773709445": [
"spec/oauth2/client_spec.rb:824695973": [
[6, 1, 29, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/client*_spec.rb`.", 439549885],
[174, 7, 492, "RSpec/NoExpectationExample: No expectation found in this example.", 1272021224],
[193, 7, 592, "RSpec/NoExpectationExample: No expectation found in this example.", 3428877205],
[206, 15, 20, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2320605227],
[221, 15, 20, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1276531672],
[236, 15, 43, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1383956904],
[251, 15, 43, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 3376202107],
[590, 5, 360, "RSpec/NoExpectationExample: No expectation found in this example.", 536201463],
[599, 5, 461, "RSpec/NoExpectationExample: No expectation found in this example.", 3392600621],
[610, 5, 340, "RSpec/NoExpectationExample: No expectation found in this example.", 244592251],
[655, 63, 2, "RSpec/BeEq: Prefer `be` over `eq`.", 5860785],
[700, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
[704, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
[712, 7, 89, "RSpec/NoExpectationExample: No expectation found in this example.", 4609419],
[800, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
[804, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
[884, 17, 12, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 664794325],
[909, 5, 459, "RSpec/NoExpectationExample: No expectation found in this example.", 2216851076],
[919, 7, 450, "RSpec/NoExpectationExample: No expectation found in this example.", 2619808549]
[869, 5, 360, "RSpec/NoExpectationExample: No expectation found in this example.", 536201463],
[878, 5, 461, "RSpec/NoExpectationExample: No expectation found in this example.", 3392600621],
[889, 5, 340, "RSpec/NoExpectationExample: No expectation found in this example.", 244592251],
[934, 63, 2, "RSpec/BeEq: Prefer `be` over `eq`.", 5860785],
[979, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
[983, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
[991, 7, 89, "RSpec/NoExpectationExample: No expectation found in this example.", 4609419],
[1079, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
[1083, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
[1163, 17, 12, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 664794325],
[1188, 5, 459, "RSpec/NoExpectationExample: No expectation found in this example.", 2216851076],
[1198, 7, 450, "RSpec/NoExpectationExample: No expectation found in this example.", 2619808549]
],
"spec/oauth2/error_spec.rb:1209122273": [
[23, 1, 28, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/error*_spec.rb`.", 3385870076],
Expand Down
3 changes: 2 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ begin
require "rubocop/lts"

Rubocop::Lts.install_tasks
defaults << "rubocop_gradual"
# Make autocorrect the default rubocop task
defaults << "rubocop_gradual:autocorrect"
rescue LoadError
desc("(stub) rubocop_gradual is unavailable")
task(:rubocop_gradual) do
Expand Down
81 changes: 71 additions & 10 deletions lib/oauth2/access_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,47 @@ class AccessToken # rubocop:disable Metrics/ClassLength
class << self
# Initializes an AccessToken from a Hash
#
# @param [Client] client the OAuth2::Client instance
# @param [Hash] hash a hash of AccessToken property values
# @option hash [String, Symbol] 'access_token', 'id_token', 'token', :access_token, :id_token, or :token the access token
# @return [AccessToken] the initialized AccessToken
# @param [OAuth2::Client] client the OAuth2::Client instance
# @param [Hash] hash a hash containing the token and other properties
# @option hash [String] 'access_token' the access token value
# @option hash [String] 'id_token' alternative key for the access token value
# @option hash [String] 'token' alternative key for the access token value
# @option hash [String] 'refresh_token' (optional) the refresh token value
# @option hash [Integer, String] 'expires_in' (optional) number of seconds until token expires
# @option hash [Integer, String] 'expires_at' (optional) epoch time in seconds when token expires
# @option hash [Integer, String] 'expires_latency' (optional) seconds to reduce token validity by
#
# @return [OAuth2::AccessToken] the initialized 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 For "soon-to-expire"/"clock-skew" functionality see the `:expires_latency` option.
# @mote If snaky key conversion is being used, token_name needs to match the converted key.
#
# @example
# hash = { 'access_token' => 'token_value', 'refresh_token' => 'refresh_value' }
# access_token = OAuth2::AccessToken.from_hash(client, hash)
def from_hash(client, hash)
fresh = hash.dup
supported_keys = TOKEN_KEY_LOOKUP & fresh.keys
key = supported_keys[0]
extra_tokens_warning(supported_keys, key)
token = fresh.delete(key)
# If token_name is present, then use that key name
if fresh.key?(:token_name)
key = fresh[:token_name]
if key.nil? || !fresh.key?(key)
warn(%[
OAuth2::AccessToken#from_hash key mismatch.
Custom token_name (#{key}) does match any keys (#{fresh.keys})
You may need to set `snaky: false`. See inline documentation for more info.
])
end
else
# Otherwise, if one of the supported default keys is present, use whichever has precedence
supported_keys = TOKEN_KEY_LOOKUP & fresh.keys
key = supported_keys[0]
extra_tokens_warning(supported_keys, key)
end
token = fresh.delete(key) || ""
new(client, token, fresh)
end

Expand All @@ -50,6 +81,16 @@ def extra_tokens_warning(supported_keys, key)

# Initialize an 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`.
#
# @param [Client] client the OAuth2::Client instance
# @param [String] token the Access Token value (optional, may not be used in refresh flows)
# @param [Hash] opts the options to create the Access Token with
Expand All @@ -62,10 +103,11 @@ def extra_tokens_warning(supported_keys, key)
# @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
# @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the
# Access Token value in :body or :query transmission mode
# @option opts [String] :token_name (nil) the name of the response parameter that identifies the access token
# When nil one of TOKEN_KEY_LOOKUP will be used
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))
Expand All @@ -91,6 +133,8 @@ def initialize(client, token, opts = {})
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

Expand Down Expand Up @@ -139,9 +183,26 @@ def refresh(params = {}, access_token_opts = {})

# Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
#
# @note Don't return expires_latency because it has already been deducted from expires_at
#
# @return [Hash] a hash of AccessToken property values
def to_hash
params.merge(access_token: token, refresh_token: refresh_token, expires_at: expires_at)
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

# Make a request with the Access Token
Expand Down
Loading
Loading