Raise error when http_client and ssrf_safe=True are both provided

🤖 Generated with Claude Code

https://claude.ai/code/session_012QKWmKd21vypDmxWbwuE4e
This commit is contained in:
Claude 2026-02-25 20:34:29 +00:00 committed by Jeremiah Lowin
commit 730175910c
3 changed files with 21 additions and 14 deletions

View file

@ -364,6 +364,10 @@ verifier = JWTVerifier(
)
```
<Warning>
`JWTVerifier` does not support `http_client` when `ssrf_safe=True`. SSRF-safe mode requires a hardened transport that validates DNS resolution and connection targets, which cannot be guaranteed with a user-provided client. Attempting to use both will raise a `ValueError`.
</Warning>
<Note>
When you provide an `http_client`, you are responsible for its lifecycle. The verifier will not close it. Use the server's `lifespan` to manage client cleanup:

View file

@ -189,10 +189,11 @@ class JWTVerifier(TokenVerifier):
http_client: Optional httpx.AsyncClient for connection pooling. When provided,
the client is reused for JWKS fetches and the caller is responsible for
its lifecycle. When None (default), a fresh client is created per fetch.
Only used when ssrf_safe is False; SSRF-safe fetches use their own transport.
Cannot be used with ssrf_safe=True.
Raises:
ValueError: If neither or both of `public_key` and `jwks_uri` are provided, or if `algorithm` is unsupported.
ValueError: If neither or both of `public_key` and `jwks_uri` are provided,
if `algorithm` is unsupported, or if `http_client` is provided with `ssrf_safe=True`.
"""
if not public_key and not jwks_uri:
raise ValueError("Either public_key or jwks_uri must be provided")
@ -200,6 +201,12 @@ class JWTVerifier(TokenVerifier):
if public_key and jwks_uri:
raise ValueError("Provide either public_key or jwks_uri, not both")
if ssrf_safe and http_client is not None:
raise ValueError(
"http_client cannot be used with ssrf_safe=True; "
"SSRF-safe mode requires its own hardened transport"
)
algorithm = algorithm or "RS256"
if algorithm not in {
"HS256",

View file

@ -180,21 +180,17 @@ class TestJWTVerifierHttpClient:
assert result is not None
assert not shared_client.is_closed
async def test_ssrf_safe_ignores_http_client(
def test_ssrf_safe_rejects_http_client(
self,
shared_client: httpx.AsyncClient,
):
"""When ssrf_safe=True, the custom http_client should NOT be used."""
verifier = JWTVerifier(
jwks_uri="https://auth.example.com/.well-known/jwks.json",
ssrf_safe=True,
http_client=shared_client,
)
# ssrf_safe uses ssrf_safe_fetch instead of httpx.AsyncClient
# The http_client is stored but not used in this code path
assert verifier._http_client is shared_client
assert verifier.ssrf_safe is True
"""ssrf_safe=True and http_client cannot be used together."""
with pytest.raises(ValueError, match="cannot be used with ssrf_safe=True"):
JWTVerifier(
jwks_uri="https://auth.example.com/.well-known/jwks.json",
ssrf_safe=True,
http_client=shared_client,
)
class TestGitHubHttpClient: