Skip to main content
CVE-2026-118669VulnerabilityPublished

apprise · OAuthLib: Token grants can use attacker-supplied client_id for authorization after authenticating a different client

Affects appriseSource GCVE Database

0
Compare
Authoritative record:CVE-2026-118669· High 8.4

Not known to be exploited · CVSS 8.4 High : upgrade to 2.0.0_1 or 1.7.0 soon.

Data as of 31 Oct 2026, 16:51 UTC

CVSS 4.0 base score
8.4High
How severe the flaw is on the 0–10 CVSS scale.

Thin evidence

One scorer or none
CVSS scores by source
CNA (GitHub_M)CVSS 4.08.4
Patch
Available – fixed in 2.0.0_1, 1.7.0
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:H/SI:H/SA:N

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 28 Oct 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponized: 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 30 Oct 2026.

What to do

Upgrade apprise to 2.0.0_1 or 1.7.0.

Affected software

ProductAffectedFixed in
Homebrewapprisepkg:brew/appriseOther threatsAffected
  • >=1.5.0 <1.7.0
  • >=1.7.2 <2.0.0_1
Fixed in
  • 1.7.0
  • 2.0.0_1

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

Description

## Summary OAuthlib's token grant handlers can authenticate one client while authorizing the grant type and requested scopes using a different, attacker-supplied `client_id` from the request body. In affected token grant flows, `authenticate_client(request)` sets `request.client` to the authenticated client object, but OAuthlib does not require `request.client_id` to match `request.client.client_id`. If the request body already contains `client_id=...`, OAuthlib keeps that value and passes it to `validate_grant_type()` and `validate_scopes()`. As a result, an authenticated low-privilege confidential client can authenticate as itself, submit another client's `client_id` in the token request body, and cause grant and scope validation to run under that other client identity. ## Affected Surface The affected public surface is the OAuth2 token endpoint handling for: - `client_credentials` - `password` / Resource Owner Password Credentials The issue is relevant to OAuthlib provider deployments where: - the attacker has credentials for one valid lower-privilege confidential client; - another client has broader grants or scopes; - the application validator uses the `client_id` argument passed by OAuthlib in `validate_grant_type()` or `validate_scopes()`, as the `RequestValidator` API is designed to support. Deployments whose validators ignore the `client_id` argument and authorize only from `request.client` may not be affected. ## Details `Request` parses query and body parameters into request attributes, including `client_id`: - https://github.com/oauthlib/oauthlib/blob/b3f9edffed6b370e3eb92721cea0da22ec7e18b9/oauthlib/common.py#L360-L397 The `RequestValidator.authenticate_client()` contract requires the integrator to authenticate the client and set `request.client`: - https://github.com/oauthlib/oauthlib/blob/b3f9edffed6b370e3eb92721cea0da22ec7e18b9/oauthlib/oauth2/rfc6749/request_validator.py#L41-L66 For the `client_credentials` grant, OAuthlib authenticates the client, verifies that `request.client` has a `client_id`, then performs grant and scope validation before normalizing `request.client_id` from `request.client.client_id`: - https://github.com/oauthlib/oauthlib/blob/b3f9edffed6b370e3eb92721cea0da22ec7e18b9/oauthlib/oauth2/rfc6749/grant_types/client_credentials.py#L106-L119 The problematic order is: ```python if not self.request_validator.authenticate_client(request): ... elif not hasattr(request.client, 'client_id'): ... self.validate_grant_type(request) request.client_id = request.client_id or request.client.client_id self.validate_scopes(request) ``` Because OAuthlib only fills `request.client_id` when it is missing, an attacker-supplied body value such as `client_id=high` survives even when `request.client.client_id == "low"`. The shared grant helper then authorizes using `request.client_id`, while passing the authenticated client object separately: - `validate_grant_type(request.client_id, request.grant_type, request.client, request)`: https://github.com/oauthlib/oauthlib/blob/b3f9edffed6b370e3eb92721cea0da22ec7e18b9/oauthlib/oauth2/rfc6749/grant_types/base.py#L152-L162 - `validate_scopes(request.client_id, request.scopes, request.client, request)`: https://github.com/oauthlib/oauthlib/blob/b3f9edffed6b370e3eb92721cea0da22ec7e18b9/oauthlib/oauth2/rfc6749/grant_types/base.py#L164-L176 The same identity split exists in the Resource Owner Password Credentials flow. After client authentication and user validation, OAuthlib validates the grant and scopes before replacing a missing `request.client_id` from `request.client.client_id`: - https://github.com/oauthlib/oauthlib/blob/b3f9edffed6b370e3eb92721cea0da22ec7e18b9/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py#L87-L97 - https://github.com/oauthlib/oauthlib/blob/b3f9edffed6b370e3eb92721cea0da22ec7e18b9/oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py#L178-L195 This creates an inconsistent identity state: ```text request.client.client_id == "low" # authenticated client request.client_id == "high" # attacker-supplied request parameter ``` OAuthlib then calls application authorization hooks with both values, but uses the attacker-controlled `request.client_id` as the first-class `client_id` argument. ## Impact An authenticated lower-privilege confidential client can obtain tokens authorized under another client's grant and scope policy if the application's validator follows the `RequestValidator` API and authorizes by the `client_id` argument. Concrete impact can include: - issuing access tokens with scopes not assigned to the authenticated client; - allowing a low-privilege client to use a grant type reserved for a higher-privilege client; - persisting tokens under a spoofed `client_id` while the authenticated client object is different. This is a token authorization bypass, not an unauthenticated attack. The attacker needs valid credentials for one clie

References

Community reviews

Loading reviews…