Skip to content

Allow explicit empty-string key IDs in PyJWKClient - #1236

Open
FanWu-ai wants to merge 1 commit into
jpadilla:masterfrom
FanWu-ai:fix-jwks-explicit-empty-id-20261019
Open

FanWu-ai wants to merge 1 commit into
jpadilla:masterfrom
FanWu-ai:fix-jwks-explicit-empty-id-20261019

Conversation

@FanWu-ai

Copy link
Copy Markdown

Summary

PyJWKClient drops JWKs whose kid is explicitly an empty string, even when the token header contains the same value. This prevents it from selecting a key that PyJWKSet[""] can already retrieve and that PyJWT can already use to sign and verify tokens.

RFC 7517 §4.5 defines kid as a case-sensitive string with an unspecified structure; it does not require a nonempty value. Keep explicitly empty string IDs in the signing-key filter while retaining its existing handling of missing IDs, null IDs, and other false-valued inputs. The key-use filter and exact key-ID matching are unchanged.

The 14 regression cases cover signed-token lookup across all four cache settings, both allowed key-use values, coexistence with a nonempty key ID, excluded malformed/missing IDs, encryption keys, and a missing token ID. Six cases fail on the original source; the eight negative controls pass. A changelog entry is included.

Related work

Closed, unmerged draft #1073 and issue #1072 discussed the broader case of absent key IDs and choosing a sole key when a token omits kid. This change deliberately does not add that fallback: a token without kid still cannot select a key whose ID is "".

Validation

Based on master 16231abfd1d8eab4953e8779b3017b859c88565e, on Linux:

  • Pristine full suites on Python 3.9.25 and 3.12.14: 472 passed / 4 expected skips with cryptography; 286 passed / 190 expected skips without it.
  • Final full suites on both versions: 486 passed / 4 expected skips with cryptography; 300 passed / 190 expected skips without it. All new cases run in both modes.
  • Strict mypy in crypto and no-crypto environments on both versions: 25 files clean.
  • All 13 configured pre-commit hooks passed in the configured Python 3.10 environment.
  • Python 3.11 strict Sphinx HTML/doctest builds and README/usage doctests passed.
  • Configured package-description and coverage-report targets passed. Isolated source/wheel builds and strict Twine checks passed.
  • An independently installed wheel passed signed-token checks, invalid-signature and algorithm-policy negatives. Its source bytes match the tested source; the source distribution also includes the exact tests and changelog.
  • git diff --check passed.

The lint environment needed writable cache-directory environment variables passed through tox; no project configuration or check was changed. The remaining interpreter/OS matrix has not run locally, and no hosted CI pass is claimed.

AI assistance was used to prepare, test, and review this change; no human review is claimed.

@FanWu-ai
FanWu-ai marked this pull request as ready for review October 19, 2026 08:24
@auvipy
auvipy self-requested a review October 19, 2026 08:53

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant