oauth2 2.0.1 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/oauth2/client.rb CHANGED
@@ -5,9 +5,11 @@ require 'logger'
5
5
 
6
6
  module OAuth2
7
7
  ConnectionError = Class.new(Faraday::ConnectionFailed)
8
+ TimeoutError = Class.new(Faraday::TimeoutError)
9
+
8
10
  # The OAuth2::Client class
9
11
  class Client # rubocop:disable Metrics/ClassLength
10
- RESERVED_PARAM_KEYS = %w[headers parse].freeze
12
+ RESERVED_PARAM_KEYS = %w[body headers params parse snaky].freeze
11
13
 
12
14
  attr_reader :id, :secret, :site
13
15
  attr_accessor :options
@@ -31,6 +33,7 @@ module OAuth2
31
33
  # @option options [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error on responses with 400+ status codes
32
34
  # @option options [Logger] :logger (::Logger.new($stdout)) which logger to use when OAUTH_DEBUG is enabled
33
35
  # @option options [Proc] :extract_access_token proc that takes the client and the response Hash and extracts the access token from the response (DEPRECATED)
36
+ # @option options [Class] :access_token_class [Class] class of access token for easier subclassing OAuth2::AccessToken, @version 2.0+
34
37
  # @yield [builder] The Faraday connection builder
35
38
  def initialize(client_id, client_secret, options = {}, &block)
36
39
  opts = options.dup
@@ -38,7 +41,7 @@ module OAuth2
38
41
  @secret = client_secret
39
42
  @site = opts.delete(:site)
40
43
  ssl = opts.delete(:ssl)
41
-
44
+ warn('OAuth2::Client#initialize argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class`.') if opts[:extract_access_token]
42
45
  @options = {
43
46
  authorize_url: 'oauth/authorize',
44
47
  token_url: 'oauth/token',
@@ -49,6 +52,7 @@ module OAuth2
49
52
  max_redirects: 5,
50
53
  raise_errors: true,
51
54
  logger: ::Logger.new($stdout),
55
+ access_token_class: AccessToken,
52
56
  }.merge(opts)
53
57
  @options[:connection_opts][:ssl] = ssl if ssl
54
58
  end
@@ -104,20 +108,10 @@ module OAuth2
104
108
  # @option opts [Boolean] :raise_errors whether or not to raise an OAuth2::Error on 400+ status
105
109
  # code response for this request. Will default to client option
106
110
  # @option opts [Symbol] :parse @see Response::initialize
107
- # @yield [req] The Faraday request
108
- def request(verb, url, opts = {})
109
- url = connection.build_url(url).to_s
110
-
111
- begin
112
- response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
113
- req.params.update(opts[:params]) if opts[:params]
114
- yield(req) if block_given?
115
- end
116
- rescue Faraday::ConnectionFailed => e
117
- raise ConnectionError, e
118
- end
119
-
120
- response = Response.new(response, parse: opts[:parse])
111
+ # @option opts [true, false] :snaky (true) @see Response::initialize
112
+ # @yield [req] @see Faraday::Connection#run_request
113
+ def request(verb, url, opts = {}, &block)
114
+ response = execute_request(verb, url, opts, &block)
121
115
 
122
116
  case response.status
123
117
  when 301, 302, 303, 307
@@ -153,45 +147,60 @@ module OAuth2
153
147
 
154
148
  # Initializes an AccessToken by making a request to the token endpoint
155
149
  #
156
- # @param params [Hash] a Hash of params for the token endpoint
150
+ # @param params [Hash] a Hash of params for the token endpoint, except:
151
+ # @option params [Symbol] :parse @see Response#initialize
152
+ # @option params [true, false] :snaky (true) @see Response#initialize
157
153
  # @param access_token_opts [Hash] access token options, to pass to the AccessToken object
158
154
  # @param extract_access_token [Proc] proc that extracts the access token from the response (DEPRECATED)
159
- # @param access_token_class [Class] class of access token for easier subclassing OAuth2::AccessToken, @version 2.0+
155
+ # @yield [req] @see Faraday::Connection#run_request
160
156
  # @return [AccessToken] the initialized AccessToken
