Oct 31, 2026·8 min read·2 visits
An Insecure Direct Object Reference (IDOR) flaw in Authlib's RFC 7592 ClientConfigurationEndpoint allows any client with a valid registration access token to read, modify, or delete other clients' OAuth 2.0 configurations, including client secrets and redirect URIs. Upgrading to Authlib 1.9.1 enforces mandatory token-client binding within the endpoint processing pipeline.
Authlib versions 0.15 through 1.9.0 contain an authorization bypass vulnerability (CWE-639) in the RFC 7592 ClientConfigurationEndpoint implementation and its corresponding reference documentation. The framework fails to verify that the presented registration access token belongs to the target client specified in the request, and the official reference implementations for authenticate_client and check_permission omit token-to-client binding checks. Consequently, an authenticated client holding any valid registration access token can read, modify, or delete the registration metadata of arbitrary clients on the authorization server.
Authlib is a Python library used to build OAuth 1.0, OAuth 2.0, and OpenID Connect (OIDC) clients and authorization servers. Among the specifications supported by Authlib is RFC 7592 (OAuth 2.0 Dynamic Client Registration Management Protocol), which defines endpoints and mechanisms for dynamically registered OAuth clients to inspect, update, or deprovision their registration metadata after initial creation via RFC 7591.
In Authlib versions >= 0.15 and < 1.9.1, the ClientConfigurationEndpoint class (located in authlib/oauth2/rfc7592/endpoint.py) and its accompanying developer documentation contain an authorization bypass vulnerability tracked as CVE-2026-116672 (GHSA-c73q-w75p-j82v). The vulnerability is classified under CWE-639 (Authorization Bypass Through User-Controlled Key) and CWE-1188 (Insecure Default Initialization of Resource).
When handling management requests (GET, PUT, DELETE), ClientConfigurationEndpoint authenticates the bearer registration access token and resolves the target client record using user-controlled input. However, the base endpoint pipeline did not enforce a check verifying that the authenticated token was actually issued to the resolved target client. Furthermore, Authlib's official reference implementations for the abstract methods authenticate_client and check_permission omitted this verification entirely.
Deployments that implement RFC 7592 client configuration management using Authlib's base pipeline and documented guidance allow cross-client access. Any adversary capable of obtaining a valid registration access token—such as through open dynamic client registration—can read sensitive configuration data (including client_secret), overwrite redirect_uris to hijack authorization flows, or delete existing client registrations.
Under RFC 7592 Section 2, when an OAuth 2.0 client registers dynamically with an authorization server, the server issues a client-specific registration_access_token alongside a registration_client_uri. When the client later queries or modifies its registration at this URI, the authorization server must validate the bearer token and confirm that the token is explicitly bound to the target client_id being accessed.
In Authlib prior to version 1.9.1, ClientConfigurationEndpoint.create_configuration_response orchestrated request validation through three sequential delegate methods: authenticate_token(request), authenticate_client(request), and check_permission(client, request). While authenticate_token validated that the bearer token existed and was active (storing it in request.credential), the framework never invoked token.check_client(client) to bind the credential to the resolved client entity.
This framework omission was compounded by flawed reference implementations in both the class docstrings (authlib/oauth2/rfc7592/endpoint.py) and the Sphinx documentation (docs/oauth2/specs/rfc7592.rst). The documented implementation of authenticate_client extracted client_id directly from user-supplied request data (request.payload.data.get("client_id")) and queried the database without consulting request.credential.
Subsequently, the documented implementation of check_permission(self, client, request) only evaluated whether the target client object had a static property set (specifically return client.editable). Because dynamic clients typically have editable set to True by default, the authorization check succeeded regardless of which client owned request.credential.
Prior to commit 1bd2a313492ce0a4d0571c150730907aa3fc91ff, the request processing logic in authlib/oauth2/rfc7592/endpoint.py delegated all authorization decisions to subclass hooks without enforcing mandatory RFC 7592 token-client binding. Below is the vulnerable implementation in create_configuration_response and the accompanying docstring guidance:
# Vulnerable implementation in authlib/oauth2/rfc7592/endpoint.py (< 1.9.1)
def create_configuration_response(self, request):
token = self.authenticate_token(request)
if not token:
raise InvalidTokenError()
request.credential = token
client = self.authenticate_client(request)
if not client:
raise InvalidClientError(
status_code=401, description="The client does not exist on this server."
)
# VULNERABILITY: No check verifying that 'token' belongs to 'client'.
# Execution proceeds directly to check_permission.
if not self.check_permission(client, request):
raise AccessDeniedError()
# Proceeds to endpoint_request_Read / Update / Delete...In the same file and in docs/oauth2/specs/rfc7592.rst, developers were instructed to implement the abstract hooks as follows:
# Vulnerable reference implementation in documentation (< 1.9.1)
def authenticate_client(self, request):
# Reads client_id from untrusted request payload
client_id = request.payload.data.get("client_id")
return Client.get(client_id=client_id)
def check_permission(self, client, request):
# Evaluates only target client attribute; ignores request.credential
return client.editableIn version 1.9.1 (commit 1bd2a313492ce0a4d0571c150730907aa3fc91ff), the maintainers remediated the vulnerability by enforcing token.check_client(client) directly within create_configuration_response and updating the documentation to extract client_id from the request URI path:
--- a/authlib/oauth2/rfc7592/endpoint.py
+++ b/authlib/oauth2/rfc7592/endpoint.py
@@ -39,6 +39,11 @@ def create_configuration_response(self, request):
status_code=401, description="The client does not exist on this server."
)
+ # The registration access token is only valid for the client it was
+ # issued to.
+ if not token.check_client(client):
+ raise AccessDeniedError()
+
if not self.check_permission(client, request):
raise AccessDeniedError()The updated documentation and docstrings now specify that the token object returned by authenticate_token must implement authlib.oauth2.rfc6749.TokenMixin.check_client, and demonstrate parsing client_id from the endpoint URL path using urllib.parse.urlparse:
# Updated reference implementation in 1.9.1
def authenticate_client(self, request):
path = urlparse(request.uri).path
client_id = path.rstrip("/").rsplit("/", 1)[-1]
return Client.get(client_id=client_id)> [!NOTE]
> Assessment of Fix Completeness: Enforcing token.check_client(client) inside create_configuration_response secures the framework pipeline by default, provided that the application's token model properly implements TokenMixin.check_client(client). However, if an application uses a custom token object that does not inherit from TokenMixin or implements a permissive check_client method, the check will either raise an AttributeError or fail open. Additionally, applications that copied the pre-1.9.1 authenticate_client pattern reading request.payload.data.get("client_id") may break on GET and DELETE requests where no JSON body is present unless updated to read from the URL path or route parameters.
Exploiting CVE-2026-116672 requires two prerequisites: the target authorization server must expose an RFC 7592 client configuration endpoint implemented via Authlib without custom token-to-client binding logic, and the attacker must possess a valid registration access token alongside a known or enumerable victim client_id. In standard OAuth 2.0 deployments, client_id values are public identifiers exposed in authorization redirect URLs, and attackers can obtain a valid registration access token by registering their own client via RFC 7591.
Once in possession of a valid token (attacker_token) and a target client identifier (other-client-id), the attacker can execute three distinct operations against the configuration endpoint:
GET): The attacker sends a GET request to the victim client's configuration URI using attacker_token in the Authorization header. The server resolves other-client-id, verifies that other_client.editable is true, and returns the full registration payload, including the plaintext client_secret.GET /configure_client/other-client-id HTTP/1.1
Host: auth.example.com
Authorization: Bearer <attacker_registration_access_token>PUT): The attacker sends a PUT request targeting other-client-id with modified metadata, such as adding an attacker-controlled URI to redirect_uris. Subsequent authorization flows initiated for other-client-id can then be redirected to the attacker's endpoint, allowing interception of authorization codes or access tokens.PUT /configure_client/other-client-id HTTP/1.1
Host: auth.example.com
Authorization: Bearer <attacker_registration_access_token>
Content-Type: application/json
{
"client_id": "other-client-id",
"client_name": "Hijacked",
"redirect_uris": ["https://attacker.example/callback"]
}DELETE): The attacker sends a DELETE request targeting other-client-id. The server deletes the victim client's registration record from the database, immediately invalidating all future authentication and token requests for that client.DELETE /configure_client/other-client-id HTTP/1.1
Host: auth.example.com
Authorization: Bearer <attacker_registration_access_token>The official fix commit 1bd2a313492ce0a4d0571c150730907aa3fc91ff includes regression test cases in tests/flask/test_oauth2/test_client_configuration_endpoint.py (test_read_token_of_another_client, test_update_token_of_another_client, and test_delete_token_of_another_client) that demonstrate these exact request patterns and verify that patched servers return HTTP 400 with {"error": "access_denied"}.
CVE-2026-116672 carries a CVSS v3.1 base score of 8.8 (High) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. The Attack Vector is Network (AV:N) and Attack Complexity is Low (AC:L), as exploitation relies on standard HTTP requests to the OAuth configuration API without special timing requirements. Privileges Required is Low (PR:L) because the attacker must hold a valid registration access token, and User Interaction is None (UI:N).
The Confidentiality Impact is High (C:H). According to RFC 7592 Section 3, a successful read response from the client configuration endpoint returns the full client metadata object, which includes client_secret for confidential clients and registration_access_token if rotated. Exposing client_secret allows the attacker to impersonate the confidential client at the token introspection, revocation, and token issuance endpoints.
The Integrity Impact is High (I:H). By overwriting client metadata parameters such as redirect_uris, jwks_uri, or jwks, an attacker can redirect authorization responses containing authorization codes or implicit tokens to an external server, or substitute the public keys used to validate private_key_jwt client assertions.
The Availability Impact is High (A:H). Unauthenticated or cross-client DELETE operations permanently remove the target client registration, resulting in a denial of service for all legitimate users and relying party services dependent on that OAuth client. As of late October 2026, the EPSS score for CVE-2026-116672 is 0.00274 (19.83rd percentile), and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog.
To remediate CVE-2026-116672, upgrade the Authlib package to version 1.9.1 or later. Version 1.9.1 enforces token.check_client(client) inside ClientConfigurationEndpoint.create_configuration_response prior to calling check_permission.
pip install --upgrade "authlib>=1.9.1"After upgrading, developers must verify that the token model returned by ClientConfigurationEndpoint.authenticate_token(request) implements the check_client(client) method (standard when inheriting from authlib.oauth2.rfc6749.TokenMixin). The method must strictly compare the client identifier associated with the token against the target client's identifier:
def check_client(self, client):
return self.client_id == client.get_client_id()In addition, audit existing ClientConfigurationEndpoint subclasses to ensure that authenticate_client(self, request) resolves the target client identifier consistently from the endpoint route or URI path rather than relying solely on mutable request body parameters.
If an immediate upgrade to Authlib 1.9.1 is not feasible, developers can mitigate the vulnerability in versions 0.15 through 1.9.0 by explicitly enforcing token-to-client binding within their subclass implementation of check_permission:
def check_permission(self, client, request):
token = request.credential
if not token or not token.check_client(client):
return False
return client.editableCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Authlib authlib | >= 0.15, < 1.9.1 | 1.9.1 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Insecure Direct Object Reference (IDOR) / Authorization Bypass |
| CWE ID | CWE-639 / CWE-1188 |
| CVSS v3.1 Score | 8.8 High (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H) |
| Affected Component | authlib.oauth2.rfc7592.ClientConfigurationEndpoint |
| Affected Versions | >= 0.15, < 1.9.1 |
| Patched Version | 1.9.1 |
| EPSS Score | 0.27% (19.83rd percentile) |
| CISA KEV Status | Not Listed |
The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.
CVE-2026-118773 (GHSA-3g3m-2fjr-6pw4) is an uncontrolled resource consumption vulnerability in the node-glob npm package affecting versions 9.0.0 through 14.0.1. Due to missing return statements in the AbortSignal check routines within the directory traversal walker, aborted operations reject the caller promise as expected but continue executing background filesystem reads and pattern matching. Applications that rely on AbortSignal or AbortSignal.timeout() to enforce execution budgets on user-influenced glob operations remain susceptible to CPU, event loop, and disk I/O exhaustion.
CVE-2026-118764 (also tracked as GHSA-g3hv-75wg-m249) is an asymmetric resource consumption vulnerability (CWE-405) in the npm package minimatch prior to version 11.0.1. When pattern compilation is invoked with an optimizationLevel of 2 or higher, the internal preprocessing functions firstPhasePreProcess() and secondPhasePreProcess() in src/index.ts perform unbounded pattern splitting and quadratic pairwise deduplication. An attacker supplying a crafted glob string containing repeated **/.. segments can induce a combinatorial explosion of pattern permutations followed by an O(M^2) comparison routine, blocking the Node.js event loop and exhausting process memory.
A regular expression denial of service (ReDoS) vulnerability exists in @hapi/content from version 3.0.6 prior to 6.0.3. The Content.disposition() function utilizes an unanchored parameter-name regular expression inside a global replacement scan. When processing a Content-Disposition header parameter containing a long token lacking an equals sign, the JavaScript regular expression engine undergoes quadratic O(N^2) backtracking. Because @hapi/content is invoked by @hapi/pez during multipart/form-data request parsing, unauthenticated remote attackers can send a crafted multipart request to any Hapi route with multipart payload parsing enabled, starving the single-threaded Node.js event loop.
A Regular Expression Denial of Service (ReDoS) vulnerability exists in the joi schema description and data validation library for JavaScript. When validating multiple email addresses using the Joi.string().email({ multiple: true }) configuration, the library compiles a separator regular expression with an unanchored greedy leading whitespace quantifier. Supplying an input string containing a long sequence of whitespace characters without the expected delimiter triggers quadratic time complexity during String.prototype.split execution, blocking the single-threaded Node.js event loop.
CVE-2026-119205 (GHSA-mfr5-77xh-9j7h) is a Denial of Service (DoS) vulnerability in pypdf prior to version 6.21.4. The flaw occurs during the cloning or duplication of nested PDF dictionary structures. Because the cycle-detection algorithm within DictionaryObject._clone() tracked generated destination object references rather than the original source indirect references, cyclic reference graphs in source PDFs evaded detection, resulting in an infinite loop and CPU exhaustion.
CVE-2026-119204 (also tracked as GHSA-cggf-277g-3q54) is a Denial of Service (DoS) vulnerability in pypdf prior to version 6.21.4. The vulnerability stems from uncontrolled resource consumption (CWE-400) during the flattening of a PDF document's page tree. By supplying a compact PDF containing a hierarchical amplification structure of shared `/Pages` and `/Page` nodes with dense dictionary entries or inherited resources, an unauthenticated attacker can cause pypdf to copy millions of dictionary entries into individual `PageObject` instances without enforcing a document-wide entry budget. This triggers severe heap memory exhaustion and process termination via Out-Of-Memory (OOM) kills.