Skip to content

Commit 83aa50c

Browse files
committed
Merge branch 'feat/token_name' into 'main'
✨ token_name See merge request oauth-xx/oauth2!643
2 parents e220fdb + cfcc630 commit 83aa50c

File tree

10 files changed

+551
-87
lines changed

10 files changed

+551
-87
lines changed

.envrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export K_SOUP_COV_DO=true # Means you want code coverage
1919
# Available formats are html, xml, rcov, lcov, json, tty
2020
export K_SOUP_COV_COMMAND_NAME="RSpec Coverage"
2121
export K_SOUP_COV_FORMATTERS="html,tty"
22-
export K_SOUP_COV_MIN_BRANCH=99 # Means you want to enforce X% branch coverage
22+
export K_SOUP_COV_MIN_BRANCH=100 # Means you want to enforce X% branch coverage
2323
export K_SOUP_COV_MIN_LINE=100 # Means you want to enforce X% line coverage
2424
export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met
2525
export K_SOUP_COV_MULTI_FORMATTERS=true

.github/workflows/coverage.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
name: Test Coverage
22

33
env:
4-
K_SOUP_COV_MIN_BRANCH: 98
5-
K_SOUP_COV_MIN_LINE: 98
4+
K_SOUP_COV_MIN_BRANCH: 100
5+
K_SOUP_COV_MIN_LINE: 100
66
K_SOUP_COV_MIN_HARD: true
77
K_SOUP_COV_FORMATTERS: "html,rcov,lcov,json,tty"
88
K_SOUP_COV_DO: true

.gitlab-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ variables:
77
K_SOUP_COV_DEBUG: true
88
K_SOUP_COV_DO: true
99
K_SOUP_COV_HARD: true
10-
K_SOUP_COV_MIN_BRANCH: 98
11-
K_SOUP_COV_MIN_LINE: 98
10+
K_SOUP_COV_MIN_BRANCH: 100
11+
K_SOUP_COV_MIN_LINE: 100
1212
K_SOUP_COV_VERBOSE: true
1313
K_SOUP_COV_FORMATTERS: "html,xml,rcov,lcov,json,tty"
1414
K_SOUP_COV_MULTI_FORMATTERS: true

