Module: Doorkeeper::OAuth::Helpers::URIChecker

Defined in:
lib/doorkeeper/oauth/helpers/uri_checker.rb

Class Method Summary collapse

Class Method Details

.as_uri(url) ⇒ Object



71
72
73
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 71

def self.as_uri(url)
  URI.parse(url)
end

.hypertext_scheme?(uri) ⇒ Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 81

def self.hypertext_scheme?(uri)
  %w[http https].include?(uri.scheme)
end

.iff_host?(uri) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 85

def self.iff_host?(uri)
  !(hypertext_scheme?(uri) && uri.host.blank?)
end

.loopback_uri?(uri) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
59
60
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 56

def self.loopback_uri?(uri)
  IPAddr.new(uri.host).loopback?
rescue IPAddr::Error, IPAddr::InvalidAddressError
  false
end

.matches?(url, client_url) ⇒ Boolean

RFC6749, Section 3.1.2.3 requires the requested redirect URI to be compared to the registered redirect URIs using the simple string comparison defined in RFC3986, Section 6.2.1.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 22

def self.matches?(url, client_url)
  return true if url == client_url

  # RFC8252, Paragraph 7.3 allows the port of loopback interface
  # redirect URIs to vary at runtime, so it is ignored when both
  # URIs point to the loopback interface.
  # @see https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
  url = as_uri(url)
  client_url = as_uri(client_url)

  return false unless loopback_uri?(url) && loopback_uri?(client_url)

  urls_match?(url, client_url)
rescue URI::InvalidURIError
  false
end

.oob_uri?(uri) ⇒ Boolean

Returns:

  • (Boolean)


89
90
91
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 89

def self.oob_uri?(uri)
  NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
end

.urls_match?(url, client_url) ⇒ Boolean

RFC 8252 §7.3 lets only the PORT of a loopback redirect URI vary at runtime. Compare every other component explicitly instead of blanking the port and comparing the reassembled strings: URI#port= also clears the userinfo on Ruby >= 4.0, which would let http://attacker@127.0.0.1/cb match a registered http://127.0.0.1/cb. Comparing components (not URI#==) also keeps the exception from widening past the port — e.g. an empty path is not treated as equal to "/".

Returns:

  • (Boolean)


47
48
49
50
51
52
53
54
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 47

def self.urls_match?(url, client_url)
  url.scheme == client_url.scheme &&
    url.userinfo == client_url.userinfo &&
    url.host == client_url.host &&
    url.path == client_url.path &&
    url.query == client_url.query &&
    url.fragment == client_url.fragment
end

.valid?(url) ⇒ Boolean

Returns:

  • (Boolean)


9
10
11
12
13
14
15
16
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 9

def self.valid?(url)
  return true if oob_uri?(url)

  uri = as_uri(url)
  valid_scheme?(uri) && iff_host?(uri) && uri.fragment.nil? && uri.opaque.nil?
rescue URI::InvalidURIError
  false
end

.valid_for_authorization?(url, client_url) ⇒ Boolean

Returns:

  • (Boolean)


62
63
64
65
66
67
68
69
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 62

def self.valid_for_authorization?(url, client_url)
  # client_url is blank when the application was registered without a
  # redirect URI (allow_blank_redirect_uri) — no possible match for
  # redirection based OAuth flows, not an error.
  return false if client_url.blank?

  valid?(url) && client_url.split.any? { |other_url| matches?(url, other_url) }
end

.valid_scheme?(uri) ⇒ Boolean

Returns:

  • (Boolean)


75
76
77
78
79
# File 'lib/doorkeeper/oauth/helpers/uri_checker.rb', line 75

def self.valid_scheme?(uri)
  return false if uri.scheme.blank?

  %w[localhost].exclude?(uri.scheme)
end