oauth2 2.0.20 → 2.0.23

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/RUBOCOP.md CHANGED
File without changes
data/SECURITY.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |----------|-----------|
7
- | 1.latest | ✅ |
7
+ | 2.0.latest | ✅ |
8
8
 
9
9
  ## Security contact information
10
10
 
@@ -12,8 +12,6 @@ To report a security vulnerability, please use the
12
12
  [Tidelift security contact](https://tidelift.com/security).
13
13
  Tidelift will coordinate the fix and disclosure.
14
14
 
15
- More detailed explanation of the process is in [IRP.md][IRP].
16
-
17
15
  ## Additional Support
18
16
 
19
17
  If you are interested in support for versions older than the latest release,
@@ -21,4 +19,3 @@ please consider sponsoring the project / maintainer @ https://liberapay.com/pbol
21
19
  or find other sponsorship links in the [README].
22
20
 
23
21
  [README]: README.md
24
- [IRP]: IRP.md
data/certs/pboling.pem ADDED
@@ -0,0 +1,27 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl
3
+ ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW
4
+ A2NvbTAeFw0yNTA1MDQxNTMzMDlaFw00NTA0MjkxNTMzMDlaMEMxFTATBgNVBAMM
5
+ DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
6
+ LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAruUoo0WA
7
+ uoNuq6puKWYeRYiZekz/nsDeK5x/0IEirzcCEvaHr3Bmz7rjo1I6On3gGKmiZs61
8
+ LRmQ3oxy77ydmkGTXBjruJB+pQEn7UfLSgQ0xa1/X3kdBZt6RmabFlBxnHkoaGY5
9
+ mZuZ5+Z7walmv6sFD9ajhzj+oIgwWfnEHkXYTR8I6VLN7MRRKGMPoZ/yvOmxb2DN
10
+ coEEHWKO9CvgYpW7asIihl/9GMpKiRkcYPm9dGQzZc6uTwom1COfW0+ZOFrDVBuV
11
+ FMQRPswZcY4Wlq0uEBLPU7hxnCL9nKK6Y9IhdDcz1mY6HZ91WImNslOSI0S8hRpj
12
+ yGOWxQIhBT3fqCBlRIqFQBudrnD9jSNpSGsFvbEijd5ns7Z9ZMehXkXDycpGAUj1
13
+ to/5cuTWWw1JqUWrKJYoifnVhtE1o1DZ+LkPtWxHtz5kjDG/zR3MG0Ula0UOavlD
14
+ qbnbcXPBnwXtTFeZ3C+yrWpE4pGnl3yGkZj9SMTlo9qnTMiPmuWKQDatAgMBAAGj
15
+ fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQE8uWvNbPVNRXZ
16
+ HlgPbc2PCzC4bjAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG
17
+ A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
18
+ ggGBAJbnUwfJQFPkBgH9cL7hoBfRtmWiCvdqdjeTmi04u8zVNCUox0A4gT982DE9
19
+ wmuN12LpdajxZONqbXuzZvc+nb0StFwmFYZG6iDwaf4BPywm2e/Vmq0YG45vZXGR
20
+ L8yMDSK1cQXjmA+ZBKOHKWavxP6Vp7lWvjAhz8RFwqF9GuNIdhv9NpnCAWcMZtpm
21
+ GUPyIWw/Cw/2wZp74QzZj6Npx+LdXoLTF1HMSJXZ7/pkxLCsB8m4EFVdb/IrW/0k
22
+ kNSfjtAfBHO8nLGuqQZVH9IBD1i9K6aSs7pT6TW8itXUIlkIUI2tg5YzW6OFfPzq
23
+ QekSkX3lZfY+HTSp/o+YvKkqWLUV7PQ7xh1ZYDtocpaHwgxe/j3bBqHE+CUPH2vA
24
+ 0V/FwdTRWcwsjVoOJTrYcff8pBZ8r2MvtAc54xfnnhGFzeRHfcltobgFxkAXdE6p
25
+ DVjBtqT23eugOqQ73umLcYDZkc36vnqGxUBSsXrzY9pzV5gGr2I8YUxMqf6ATrZt
26
+ L9nRqA==
27
+ -----END CERTIFICATE-----
@@ -68,13 +68,7 @@ module OAuth2
68
68
  end
69
69
  # :nocov:
70
70
  # TODO: Get rid of this branching logic when dropping Hashie < v3.2
71
- token = if !defined?(Hashie::VERSION) # i.e. <= "1.1.0"; the first Hashie to ship with a VERSION constant
72
- warn("snaky_hash and oauth2 will drop support for Hashie v0 in the next major version. Please upgrade to a modern Hashie.")
73
- # There is a bug in Hashie v0, which is accounts for.
74
- fresh.delete(t_key) || fresh[t_key] || ""
75
- else
76
- fresh.delete(t_key) || ""
77
- end
71
+ token = extract_token_value(fresh, t_key)
78
72
  # :nocov:
79
73
  new(client, token, fresh)
80
74
  end
@@ -108,6 +102,17 @@ Custom token_name (#{key}) is not found in (#{hash.keys})
108
102
  You may need to set `snaky: false`. See inline documentation for more info.
109
103
  ])