.rubocop_gradual.lock

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
[9, 9, 25, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2012823020],
1919
[13, 9, 25, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2012823020]
2020
],
21-
"lib/oauth2/response.rb:877496664": [
21+
"lib/oauth2/response.rb:355921218": [
2222
[35, 5, 204, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 996912427]
2323
],
2424
"oauth2.gemspec:290828046": [
@@ -31,11 +31,11 @@
3131
[130, 3, 52, "Gemspec/DependencyVersion: Dependency version specification is required.", 3163430777],
3232
[131, 3, 48, "Gemspec/DependencyVersion: Dependency version specification is required.", 425065368]
3333
],
34-
"spec/oauth2/access_token_spec.rb:1576666213": [
34+
"spec/oauth2/access_token_spec.rb:388877639": [
3535
[3, 1, 34, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/access_token*_spec.rb`.", 1972107547],
36-
[590, 13, 25, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 770233088],
37-
[660, 9, 101, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3022740639],
38-
[664, 9, 79, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2507338967]
36+
[594, 13, 25, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 770233088],
37+
[664, 9, 101, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3022740639],
38+
[668, 9, 79, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2507338967]
3939
],
4040
"spec/oauth2/authenticator_spec.rb:853320290": [
4141
[3, 1, 36, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/authenticator*_spec.rb`.", 819808017],
@@ -44,26 +44,26 @@
4444
[69, 15, 38, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1480816240],
4545
[79, 13, 23, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2314399065]
4646
],
47-
"spec/oauth2/client_spec.rb:3773709445": [
47+
"spec/oauth2/client_spec.rb:824695973": [
4848
[6, 1, 29, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/client*_spec.rb`.", 439549885],
4949
[174, 7, 492, "RSpec/NoExpectationExample: No expectation found in this example.", 1272021224],
5050
[193, 7, 592, "RSpec/NoExpectationExample: No expectation found in this example.", 3428877205],
5151
[206, 15, 20, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2320605227],
5252
[221, 15, 20, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1276531672],
5353
[236, 15, 43, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1383956904],
5454
[251, 15, 43, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 3376202107],
55-
[590, 5, 360, "RSpec/NoExpectationExample: No expectation found in this example.", 536201463],
56-
[599, 5, 461, "RSpec/NoExpectationExample: No expectation found in this example.", 3392600621],
57-
[610, 5, 340, "RSpec/NoExpectationExample: No expectation found in this example.", 244592251],
58-
[655, 63, 2, "RSpec/BeEq: Prefer `be` over `eq`.", 5860785],
59-
[700, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
60-
[704, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
61-
[712, 7, 89, "RSpec/NoExpectationExample: No expectation found in this example.", 4609419],
62-
[800, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
63-
[804, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
64-
[884, 17, 12, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 664794325],
65-
[909, 5, 459, "RSpec/NoExpectationExample: No expectation found in this example.", 2216851076],
66-
[919, 7, 450, "RSpec/NoExpectationExample: No expectation found in this example.", 2619808549]
55+
[869, 5, 360, "RSpec/NoExpectationExample: No expectation found in this example.", 536201463],
56+
[878, 5, 461, "RSpec/NoExpectationExample: No expectation found in this example.", 3392600621],
57+
[889, 5, 340, "RSpec/NoExpectationExample: No expectation found in this example.", 244592251],
58+
[934, 63, 2, "RSpec/BeEq: Prefer `be` over `eq`.", 5860785],
59+
[979, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
60+
[983, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
61+
[991, 7, 89, "RSpec/NoExpectationExample: No expectation found in this example.", 4609419],
62+
[1079, 11, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 3084776886],
63+
[1083, 11, 82, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1524553529],
64+
[1163, 17, 12, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 664794325],
65+
[1188, 5, 459, "RSpec/NoExpectationExample: No expectation found in this example.", 2216851076],
66+
[1198, 7, 450, "RSpec/NoExpectationExample: No expectation found in this example.", 2619808549]
6767
],
6868
"spec/oauth2/error_spec.rb:1209122273": [
6969
[23, 1, 28, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth2/error*_spec.rb`.", 3385870076],

Rakefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ begin
4242
require "rubocop/lts"
4343

4444
Rubocop::Lts.install_tasks
45-
defaults << "rubocop_gradual"
45+
# Make autocorrect the default rubocop task
46+
defaults << "rubocop_gradual:autocorrect"
4647
rescue LoadError
4748
desc("(stub) rubocop_gradual is unavailable")
4849
task(:rubocop_gradual) do

lib/oauth2/access_token.rb

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,47 @@ class AccessToken # rubocop:disable Metrics/ClassLength
1515
class << self
1616
# Initializes an AccessToken from a Hash
1717
#
18-
# @param [Client] client the OAuth2::Client instance
19-
# @param [Hash] hash a hash of AccessToken property values
20-
# @option hash [String, Symbol] 'access_token', 'id_token', 'token', :access_token, :id_token, or :token the access token
21-
# @return [AccessToken] the initialized AccessToken
18+
# @param [OAuth2::Client] client the OAuth2::Client instance
19+
# @param [Hash] hash a hash containing the token and other properties
20+
# @option hash [String] 'access_token' the access token value
21+
# @option hash [String] 'id_token' alternative key for the access token value
22+
# @option hash [String] 'token' alternative key for the access token value
23+
# @option hash [String] 'refresh_token' (optional) the refresh token value
24+
# @option hash [Integer, String] 'expires_in' (optional) number of seconds until token expires
25+
# @option hash [Integer, String] 'expires_at' (optional) epoch time in seconds when token expires
26+
# @option hash [Integer, String] 'expires_latency' (optional) seconds to reduce token validity by
27+
#
28+
# @return [OAuth2::AccessToken] the initialized AccessToken
29+
#
30+
# @note The method will use the first found token key in the following order:
31+
# 'access_token', 'id_token', 'token' (or their symbolic versions)
32+
# @note If multiple token keys are present, a warning will be issued unless
33+
# OAuth2.config.silence_extra_tokens_warning is true
34+
# @note For "soon-to-expire"/"clock-skew" functionality see the `:expires_latency` option.
35+
# @mote If snaky key conversion is being used, token_name needs to match the converted key.
36+
#
37+
# @example
38+
# hash = { 'access_token' => 'token_value', 'refresh_token' => 'refresh_value' }
39+
# access_token = OAuth2::AccessToken.from_hash(client, hash)
2240
def from_hash(client, hash)
2341
fresh = hash.dup
24-
supported_keys = TOKEN_KEY_LOOKUP & fresh.keys
25-
key = supported_keys[0]
26-
extra_tokens_warning(supported_keys, key)
27-
token = fresh.delete(key)
42+
# If token_name is present, then use that key name
43+
if fresh.key?(:token_name)
44+
key = fresh[:token_name]
45+
if key.nil? || !fresh.key?(key)
46+
warn(%[
47+
OAuth2::AccessToken#from_hash key mismatch.
48+
Custom token_name (#{key}) does match any keys (#{fresh.keys})
49+
You may need to set `snaky: false`. See inline documentation for more info.
50+
])
51+
end
52+
else
53+
# Otherwise, if one of the supported default keys is present, use whichever has precedence
54+
supported_keys = TOKEN_KEY_LOOKUP & fresh.keys
55+
key = supported_keys[0]
56+
extra_tokens_warning(supported_keys, key)
57+
end
58+
token = fresh.delete(key) || ""
2859
new(client, token, fresh)
2960
end
3061

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

5182
# Initialize an AccessToken
5283
#
84+
# @note For "soon-to-expire"/"clock-skew" functionality see the `:expires_latency` option.
85+
# @note If no token is provided, the AccessToken will be considered invalid.
86+
# This is to prevent the possibility of a token being accidentally
87+
# created with no token value.
88+
# If you want to create an AccessToken with no token value,
89+
# you can pass in an empty string or nil for the token value.
90+
# If you want to create an AccessToken with no token value and
91+
# no refresh token, you can pass in an empty string or nil for the
92+
# token value and nil for the refresh token, and `raise_errors: false`.
93+
#
5394
# @param [Client] client the OAuth2::Client instance
5495
# @param [String] token the Access Token value (optional, may not be used in refresh flows)
5596
# @param [Hash] opts the options to create the Access Token with
@@ -62,10 +103,11 @@ def extra_tokens_warning(supported_keys, key)
62103
# @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
63104
# @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the
64105
# Access Token value in :body or :query transmission mode
106+
# @option opts [String] :token_name (nil) the name of the response parameter that identifies the access token
107+
# When nil one of TOKEN_KEY_LOOKUP will be used
65108
def initialize(client, token, opts = {})
66109
@client = client
67110
@token = token.to_s
68-
69111
opts = opts.dup
70112
%i[refresh_token expires_in expires_at expires_latency].each do |arg|
71113
instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
@@ -91,6 +133,8 @@ def initialize(client, token, opts = {})
91133
header_format: opts.delete(:header_format) || "Bearer %s",
92134
param_name: opts.delete(:param_name) || "access_token",
93135
}
136+
@options[:token_name] = opts.delete(:token_name) if opts.key?(:token_name)
137+
94138
@params = opts
95139
end
96140

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

140184
# Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
141185
#
186+
# @note Don't return expires_latency because it has already been deducted from expires_at
187+
#
142188
# @return [Hash] a hash of AccessToken property values
143189
def to_hash
144-
params.merge(access_token: token, refresh_token: refresh_token, expires_at: expires_at)
190+
hsh = {
191+
access_token: token,
192+
refresh_token: refresh_token,
193+
expires_at: expires_at,
194+
mode: options[:mode],
195+
header_format: options[:header_format],
196+
param_name: options[:param_name],
197+
}
198+
hsh[:token_name] = options[:token_name] if options.key?(:token_name)
199+
# TODO: Switch when dropping Ruby < 2.5 support
200+
# params.transform_keys(&:to_sym) # Ruby 2.5 only
201+
# Old Ruby transform_keys alternative:
202+
sheesh = @params.each_with_object({}) { |(k, v), memo|
203+
memo[k.to_sym] = v
204+
}
205+
sheesh.merge(hsh)
145206
end
146207

147208
# Make a request with the Access Token

0 commit comments

Comments
 (0)