161
- def get_token(params, access_token_opts = {}, extract_access_token = options[:extract_access_token], access_token_class: AccessToken)
162
- params = params.map do |key, value|
163
- if RESERVED_PARAM_KEYS.include?(key)
164
- [key.to_sym, value]
165
- else
166
- [key, value]
167
- end
168
- end.to_h
169
-
170
- params = authenticator.apply(params)
171
- opts = {raise_errors: options[:raise_errors], parse: params.delete(:parse)}
172
- headers = params.delete(:headers) || {}
157
+ def get_token(params, access_token_opts = {}, extract_access_token = nil, &block)
158
+ warn('OAuth2::Client#get_token argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class` on #initialize.') if extract_access_token
159
+ extract_access_token ||= options[:extract_access_token]
160
+ parse, snaky, params, headers = parse_snaky_params_headers(params)
161
+
162
+ request_opts = {
163
+ raise_errors: options[:raise_errors],
164
+ parse: parse,
165
+ snaky: snaky,
166
+ }
173
167
  if options[:token_method] == :post
174
- opts[:body] = params
175
- opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
168
+
169
+ # NOTE: If proliferation of request types continues we should implement a parser solution for Request,
170
+ # just like we have with Response.
171
+ request_opts[:body] = if headers['Content-Type'] == 'application/json'
172
+ params.to_json
173
+ else
174
+ params
175
+ end
176
+
177
+ request_opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
176
178
  else
177
- opts[:params] = params
178
- opts[:headers] = {}
179
+ request_opts[:params] = params
180
+ request_opts[:headers] = {}
179
181
  end
180
- opts[:headers].merge!(headers)
181
- http_method = options[:token_method]
182
- http_method = :post if http_method == :post_with_query_string
183
- response = request(http_method, token_url, opts)
182
+ request_opts[:headers].merge!(headers)
183
+ response = request(http_method, token_url, request_opts, &block)
184
184
 
185
185
  # In v1.4.x, the deprecated extract_access_token option retrieves the token from the response.
186
186
  # We preserve this behavior here, but a custom access_token_class that implements #from_hash
187
187
  # should be used instead.
188
188
  if extract_access_token
189
- parse_response_with_legacy_extract(response, access_token_opts, extract_access_token)
189
+ parse_response_legacy(response, access_token_opts, extract_access_token)
190
190
  else
191
- parse_response(response, access_token_opts, access_token_class)
191
+ parse_response(response, access_token_opts)
192
192
  end
193
193
  end
194
194
 
195
+ # The HTTP Method of the request
196
+ # @return [Symbol] HTTP verb, one of :get, :post, :put, :delete
197
+ def http_method
198
+ http_meth = options[:token_method].to_sym
199
+ return :post if http_meth == :post_with_query_string
200
+
201
+ http_meth
202
+ end
203
+
195
204
  # The Authorization Code strategy
196
205
  #
197
206
  # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1
@@ -250,6 +259,42 @@ module OAuth2
250
259
 
251
260
  private
252
261
 
262
+ def parse_snaky_params_headers(params)
263
+ params = params.map do |key, value|
264
+ if RESERVED_PARAM_KEYS.include?(key)
265
+ [key.to_sym, value]
266
+ else
267
+ [key, value]
268
+ end
269
+ end.to_h
270
+ parse = params.key?(:parse) ? params.delete(:parse) : Response::DEFAULT_OPTIONS[:parse]
271
+ snaky = params.key?(:snaky) ? params.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky]
272
+ params = authenticator.apply(params)
273
+ # authenticator may add :headers, and we remove them here
274
+ headers = params.delete(:headers) || {}
275
+ [parse, snaky, params, headers]
276
+ end
277
+
278
+ def execute_request(verb, url, opts = {})
279
+ url = connection.build_url(url).to_s
280
+
281
+ begin
282
+ response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
283
+ req.params.update(opts[:params]) if opts[:params]
284
+ yield(req) if block_given?
285
+ end
286
+ rescue Faraday::ConnectionFailed => e
287
+ raise ConnectionError, e
288
+ rescue Faraday::TimeoutError => e
289
+ raise TimeoutError, e
290
+ end
291
+
292
+ parse = opts.key?(:parse) ? opts.delete(:parse) : Response::DEFAULT_OPTIONS[:parse]
293
+ snaky = opts.key?(:snaky) ? opts.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky]
294
+
295
+ Response.new(response, parse: parse, snaky: snaky)
296
+ end
297
+
253
298
  # Returns the authenticator object
254
299
  #
255
300
  # @return [Authenticator] the initialized Authenticator
@@ -257,8 +302,8 @@ module OAuth2
257
302
  Authenticator.new(id, secret, options[:auth_scheme])
258
303
  end
259
304
 
260
- def parse_response_with_legacy_extract(response, access_token_opts, extract_access_token)
261
- access_token = build_access_token_legacy_extract(response, access_token_opts, extract_access_token)
305
+ def parse_response_legacy(response, access_token_opts, extract_access_token)
306
+ access_token = build_access_token_legacy(response, access_token_opts, extract_access_token)
262
307
 