110
104
  end
105
+
106
+ # :nocov:
107
+ def extract_token_value(fresh, key)
108
+ token_value = fresh.delete(key)
109
+ return token_value || "" if defined?(Hashie::VERSION)
110
+
111
+ warn("snaky_hash and oauth2 will drop support for Hashie v0 in the next major version. Please upgrade to a modern Hashie.")
112
+ # There is a bug in Hashie v0, which this accounts for.
113
+ token_value || fresh[key] || ""
114
+ end
115
+ # :nocov:
111
116
  end
112
117
 
113
118
  # Initialize an AccessToken
@@ -305,8 +310,8 @@ You may need to set `snaky: false`. See inline documentation for more info.
305
310
  # TODO: Switch when dropping Ruby < 2.5 support
306
311
  # params.transform_keys(&:to_sym) # Ruby 2.5 only
307
312
  # Old Ruby transform_keys alternative:
308
- sheesh = @params.each_with_object({}) { |(k, v), memo|
309
- memo[k.to_sym] = v
313
+ sheesh = @params.each_with_object({}) { |(key, value), memo|
314
+ memo[key.to_sym] = value
310
315
  }
311
316
  sheesh.merge(hsh)
312
317
  end
@@ -375,6 +380,7 @@ You may need to set `snaky: false`. See inline documentation for more info.
375
380
 
376
381
  def configure_authentication!(opts, verb)
377
382
  mode_opt = options[:mode]
383
+ param_name = options[:param_name]
378
384
  mode =
379
385
  if mode_opt.respond_to?(:call)
380
386
  mode_opt.call(verb)
@@ -388,19 +394,19 @@ You may need to set `snaky: false`. See inline documentation for more info.
388
394
 
389
395
  case mode
390
396
  when :header
391
- opts[:headers] ||= {}
392
- opts[:headers].merge!(headers)
397
+ request_headers = opts[:headers] ||= {}
398
+ request_headers.merge!(headers)
393
399
  when :query
394
400
  # OAuth 2.1 note: Bearer tokens in the query string are omitted from the spec due to security risks.
395
401
  # Prefer the default :header mode whenever possible.
396
- opts[:params] ||= {}
397
- opts[:params][options[:param_name]] = token
402
+ request_params = opts[:params] ||= {}
403
+ request_params[param_name] = token
398
404
  when :body
399
- opts[:body] ||= {}
400
- if opts[:body].is_a?(Hash)
401
- opts[:body][options[:param_name]] = token
405
+ request_body = opts[:body] ||= {}
406
+ if request_body.is_a?(Hash)
407
+ request_body[param_name] = token
402
408
  else
403
- opts[:body] += "&#{options[:param_name]}=#{token}"
409
+ opts[:body] = "#{request_body}&#{param_name}=#{token}"
404
410
  end
405
411
  # @todo support for multi-part (file uploads)
406
412
  else
@@ -2,7 +2,7 @@
2
2
 
3
3
  module OAuth2
4
4
  AUTH_SANITIZER = begin
5
- auth_sanitizer_requirement = Gem::Requirement.new("~> 0.1", ">= 0.1.3")
5
+ auth_sanitizer_requirement = Gem::Requirement.new("~> 0.2", ">= 0.2.1")
6
6
  auth_sanitizer_spec = Gem.loaded_specs["auth-sanitizer"]
7
7
  unless auth_sanitizer_spec && auth_sanitizer_requirement.satisfied_by?(auth_sanitizer_spec.version)
8
8
  # :nocov:
@@ -12,7 +12,7 @@ module OAuth2
12
12
 
13
13
  auth_sanitizer_loader_path = File.join(
14
14
  auth_sanitizer_spec.full_gem_path,
15
- "lib/auth_sanitizer/loader.rb",
15
+ "lib/auth_sanitizer/loader.rb"
16
16
  )
17
17
  unless File.file?(auth_sanitizer_loader_path)
18
18
  # :nocov:
@@ -25,7 +25,7 @@ module OAuth2
25
25
  auth_sanitizer_loader_namespace.module_eval(
26
26
  File.read(auth_sanitizer_loader_path),
27
27
  auth_sanitizer_loader_path,
28
- 1,
28
+ 1
29
29
  )
30
30
 
31
31
  auth_sanitizer_loader_namespace.
File without changes
data/lib/oauth2/client.rb CHANGED
@@ -86,8 +86,9 @@ module OAuth2
86
86
  @connection ||=
87
87
  Faraday.new(site, options[:connection_opts]) do |builder|
88
88
  oauth_debug_logging(builder)
89
- if options[:connection_build]
90
- options[:connection_build].call(builder)
89
+ connection_build = options[:connection_build]
90
+ if connection_build
91
+ connection_build.call(builder)
91
92
  else
92
93
  builder.request(:url_encoded) # form-encode POST params
93
94
  builder.adapter(Faraday.default_adapter) # make requests with Net::HTTP
@@ -149,9 +150,9 @@ module OAuth2
149
150
 
150
151
  case status
151
152
  when 301, 302, 303, 307
152
- req_opts[:redirect_count] ||= 0
153
- req_opts[:redirect_count] += 1
154
- return response if req_opts[:redirect_count] > options[:max_redirects]
153
+ redirect_count = (req_opts[:redirect_count] || 0).to_i + 1
154
+ req_opts[:redirect_count] = redirect_count
155
+ return response if redirect_count > options[:max_redirects]
155
156
 
156
157
  if status == 303
157
158
  verb = :get
@@ -159,8 +160,9 @@ module OAuth2
159
160
  end
160
161
  location = response.headers["location"]
161
162
  if location
162
- full_location = response.response.env.url.merge(location)
163
- request(verb, full_location, req_opts)
163
+ current_location = response.response.env.url
164
+ full_location = resolve_redirect_location(current_location, location)
165
+ request(verb, full_location, sanitize_redirect_options(req_opts, current_location, full_location))
164
166
  else
165
167
  error = Error.new(response)
166
168
  raise(error, "Got #{status} status code, but no Location header was present")
@@ -337,8 +339,9 @@ module OAuth2
337
339
  #
338
340
  # @return [Hash] the params to add to a request or URL
339
341
  def redirection_params
340
- if options[:redirect_uri]
341
- {"redirect_uri" => options[:redirect_uri]}
342
+ redirect_uri = options[:redirect_uri]
343
+ if redirect_uri
344
+ {"redirect_uri" => redirect_uri}
342
345
  else
343
346
  {}
344
347
  end
@@ -445,18 +448,18 @@ module OAuth2
445
448
  url = connection.build_url(url).to_s
446
449
  # See: Hash#partition https://bugs.ruby-lang.org/issues/16252
447
450
  req_opts, oauth_opts = opts.
448
- partition { |k, _v| RESERVED_REQ_KEYS.include?(k.to_s) }.
449
- map { |p| Hash[p] }
451
+ partition { |key, _value| RESERVED_REQ_KEYS.include?(key.to_s) }.
452
+ map(&:to_h)
450
453
 
451
454
  begin
452
455
  response = connection.run_request(verb, url, req_opts[:body], req_opts[:headers]) do |req|
453
456
  req.params.update(req_opts[:params]) if req_opts[:params]
454
457
  yield(req) if block_given?
455
458
  end
456
- rescue Faraday::ConnectionFailed => e
457
- raise ConnectionError, e
458
- rescue Faraday::TimeoutError => e
459
- raise TimeoutError, e
459
+ rescue Faraday::ConnectionFailed => exception
460
+ raise ConnectionError, exception
461
+ rescue Faraday::TimeoutError => exception
462
+ raise TimeoutError, exception
460
463
  end
461
464
 
462
465
  parse = oauth_opts.key?(:parse) ? oauth_opts.delete(:parse) : Response::DEFAULT_OPTIONS[:parse]
@@ -465,6 +468,49 @@ module OAuth2
465
468
  Response.new(response, parse: parse, snaky: snaky)
466
469
  end
467
470
 
471
+ def resolve_redirect_location(current_location, location)
472
+ return protocol_relative_redirect_location(current_location, location) if location.respond_to?(:start_with?) && location.start_with?("//")
473
+
474
+ current_location.merge(location)
475
+ end
476
+
477
+ def protocol_relative_redirect_location(current_location, location)
478
+ protocol_relative_location = URI.parse(location)
479
+ authority = +""
480
+ authority << "#{protocol_relative_location.userinfo}@" if protocol_relative_location.userinfo
481
+ authority << protocol_relative_location.host.to_s
482
+ authority << ":#{protocol_relative_location.port}" if protocol_relative_location.port
483
+
484
+ current_location.dup.tap do |safe_location|
485
+ safe_location.path = "///#{authority}#{protocol_relative_location.path}"
486
+ safe_location.query = protocol_relative_location.query if safe_location.respond_to?(:query=)
487
+ safe_location.fragment = protocol_relative_location.fragment if safe_location.respond_to?(:fragment=)
488
+ end
489
+ end
490
+
491
+ def sanitize_redirect_options(req_opts, current_location, next_location)
492
+ return req_opts unless cross_origin_redirect?(current_location, next_location)
493
+
494
+ headers = req_opts[:headers]
495
+ return req_opts unless headers && headers.any? { |key, _value| authorization_header?(key) }
496
+
497
+ safe_opts = req_opts.dup
498
+ safe_headers = headers.dup
499
+ safe_headers.delete_if { |key, _value| authorization_header?(key) }
500
+ safe_opts[:headers] = safe_headers
501
+ safe_opts
502
+ end
503
+
504
+ def authorization_header?(key)
505
+ key.to_s.casecmp("Authorization").zero?
506
+ end
507
+
508
+ def cross_origin_redirect?(current_location, next_location)
509
+ current_location.scheme != next_location.scheme ||
510
+ current_location.host != next_location.host ||
511
+ current_location.port != next_location.port
512
+ end
513
+
468
514
  # Returns the authenticator object
469
515
  #
470
516
  # @return [Authenticator] the initialized Authenticator
@@ -563,15 +609,18 @@ module OAuth2
563
609
  end
564
610
 
565
611
  def oauth_debug_logging(builder)
566
- builder.response(
567
- :logger,
568
- OAuth2::AUTH_SANITIZER::SanitizedLogger.new(
569
- options[:logger],
570
- filtered_keys: OAuth2.config[:filtered_debug_keys],
571
- label: OAuth2.config[:filtered_label],
572
- ),
573
- bodies: true,
574
- ) if OAuth2::OAUTH_DEBUG
612
+ if OAuth2::OAUTH_DEBUG
613
+ config = OAuth2.config
614
+ builder.response(
615
+ :logger,
616
+ OAuth2::AUTH_SANITIZER::SanitizedLogger.new(
617
+ options[:logger],
618
+ filtered_keys: config[:filtered_debug_keys],
619
+ label: config[:filtered_label]
620
+ ),
621
+ bodies: true
622
+ )
623
+ end
575
624
  end
576
625
  end
577
626
  end
data/lib/oauth2/error.rb CHANGED
@@ -20,9 +20,10 @@ module OAuth2
20
20
  @code = nil
21
21
  @description = nil
22
22
  if response.respond_to?(:parsed)
23
- if response.parsed.is_a?(Hash)
24
- @code = response.parsed["error"]
25
- @description = response.parsed["error_description"]
23
+ parsed_response = response.parsed
24
+ if parsed_response.is_a?(Hash)
25
+ @code = parsed_response["error"]
26
+ @description = parsed_response["error_description"]
26
27
  end
27
28
  elsif response.is_a?(Hash)
28
29
  @code = response["error"]
@@ -46,11 +47,12 @@ module OAuth2
46
47
  # @return [String] Message suitable for StandardError
47
48
  def error_message(response_body, opts = {})
48
49
  lines = []
50
+ error_description = opts[:error_description]
49
51
 
50
- lines << opts[:error_description] if opts[:error_description]
52
+ lines << error_description if error_description
51
53
 
52
- error_string = if response_body.respond_to?(:encode) && opts[:error_description].respond_to?(:encoding)
53
- script_encoding = opts[:error_description].encoding
54
+ error_string = if response_body.respond_to?(:encode) && error_description.respond_to?(:encoding)
55
+ script_encoding = error_description.encoding
54
56
  response_body.encode(script_encoding, invalid: :replace, undef: :replace)
55
57
  else
56
58
  response_body
File without changes
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "set"
4
5
  require "multi_xml"
5
6
  require "rack"
6
7
 
@@ -108,15 +109,16 @@ module OAuth2
108
109
  def parsed
109
110
  return @parsed if defined?(@parsed)
110
111
 
112
+ response_parser = parser
111
113
  @parsed =
112
- if parser.respond_to?(:call)
113
- case parser.arity
114
+ if response_parser.respond_to?(:call)
115
+ case response_parser.arity
114
116
  when 0
115
- parser.call
117
+ response_parser.call
116
118
  when 1
117
- parser.call(body)
119
+ response_parser.call(body)
118
120
  else
119
- parser.call(body, response)
121
+ response_parser.call(body, response)
120
122
  end
121
123
  end
122
124
 
@@ -132,9 +134,10 @@ module OAuth2
132
134
  #
133
135
  # @return [String, nil] The content type or nil if headers are not present
134
136
  def content_type
135
- return unless response.headers
137
+ response_headers = response.headers
138
+ return unless response_headers
136
139
 
137
- ((response.headers.values_at("content-type", "Content-Type").compact.first || "").split(";").first || "").strip.downcase
140
+ ((response_headers.values_at("content-type", "Content-Type").compact.first || "").split(";").first || "").strip.downcase
138
141
  end
139
142
 
140
143
  # Determines the parser to be used for the response body
@@ -154,11 +157,12 @@ module OAuth2
154
157
  def parser
155
158
  return @parser if defined?(@parser)
156
159
 
160
+ parse_option = options[:parse]
157
161
  @parser =
158
- if options[:parse].respond_to?(:call)
159
- options[:parse]
160
- elsif options[:parse]
161
- @@parsers[options[:parse].to_sym]
162
+ if parse_option.respond_to?(:call)
163
+ parse_option
164
+ elsif parse_option
165
+ @@parsers[parse_option.to_sym]
162
166
  end
163
167
 
164
168
  @parser ||= @@parsers[@@content_types[content_type]]
@@ -171,7 +175,7 @@ end
171
175
  OAuth2::Response.register_parser(:xml, ["text/xml", "application/rss+xml", "application/rdf+xml", "application/atom+xml", "application/xml"]) do |body|
172
176
  next body unless body.respond_to?(:to_str)
173
177
 
174
- MultiXml.parse(body)
178
+ (defined?(MultiXML) ? MultiXML : MultiXml).parse(body)
175
179
  end
176
180
 
177
181
  # Register JSON parser
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -2,7 +2,7 @@
2
2
 
3
3
  module OAuth2
4
4
  module Version
5
- VERSION = "2.0.20"
5
+ VERSION = "2.0.23"
6
6
  end
7
7
  VERSION = Version::VERSION # Traditional Constant Location
8
8
  end
data/lib/oauth2.rb CHANGED
@@ -67,7 +67,7 @@ module OAuth2
67
67
  assertion
68
68
  code_verifier
69
69
  token
70
- ],
70
+ ]
71
71
  )
72
72
 
73
73
  # The current runtime configuration for the library.
File without changes
File without changes
File without changes
data/sig/oauth2/error.rbs CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -2,4 +2,5 @@ module OAuth2
2
2
  module Version
3
3
  VERSION: String
4
4
  end
5
+ VERSION: String
5
6
  end
data/sig/oauth2.rbs CHANGED
File without changes
data.tar.gz.sig CHANGED
Binary file