CVE-2026-54603
OAuth2::Client#request: Protocol-relative redirect Location overrides authority, leaking bearer Authorization to attacker host
OAuth2 is a Ruby wrapper for the OAuth 2.0 and 2.1 authorization frameworks, including OpenID Connect (OIDC). From 0.4.0 to 2.0.21, a protocol-relative redirect Location returned to OAuth2::Client#request overrides the request authority, so the bearer Authorization header is sent to an attacker-controlled host, leaking the credential. This issue is fixed in version 2.0.22.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.6
OAuth2::Client#request: Protocol-relative redirect Location overrides authority, leaking bearer Authorization to attacker host
OAuth2 is a Ruby wrapper for the OAuth 2.0 and 2.1 authorization frameworks, including OpenID Connect (OIDC). From 0.4.0 to 2.0.21, a protocol-relative redirect Location returned to OAuth2::Client#request overrides the request authority, so the bearer Authorization header is sent to an attacker-controlled host, leaking the credential. This issue is fixed in version 2.0.22.
- Record
CVE-2026-54603- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-200, CWE-601
- Updated
- 2026-11-02
EUVDEUVD-2026-49903
8.6
OAuth2 is a Ruby wrapper for the OAuth 2.0 and 2.1 authorization frameworks, including OpenID Connect (OIDC). From 0.4.0 to 2.0.21, a protocol-relative redirect Location returned to OAuth2::Client#request overrides the request authority, so the bearer Authorization header is sent to an attacker-controlled host, leaking the credential. This issue is fixed in version 2.0.22.
- Record
EUVD-2026-49903- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-pp92-crg2-gfv9
8.6
OAuth2::Client#request: Protocol-relative redirect Location overrides authority, leaking bearer Authorization to attacker host
Summary
When an application uses OAuth2::Client (typically via an OAuth2::AccessToken) and the configured authorization server returns a redirect whose Location header is a protocol-relative URI of the form //attacker.example/leak, OAuth2::Client#request resolves the redirect with response.response.env.url.merge(location). Per RFC 3986 §5.2, an input that starts with // is a network-path reference and replaces the authority of the base URL: URI("http://idp.trusted/userinfo").merge("//attacker.example/leak") returns http://attacker.example/leak. The recursive request(verb, full_location, req_opts) call then re-sends the request to the attacker host while preserving the Authorization: Bearer <access-token> header that OAuth2::AccessToken#configure_authentication! installed on req_opts[:headers] for the original request.
The result is a one-shot cross-origin credential disclosure: any 30x response from the IdP that an attacker can influence (a compromised endpoint, a tenant-controlled IdP in a multi-tenant deployment, or an open-redirect handler that does not normalise the Location it emits) can extract the bearer access token of the calling user.
Affected
oauth2v2.0.21 and all prior versions back to and including v0.4.0.- The underlying unsafe redirect-following behavior that reuses headers starts in v0.4.0 via https://github.com/ruby-oauth/oauth2/commit/b944da54cd70487c06d7252b9e4e7948eae56e73
- The vulnerability was retained when the code was refactored to a redirect helper, and in this form has been present in
OAuth2::Client#requestsince v2.0.0 via https://github.com/ruby-oauth/oauth2/commit/b944da54cd70487c06d7252b9e4e7948eae56e73
- Ruby 4.0.5 on
arm64-darwin25. The behaviour ofURI#mergefor protocol-relative inputs is RFC-conformant and the same on every supported Ruby (≥ 2.x). - Adapter independence: confirmed against the default
faraday2.14.2 +faraday-net_http3.4.3 stack. TheURI#mergecall is inoauth2itself, not in Faraday, so the issue is not adapter-specific.
Impact
A consumer that uses OAuth2::AccessToken#get / #post / #request against an IdP whose redirect target an attacker can influence (open redirect, malicious tenant, or in-path adversary) loses two things at once:
- Cross-origin credential disclosure. The connection-scoped
Authorization: Bearer <token>header attached byOAuth2::AccessToken#configure_authentication!is sent to the attacker host on the very next request, with no second user interaction. - SSRF from the application server. The OAuth2 client follows the redirect on behalf of the application, so the host that ultimately receives the request is one the attacker chooses — useful for hitting internal addresses (
//169.254.169.254/...,//127.0.0.1:.../...) that the application server can reach but the attacker cannot.
The combined primitive is stronger than the usual cross-origin-redirect leak because no application-level cooperation is required and no Location: http://attacker/... is needed — the protocol-relative //attacker/x form slips past naive scheme-based Location filters that allow same-scheme-implicit redirects.
Vulnerable code
lib/oauth2/client.rb#L146-L182 at commit e2d509705db6091c8d5f27c31e29c58e39e51c7c (tag v2.0.20):
def request(verb, url, req_opts = {}, &block)
response = execute_request(verb, url, req_opts, &block)
status = response.status
case status
when 301, 302, 303, 307
req_opts[:redirect_count] ||= 0
req_opts[:redirect_count] += 1
return response if req_opts[:redirect_count] > options[:max_redirects]
if status == 303
verb = :get
req_opts.delete(:body)
end
location = response.headers["location"]
if location
full_location = response.response.env.url.merge(location) # <--…
- Record
GHSA-pp92-crg2-gfv9- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-200, CWE-601
- Updated
- 2026-11-02
NVD
8.6
OAuth2 is a Ruby wrapper for the OAuth 2.0 and 2.1 authorization frameworks, including OpenID Connect (OIDC). From 0.4.0 to 2.0.21, a protocol-relative redirect Location returned to OAuth2::Client#request overrides the request authority, so the bearer Authorization header is sent to an attacker-controlled host, leaking the credential. This issue is fixed in version 2.0.22.
- Record
CVE-2026-54603- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-200, CWE-601
- Updated
- 2026-11-02
OSV
8.6
OAuth2::Client#request: Protocol-relative redirect Location overrides authority, leaking bearer Authorization to attacker host
OAuth2 is a Ruby wrapper for the OAuth 2.0 and 2.1 authorization frameworks, including OpenID Connect (OIDC). From 0.4.0 to 2.0.21, a protocol-relative redirect Location returned to OAuth2::Client#request overrides the request authority, so the bearer Authorization header is sent to an attacker-controlled host, leaking the credential. This issue is fixed in version 2.0.22.
- Record
CVE-2026-54603- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-200, CWE-601
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-54603
8.6
OAuth2 is a Ruby wrapper for the OAuth 2.0 and 2.1 authorization frameworks, including OpenID Connect (OIDC). From 0.4.0 to 2.0.21, a protocol-relative redirect Location returned to OAuth2::Client#request overrides the request authority, so the bearer Authorization header is sent to an attacker-controlled host, leaking the credential. This issue is fixed in version 2.0.22.
- Record
DEBIAN-CVE-2026-54603- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Updated
- 2026-11-02
OSVGHSA-pp92-crg2-gfv9
8.6
OAuth2::Client#request: Protocol-relative redirect Location overrides authority, leaking bearer Authorization to attacker host
Summary
When an application uses OAuth2::Client (typically via an OAuth2::AccessToken) and the configured authorization server returns a redirect whose Location header is a protocol-relative URI of the form //attacker.example/leak, OAuth2::Client#request resolves the redirect with response.response.env.url.merge(location). Per RFC 3986 §5.2, an input that starts with // is a network-path reference and replaces the authority of the base URL: URI("http://idp.trusted/userinfo").merge("//attacker.example/leak") returns http://attacker.example/leak. The recursive request(verb, full_location, req_opts) call then re-sends the request to the attacker host while preserving the Authorization: Bearer <access-token> header that OAuth2::AccessToken#configure_authentication! installed on req_opts[:headers] for the original request.
The result is a one-shot cross-origin credential disclosure: any 30x response from the IdP that an attacker can influence (a compromised endpoint, a tenant-controlled IdP in a multi-tenant deployment, or an open-redirect handler that does not normalise the Location it emits) can extract the bearer access token of the calling user.
Affected
oauth2v2.0.21 and all prior versions back to and including v0.4.0.- The underlying unsafe redirect-following behavior that reuses headers starts in v0.4.0 via https://github.com/ruby-oauth/oauth2/commit/b944da54cd70487c06d7252b9e4e7948eae56e73
- The vulnerability was retained when the code was refactored to a redirect helper, and in this form has been present in
OAuth2::Client#requestsince v2.0.0 via https://github.com/ruby-oauth/oauth2/commit/b944da54cd70487c06d7252b9e4e7948eae56e73
- Ruby 4.0.5 on
arm64-darwin25. The behaviour ofURI#mergefor protocol-relative inputs is RFC-conformant and the same on every supported Ruby (≥ 2.x). - Adapter independence: confirmed against the default
faraday2.14.2 +faraday-net_http3.4.3 stack. TheURI#mergecall is inoauth2itself, not in Faraday, so the issue is not adapter-specific.
Impact
A consumer that uses OAuth2::AccessToken#get / #post / #request against an IdP whose redirect target an attacker can influence (open redirect, malicious tenant, or in-path adversary) loses two things at once:
- Cross-origin credential disclosure. The connection-scoped
Authorization: Bearer <token>header attached byOAuth2::AccessToken#configure_authentication!is sent to the attacker host on the very next request, with no second user interaction. - SSRF from the application server. The OAuth2 client follows the redirect on behalf of the application, so the host that ultimately receives the request is one the attacker chooses — useful for hitting internal addresses (
//169.254.169.254/...,//127.0.0.1:.../...) that the application server can reach but the attacker cannot.
The combined primitive is stronger than the usual cross-origin-redirect leak because no application-level cooperation is required and no Location: http://attacker/... is needed — the protocol-relative //attacker/x form slips past naive scheme-based Location filters that allow same-scheme-implicit redirects.
Vulnerable code
lib/oauth2/client.rb#L146-L182 at commit e2d509705db6091c8d5f27c31e29c58e39e51c7c (tag v2.0.20):
def request(verb, url, req_opts = {}, &block)
response = execute_request(verb, url, req_opts, &block)
status = response.status
case status
when 301, 302, 303, 307
req_opts[:redirect_count] ||= 0
req_opts[:redirect_count] += 1
return response if req_opts[:redirect_count] > options[:max_redirects]
if status == 303
verb = :get
req_opts.delete(:body)
end
location = response.headers["location"]
if location
full_location = response.response.env.url.merge(location) # <--…
- Record
GHSA-pp92-crg2-gfv9- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-200, CWE-601
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-54603
8.6
OAuth2 is a Ruby wrapper for the OAuth 2.0 and 2.1 authorization frameworks, including OpenID Connect (OIDC). From 0.4.0 to 2.0.21, a protocol-relative redirect Location returned to OAuth2::Client#request overrides the request authority, so the bearer Authorization header is sent to an attacker-controlled host, leaking the credential. This issue is fixed in version 2.0.22.
- Record
UBUNTU-CVE-2026-54603- CVSS 3.1
- 8.6 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
oauth2RubyGems |
0.4.0 to 2.0.21 | 2.0.22 |
ruby-oauth2Debian:12 |
before 1.4.4-1+deb12u1, plus listed versions 1.4.4-1 |
1.4.4-1+deb12u1 |
ruby-oauth2Debian:13 |
All versions, plus listed versions 2.0.17-12.0.18-12.0.25-12.0.9-1 |
None known |
ruby-oauth2Debian:14 |
before 2.0.25-1, plus listed versions 2.0.17-12.0.18-12.0.9-1 |
2.0.25-1 |
oauth2RubyGems |
0.4.0 up to 2.0.22, plus listed versions 0.4.10.5.00.5.10.5.20.6.0 Show 45 moreShow less0.6.10.7.00.7.10.8.00.8.10.9.00.9.10.9.20.9.30.9.41.0.01.1.01.2.01.3.01.3.11.4.01.4.11.4.101.4.111.4.21.4.31.4.41.4.51.4.61.4.71.4.81.4.92.0.02.0.0.rc12.0.0.rc22.0.0.rc32.0.12.0.102.0.112.0.122.0.132.0.142.0.152.0.162.0.172.0.182.0.192.0.22.0.202.0.21 |
2.0.22 |
ruby-oauth2Ubuntu:16.04:LTS |
All versions, plus listed versions 1.0.0-2 |
None known |
ruby-oauth2Ubuntu:18.04:LTS |
All versions, plus listed versions 1.4.0-11.4.0-2 |
None known |
ruby-oauth2Ubuntu:20.04:LTS |
All versions, plus listed versions 1.4.1-2 |
None known |
ruby-oauth2Ubuntu:22.04:LTS |
All versions, plus listed versions 1.4.4-1 |
None known |
ruby-oauth2Ubuntu:24.04:LTS |
All versions, plus listed versions 2.0.7-2 |
None known |
ruby-oauth2Ubuntu:26.10 |
All versions, plus listed versions 2.0.18-12.0.25-1 |
None known |
ruby-oauth2Ubuntu:26.04:LTS |
All versions, plus listed versions 2.0.9-12.0.17-12.0.18-1 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-54603 is a high vulnerability (CVSS 8.6) in Ruby-oauth Oauth2, published July 28, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.45%, the 39th percentile. Fixed in oauth2 2.0.22.
References
github.com/ruby-oauth/oauth2/security/advisories/GHSA-pp92-crg2-gfv9
github.com/ruby-oauth/oauth2/commit/0f0a474f1b38453e119e660c2daca742d4378ce9
github.com/ruby-oauth/oauth2/releases/tag/v2.0.22
lists.debian.org/debian-lts-announce/2026/10/msg00014.html
github.com/advisories/GHSA-pp92-crg2-gfv9
github.com/CVEProject/cvelistV5/tree/main/cves/2026/54xxx/CVE-2026-54603.json
Left out here: 7 versions and the rest of 2 source descriptions. The JSON record has them.