263
308
  return access_token if access_token
264
309
 
@@ -270,10 +315,11 @@ module OAuth2
270
315
  nil
271
316
  end
272
317
 
273
- def parse_response(response, access_token_opts, access_token_class)
318
+ def parse_response(response, access_token_opts)
319
+ access_token_class = options[:access_token_class]
274
320
  data = response.parsed
275
321
 
276
- unless data.is_a?(Hash) && access_token_class.contains_token?(data)
322
+ unless data.is_a?(Hash) && !data.empty?
277
323
  return unless options[:raise_errors]
278
324
 
279
325
  error = Error.new(response)
@@ -295,7 +341,7 @@ module OAuth2
295
341
  # Builds the access token from the response of the HTTP call with legacy extract_access_token
296
342
  #
297
343
  # @return [AccessToken] the initialized AccessToken
298
- def build_access_token_legacy_extract(response, access_token_opts, extract_access_token)
344
+ def build_access_token_legacy(response, access_token_opts, extract_access_token)
299
345
  extract_access_token.call(self, response.parsed.merge(access_token_opts))
300
346
  rescue StandardError
301
347
  nil
data/lib/oauth2/error.rb CHANGED
@@ -2,21 +2,29 @@
2
2
 
3
3
  module OAuth2
4
4
  class Error < StandardError
5
- attr_reader :response, :code, :description
5
+ attr_reader :response, :body, :code, :description
6
6
 
7
7
  # standard error codes include:
8
8
  # 'invalid_request', 'invalid_client', 'invalid_token', 'invalid_grant', 'unsupported_grant_type', 'invalid_scope'
9
+ # response might be a Response object, or the response.parsed hash
9
10
  def initialize(response)
10
11
  @response = response
11
- message_opts = {}
12
-
13
- if response.parsed.is_a?(Hash)
14
- @code = response.parsed['error']
15
- @description = response.parsed['error_description']
16
- message_opts = parse_error_description(@code, @description)
12
+ if response.respond_to?(:parsed)
13
+ if response.parsed.is_a?(Hash)
14
+ @code = response.parsed['error']
15
+ @description = response.parsed['error_description']
16
+ end
17
+ elsif response.is_a?(Hash)
18
+ @code = response['error']
19
+ @description = response['error_description']
17
20
  end
18
-
19
- super(error_message(response.body, message_opts))
21
+ @body = if response.respond_to?(:body)
22
+ response.body
23
+ else
24
+ @response
25
+ end
26
+ message_opts = parse_error_description(@code, @description)
27
+ super(error_message(@body, message_opts))
20
28
  end
21
29
 
22
30
  private
@@ -7,6 +7,10 @@ require 'rack'
7
7
  module OAuth2
8
8
  # OAuth2::Response class
9
9
  class Response
10
+ DEFAULT_OPTIONS = {
11
+ parse: :automatic,
12
+ snaky: true,
13
+ }.freeze
10
14
  attr_reader :response
11
15
  attr_accessor :options
12
16
 
@@ -39,12 +43,17 @@ module OAuth2
39
43
  # Initializes a Response instance
40
44
  #
41
45
  # @param [Faraday::Response] response The Faraday response instance
42
- # @param [Hash] opts options in which to initialize the instance
43
- # @option opts [Symbol] :parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded),
46
+ # @param [Symbol] parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded),
44
47
  # :json, or :automatic (determined by Content-Type response header)
45
- def initialize(response, opts = {})
48
+ # @param [true, false] snaky (true) Convert @parsed to a snake-case,
49
+ # indifferent-access SnakyHash::StringKeyed, which is a subclass of Hashie::Mash (from hashie gem)?
50
+ # @param [Hash] options all other options for initializing the instance
51
+ def initialize(response, parse: :automatic, snaky: true, **options)
46
52
  @response = response
47
- @options = {parse: :automatic}.merge(opts)
53
+ @options = {
54
+ parse: parse,
55
+ snaky: snaky,
56
+ }.merge(options)
48
57
  end
49
58
 
50
59
  # The HTTP response headers
@@ -81,7 +90,7 @@ module OAuth2
81
90
  end
82
91
  end
83
92
 
84
- @parsed = OAuth2::SnakyHash.new(@parsed) if @parsed.is_a?(Hash)
93
+ @parsed = SnakyHash::StringKeyed.new(@parsed) if options[:snaky] && @parsed.is_a?(Hash)
85
94
 
86
95
  @parsed
87
96
  end
@@ -125,10 +134,14 @@ module OAuth2
125
134
  end
126
135
 
