eager-auth 2.2.0__tar.gz → 2.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {eager_auth-2.2.0 → eager_auth-2.2.1}/PKG-INFO +2 -2
  2. {eager_auth-2.2.0 → eager_auth-2.2.1}/pyproject.toml +4 -2
  3. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/tokens.py +3 -1
  4. eager_auth-2.2.1/tests/test_hostile_tokens.py +30 -0
  5. {eager_auth-2.2.0 → eager_auth-2.2.1}/.gitignore +0 -0
  6. {eager_auth-2.2.0 → eager_auth-2.2.1}/LICENSE +0 -0
  7. {eager_auth-2.2.0 → eager_auth-2.2.1}/README.md +0 -0
  8. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/__init__.py +0 -0
  9. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/claims.py +0 -0
  10. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/config.py +0 -0
  11. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/context.py +0 -0
  12. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/discovery.py +0 -0
  13. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/errors.py +0 -0
  14. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/fastapi.py +0 -0
  15. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/flow.py +0 -0
  16. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/keys.py +0 -0
  17. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/pending.py +0 -0
  18. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/presets.py +0 -0
  19. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/principal.py +0 -0
  20. {eager_auth-2.2.0 → eager_auth-2.2.1}/src/eager_auth/session.py +0 -0
  21. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/conftest.py +0 -0
  22. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_callback.py +0 -0
  23. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_config.py +0 -0
  24. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_conformance.py +0 -0
  25. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_fastapi.py +0 -0
  26. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_flow.py +0 -0
  27. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_http_jwks_source.py +0 -0
  28. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_keys.py +0 -0
  29. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_lifecycle.py +0 -0
  30. {eager_auth-2.2.0 → eager_auth-2.2.1}/tests/test_session.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: eager-auth
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: Standard OIDC relying party for Eager FastAPI services — verification core
5
5
  Project-URL: Homepage, https://github.com/Whats-Next-CC/eager-auth
6
6
  Project-URL: Source, https://github.com/Whats-Next-CC/eager-auth
@@ -20,7 +20,7 @@ Classifier: Typing :: Typed
20
20
  Requires-Python: >=3.12
21
21
  Requires-Dist: httpx>=0.27
22
22
  Requires-Dist: joserfc>=1.0
23
- Requires-Dist: pyjwt[crypto]>=2.9
23
+ Requires-Dist: pyjwt[crypto]>=2.15.0
24
24
  Provides-Extra: dev
25
25
  Requires-Dist: black>=24.0; extra == 'dev'
26
26
  Requires-Dist: fastapi>=0.110; extra == 'dev'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "eager-auth"
3
- version = "2.2.0"
3
+ version = "2.2.1"
4
4
  description = "Standard OIDC relying party for Eager FastAPI services — verification core"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -20,7 +20,9 @@ classifiers = [
20
20
  license = "Apache-2.0"
21
21
  license-files = ["LICENSE"]
22
22
  dependencies = [
23
- "pyjwt[crypto]>=2.9",
23
+ # 2.15.0 is the first release with no open advisory: of the thirteen published on
24
+ # 2026-09-29/30, 2.14.0 fixed twelve and GHSA-42vr-xj54-vc7v still affects 2.14.0.
25
+ "pyjwt[crypto]>=2.15.0",
24
26
  "httpx>=0.27",
25
27
  "joserfc>=1.0",
26
28
  ]
@@ -74,7 +74,9 @@ def _header_algorithm(token: str, config: AuthConfig) -> str:
74
74
  """Read `alg` from the header and refuse anything symmetric, before touching keys."""
75
75
  try:
76
76
  header = jwt.get_unverified_header(token)
77
- except jwt.InvalidTokenError as exc:
77
+ # RecursionError: a deeply nested header is attacker input read before any signature
78
+ # check, and PyJWT has let json.loads raise it unwrapped (GHSA-8wjv-2p76-3863).
79
+ except (jwt.InvalidTokenError, RecursionError) as exc:
78
80
  raise MalformedTokenError(f"cannot read token header: {exc}") from exc
79
81
 
80
82
  algorithm = header.get("alg")
@@ -0,0 +1,30 @@
1
+ """Tokens built to crash the parser rather than to forge a principal.
2
+
3
+ The header is read before any signature check, so whatever it contains comes from an
4
+ unauthenticated caller. Every such token must end as a typed InvalidTokenError (a 401),
5
+ never as an exception the framework turns into a 500.
6
+ """
7
+
8
+ import base64
9
+
10
+ import pytest
11
+
12
+ from eager_auth import MalformedTokenError, verify_id_token
13
+
14
+
15
+ def _b64(raw: bytes) -> str:
16
+ return base64.urlsafe_b64encode(raw).rstrip(b"=").decode()
17
+
18
+
19
+ def _token_with_header(header: bytes) -> str:
20
+ return f"{_b64(header)}.{_b64(b'{}')}.{_b64(b'signature')}"
21
+
22
+
23
+ @pytest.mark.parametrize("depth", [10_000, 100_000])
24
+ def test_deeply_nested_header_is_malformed_not_a_crash(depth, config, resolver):
25
+ # GHSA-8wjv-2p76-3863: PyJWT 2.13.0 lets json.loads raise RecursionError here, which
26
+ # is a RuntimeError, not an InvalidTokenError.
27
+ header = b'{"alg":"RS256","x":' + b"[" * depth + b"]" * depth + b"}"
28
+
29
+ with pytest.raises(MalformedTokenError):
30
+ verify_id_token(_token_with_header(header), config=config, keys=resolver)
File without changes
File without changes
File without changes
File without changes