127
136
  OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml', 'application/xml']) do |body|
137
+ next body unless body.respond_to?(:to_str)
138
+
128
139
  MultiXml.parse(body)
129
140
  end
130
141
 
131
142
  OAuth2::Response.register_parser(:json, ['application/json', 'text/javascript', 'application/hal+json', 'application/vnd.collection+json', 'application/vnd.api+json', 'application/problem+json']) do |body|
143
+ next body unless body.respond_to?(:to_str)
144
+
132
145
  body = body.dup.force_encoding(::Encoding::ASCII_8BIT) if body.respond_to?(:force_encoding)
133
146
 
134
147
  ::JSON.parse(body)
@@ -80,7 +80,7 @@ module OAuth2
80
80
  assertion = build_assertion(claims, encoding_opts)
81
81
  params = build_request(assertion, request_opts)
82
82
 
83
- @client.get_token(params, response_opts.merge('refresh_token' => nil))
83
+ @client.get_token(params, response_opts)
84
84
  end
85
85
 
86
86
  private
@@ -25,7 +25,7 @@ module OAuth2
25
25
  #
26
26
  # @param [String] code The Authorization Code value
27
27
  # @param [Hash] params additional params
28
- # @param [Hash] opts options
28
+ # @param [Hash] opts access_token_opts, @see Client#get_token
29
29
  # @note that you must also provide a :redirect_uri with most OAuth 2.0 providers
30
30
  def get_token(code, params = {}, opts = {})
31
31
  params = {'grant_type' => 'authorization_code', 'code' => code}.merge(@client.redirection_params).merge(params)
@@ -19,7 +19,7 @@ module OAuth2
19
19
  # @param [Hash] opts options
20
20
  def get_token(params = {}, opts = {})
21
21
  params = params.merge('grant_type' => 'client_credentials')
22
- @client.get_token(params, opts.merge('refresh_token' => nil))
22
+ @client.get_token(params, opts)
23
23
  end
24
24
  end
25
25
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OAuth2
4
4
  module Version
5
- VERSION = '2.0.1'.freeze
5
+ VERSION = '2.0.9'.freeze
6
6
  end
7
7
  end
data/lib/oauth2.rb CHANGED
@@ -5,13 +5,12 @@ require 'cgi'
5
5
  require 'time'
6
6
 
7
7
  # third party gems
8
- require 'rash'
8
+ require 'snaky_hash'
9
9
  require 'version_gem'
10
10
 
11
11
  # includes gem files
12
12
  require 'oauth2/version'
13
13
  require 'oauth2/error'
14
- require 'oauth2/snaky_hash'
15
14
  require 'oauth2/authenticator'
16
15
  require 'oauth2/client'
17
16
  require 'oauth2/strategy/base'
@@ -25,6 +24,15 @@ require 'oauth2/response'
25
24
 
26
25
  # The namespace of this library
27
26
  module OAuth2
27
+ DEFAULT_CONFIG = SnakyHash::SymbolKeyed.new(silence_extra_tokens_warning: false)
28
+ @config = DEFAULT_CONFIG.dup
29
+ class << self
30
+ attr_accessor :config
31
+ end
32
+ def configure
33
+ yield @config
34
+ end
35
+ module_function :configure
28
36
  end
29
37
 
30
38
  OAuth2::Version.class_eval do
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oauth2
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Boling
8
8
  - Erik Michaels-Ober
9
9
  - Michael Bleigh
10
- autorequire:
10
+ autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2022-06-22 00:00:00.000000000 Z
13
+ date: 2022-09-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: faraday
@@ -75,7 +75,7 @@ dependencies:
75
75
  version: '1.2'
76
76
  - - "<"
77
77
  - !ruby/object:Gem::Version
78
- version: '3'
78
+ version: '4'
79
79
  type: :runtime
80
80
  prerelease: false
81
81
  version_requirements: !ruby/object:Gem::Requirement
@@ -85,41 +85,35 @@ dependencies:
85
85
  version: '1.2'
86
86
  - - "<"
87
87
  - !ruby/object:Gem::Version
88
- version: '3'
88
+ version: '4'
89
89
  - !ruby/object:Gem::Dependency
90
- name: rash_alt
90
+ name: snaky_hash
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ">="
94
- - !ruby/object:Gem::Version
95
- version: '0.4'
96
- - - "<"
93
+ - - "~>"
97
94
  - !ruby/object:Gem::Version
98
- version: '1'
95
+ version: '2.0'
99
96
  type: :runtime
100
97
  prerelease: false
101
98
  version_requirements: !ruby/object:Gem::Requirement
102
99
  requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: '0.4'
106
- - - "<"
100
+ - - "~>"
107
101
  - !ruby/object:Gem::Version
108
- version: '1'
102
+ version: '2.0'
109
103
  - !ruby/object:Gem::Dependency
110
104
  name: version_gem
111
105
  requirement: !ruby/object:Gem::Requirement
112
106
  requirements:
113
107
  - - "~>"
114
108
  - !ruby/object:Gem::Version
115
- version: '1.0'
109
+ version: '1.1'
116
110
  type: :runtime
117
111
  prerelease: false
118
112
  version_requirements: !ruby/object:Gem::Requirement
119
113
  requirements:
120
114
  - - "~>"
121
115
  - !ruby/object:Gem::Version
122
- version: '1.0'
116
+ version: '1.1'
123
117
  - !ruby/object:Gem::Dependency
124
118
  name: addressable
125
119
  requirement: !ruby/object:Gem::Requirement
@@ -294,7 +288,6 @@ files:
294
288
  - lib/oauth2/client.rb
295
289
  - lib/oauth2/error.rb
296
290
  - lib/oauth2/response.rb
297
- - lib/oauth2/snaky_hash.rb
298
291
  - lib/oauth2/strategy/assertion.rb
299
292
  - lib/oauth2/strategy/auth_code.rb
300
293
  - lib/oauth2/strategy/base.rb
@@ -302,18 +295,35 @@ files:
302
295
  - lib/oauth2/strategy/implicit.rb
303
296
  - lib/oauth2/strategy/password.rb
304
297
  - lib/oauth2/version.rb
305
- homepage: https://github.com/oauth-xx/oauth2
298
+ homepage: https://gitlab.com/oauth-xx/oauth2
306
299
  licenses:
307
300
  - MIT
308
301
  metadata:
309
- homepage_uri: https://github.com/oauth-xx/oauth2
310
- source_code_uri: https://github.com/oauth-xx/oauth2/tree/v2.0.1
311
- changelog_uri: https://github.com/oauth-xx/oauth2/blob/v2.0.1/CHANGELOG.md
312
- bug_tracker_uri: https://github.com/oauth-xx/oauth2/issues
313
- documentation_uri: https://www.rubydoc.info/gems/oauth2/2.0.1
314
- wiki_uri: https://github.com/oauth-xx/oauth2/wiki
302
+ homepage_uri: https://gitlab.com/oauth-xx/oauth2
303
+ source_code_uri: https://gitlab.com/oauth-xx/oauth2/-/tree/v2.0.9
304
+ changelog_uri: https://gitlab.com/oauth-xx/oauth2/-/blob/v2.0.9/CHANGELOG.md
305
+ bug_tracker_uri: https://gitlab.com/oauth-xx/oauth2/-/issues
306
+ documentation_uri: https://www.rubydoc.info/gems/oauth2/2.0.9
307
+ wiki_uri: https://gitlab.com/oauth-xx/oauth2/-/wiki
308
+ funding_uri: https://liberapay.com/pboling
315
309
  rubygems_mfa_required: 'true'
316
- post_install_message:
310
+ post_install_message: |2+
311
+
312
+ You have installed oauth2 version 2.0.9, congratulations!
313
+
314
+ There are BREAKING changes if you are upgrading from < v2, but most will not encounter them, and updating your code should be easy!
315
+
316
+ We have made two other major migrations:
317
+ 1. master branch renamed to main
318
+ 2. Github has been replaced with Gitlab
319
+
320
+ Please see:
321
+ • https://gitlab.com/oauth-xx/oauth2#what-is-new-for-v20
322
+ • https://gitlab.com/oauth-xx/oauth2/-/blob/main/CHANGELOG.md
323
+ • https://groups.google.com/g/oauth-ruby/c/QA_dtrXWXaE
324
+
325
+ Please report issues, and support the project! Thanks, |7eter l-|. l3oling
326
+
317
327
  rdoc_options: []
318
328
  require_paths:
319
329
  - lib
@@ -328,8 +338,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
328
338
  - !ruby/object:Gem::Version
329
339
  version: '0'
330
340
  requirements: []
331
- rubygems_version: 3.3.16
332
- signing_key:
341
+ rubygems_version: 3.3.21
342
+ signing_key:
333
343
  specification_version: 4
334
344
  summary: A Ruby wrapper for the OAuth 2.0 protocol.
335
345
  test_files: []
346
+ ...
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OAuth2
4
- # Hash which allow assign string key in camel case
5
- # and query on both camel and snake case
6
- class SnakyHash < ::Hashie::Mash::Rash
7
- end
8
- end