From 3d589dc35b1caa4ccc93cd2a856bd4e90d9dc08d Mon Sep 17 00:00:00 2001 From: yucheng-berri <293926326+yucheng-berri@users.noreply.github.com> Date: Wed, 14 Oct 2026 02:03:10 -0700 Subject: [PATCH] fix(mcp): default the legacy KDF grace window on, force off under FIPS, cache the legacy derivation legacy_kdf_grace_enabled required LITELLM_MCP_LEGACY_KDF_GRACE=true, so operators who never heard of the variable lost every main-era scrypt token on upgrade day. The window now defaults on and only explicit false-style values (false/0/no/off) disable it; FIPS deployments stay force-off via the existing is_fips_mode check. legacy_scrypt (n=2**15, memory-hard) is now lru_cache'd per process so repeated legacy-format token attempts derive the subkeys once instead of re-running scrypt per attempt. --- .../outbound_credentials/key_derivation.py | 18 ++- .../integration/mcp/test_mcp_kdf_rotation.py | 108 ++++++++++++++---- .../test_bridge_credentials.py | 35 +++++- .../test_session_credentials.py | 47 +++++++- .../mcp_server/test_gateway_dcr_flow.py | 48 +++++++- 5 files changed, 218 insertions(+), 38 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/outbound_credentials/key_derivation.py b/litellm/proxy/_experimental/mcp_server/outbound_credentials/key_derivation.py index 9ea90386bc8..e1a19b84a95 100644 --- a/litellm/proxy/_experimental/mcp_server/outbound_credentials/key_derivation.py +++ b/litellm/proxy/_experimental/mcp_server/outbound_credentials/key_derivation.py @@ -2,13 +2,14 @@ Bridge envelopes and gateway session tokens are keyed by HKDF-SHA256 (RFC 5869) over the high-entropy proxy ``master_key`` with a per-family domain label as ``info``. The legacy -scrypt derivation is kept for one release as an opt-in read fallback so tokens minted +scrypt derivation is kept for one release as a default-on read fallback so tokens minted before the rotation still open during the grace window; it is never used under FIPS. """ import hashlib import os from collections.abc import Callable +from functools import lru_cache from typing import Final from cryptography.hazmat.primitives.hashes import SHA256 @@ -18,6 +19,7 @@ from litellm.proxy.common_utils.fips import is_fips_mode LEGACY_KDF_GRACE_ENV_VAR: Final = "LITELLM_MCP_LEGACY_KDF_GRACE" DERIVED_KEY_BYTES: Final = 32 +_LEGACY_GRACE_DISABLED_VALUES: Final = frozenset({"false", "0", "no", "off"}) # RFC 7914 work factors of the pre-rotation derivation; maxmem is twice the working set. _SCRYPT_N: Final = 2**15 @@ -31,9 +33,12 @@ def hkdf_sha256(master_key: str, info: bytes) -> str: return HKDF(algorithm=SHA256(), length=DERIVED_KEY_BYTES, salt=None, info=info).derive(master_key.encode()).hex() +@lru_cache(maxsize=8) def legacy_scrypt(master_key: str, salt: bytes) -> str | None: """The pre-rotation derivation. ``None`` when hashlib.scrypt is unavailable (some - OpenSSL builds, including FIPS providers, do not expose it).""" + OpenSSL builds, including FIPS providers, do not expose it). Cached per process: the + work factors are memory-hard, so repeated legacy-format token attempts must not + re-derive the same subkeys.""" try: return hashlib.scrypt( master_key.encode(), @@ -49,8 +54,11 @@ def legacy_scrypt(master_key: str, salt: bytes) -> str | None: def legacy_kdf_grace_enabled(environ: Callable[[str], str | None] = os.environ.get) -> bool: - """True only when the operator opted the legacy read fallback in and the deployment is - not in FIPS mode (scrypt is not a FIPS-approved KDF).""" + """True unless the operator explicitly disabled the legacy read fallback and the + deployment is not in FIPS mode (scrypt is not a FIPS-approved KDF). The window + defaults on so main-era tokens survive the upgrade; only ``false``-style values + (``false``/``0``/``no``/``off``) turn it off.""" if is_fips_mode(environ): return False - return (environ(LEGACY_KDF_GRACE_ENV_VAR) or "").strip().lower() == "true" + raw: Final = environ(LEGACY_KDF_GRACE_ENV_VAR) + return raw is None or raw.strip().lower() not in _LEGACY_GRACE_DISABLED_VALUES diff --git a/tests/integration/mcp/test_mcp_kdf_rotation.py b/tests/integration/mcp/test_mcp_kdf_rotation.py index 8609a1ab9a5..2fcddd7d02e 100644 --- a/tests/integration/mcp/test_mcp_kdf_rotation.py +++ b/tests/integration/mcp/test_mcp_kdf_rotation.py @@ -54,6 +54,7 @@ ENVELOPE_SIGNING: Final = b"litellm-mcp-bridge:envelope-signing:" ENVELOPE_ENCRYPTION: Final = b"litellm-mcp-bridge:envelope-encryption:" SESSION_SIGNING: Final = b"litellm-mcp-gateway:session-signing:" GRACE: Final = {"LITELLM_MCP_LEGACY_KDF_GRACE": "true"} +NO_GRACE: Final = {"LITELLM_MCP_LEGACY_KDF_GRACE": "false"} class _RefreshGrantForm(TypedDict): @@ -239,14 +240,39 @@ def test_envelope_minted_under_hkdf_sha256_keys_is_admitted(gateway: Gateway) -> _assert_admitted(_tools_list(gateway.client, f"/{alias}/mcp", envelope), peer, upstream_token) -def test_envelope_minted_under_legacy_scrypt_keys_is_rejected_without_grace(gateway: Gateway) -> None: - with mcp_peer() as peer, oauth_server() as auth, gateway.scenario() as scenario: +def test_envelope_minted_under_legacy_scrypt_keys_is_rejected_when_grace_is_explicitly_disabled( + gateway: Gateway, tmp_path: Path +) -> None: + with ( + owned_proxy(gateway, tmp_path, NO_GRACE) as strict, + mcp_peer() as peer, + oauth_server() as auth, + strict.scenario() as scenario, + ): alias: Final = "kdf" + uuid.uuid4().hex[:8] identity: Final = _register_bridge(scenario, peer, alias, auth.issuer) key: Final = scenario.key(user_id=scenario.user(), object_permission=_ObjectPermission(mcp_servers=(identity,))) peer.drain() - envelope: Final = _envelope(identity, key, _scrypt_envelope_keys(gateway.key), "up-" + uuid.uuid4().hex) - _assert_rejected(_tools_list(gateway.client, f"/{alias}/mcp", envelope), peer) + envelope: Final = _envelope(identity, key, _scrypt_envelope_keys(strict.key), "up-" + uuid.uuid4().hex) + _assert_rejected(_tools_list(strict.client, f"/{alias}/mcp", envelope), peer) + + +def test_envelope_minted_under_legacy_scrypt_keys_is_admitted_by_default_without_the_grace_variable( + gateway: Gateway, tmp_path: Path +) -> None: + with ( + owned_proxy(gateway, tmp_path, {}) as plain, + mcp_peer() as peer, + oauth_server() as auth, + plain.scenario() as scenario, + ): + alias: Final = "kdf" + uuid.uuid4().hex[:8] + identity: Final = _register_bridge(scenario, peer, alias, auth.issuer) + key: Final = scenario.key(user_id=scenario.user(), object_permission=_ObjectPermission(mcp_servers=(identity,))) + upstream_token: Final = "up-" + uuid.uuid4().hex + peer.drain() + envelope: Final = _envelope(identity, key, _scrypt_envelope_keys(plain.key), upstream_token) + _assert_admitted(_tools_list(plain.client, f"/{alias}/mcp", envelope), peer, upstream_token) @pytest.mark.parametrize("kdf", ("hkdf", "scrypt")) @@ -285,11 +311,11 @@ def test_envelope_under_a_foreign_master_key_is_rejected_during_grace(gateway: G _assert_rejected(_tools_list(graced.client, f"/{alias}/mcp", _envelope(identity, key, keys, "up")), peer) -def test_grace_variable_set_to_anything_but_true_keeps_legacy_envelopes_rejected( +def test_grace_variable_set_to_an_explicit_false_keeps_legacy_envelopes_rejected( gateway: Gateway, tmp_path: Path ) -> None: with ( - owned_proxy(gateway, tmp_path, {"LITELLM_MCP_LEGACY_KDF_GRACE": "yes"}) as proxy, + owned_proxy(gateway, tmp_path, {"LITELLM_MCP_LEGACY_KDF_GRACE": "false"}) as proxy, mcp_peer() as peer, oauth_server() as auth, proxy.scenario() as scenario, @@ -310,14 +336,26 @@ def test_session_token_minted_under_hkdf_sha256_key_is_admitted(gateway: Gateway assert "tools" in response.text, response.text -def test_session_token_minted_under_legacy_scrypt_key_is_rejected_without_grace(gateway: Gateway) -> None: - with gateway.scenario() as scenario: - token: Final = _session(scenario.user(), _scrypt_session_keys(gateway.key)) - response: Final = _tools_list(gateway.client, "/mcp", token) +def test_session_token_minted_under_legacy_scrypt_key_is_rejected_when_grace_is_explicitly_disabled( + gateway: Gateway, tmp_path: Path +) -> None: + with owned_proxy(gateway, tmp_path, NO_GRACE) as strict, strict.scenario() as scenario: + token: Final = _session(scenario.user(), _scrypt_session_keys(strict.key)) + response: Final = _tools_list(strict.client, "/mcp", token) assert response.status_code == 401, response.text assert "invalid_token" in response.headers.get("www-authenticate", ""), response.headers +def test_session_token_minted_under_legacy_scrypt_key_is_admitted_by_default_without_the_grace_variable( + gateway: Gateway, tmp_path: Path +) -> None: + with owned_proxy(gateway, tmp_path, {}) as plain, plain.scenario() as scenario: + token: Final = _session(scenario.user(), _scrypt_session_keys(plain.key)) + response: Final = _tools_list(plain.client, "/mcp", token) + assert response.status_code == 200, response.text + assert "tools" in response.text, response.text + + @pytest.mark.parametrize("kdf", ("hkdf", "scrypt")) def test_session_token_of_either_kdf_is_admitted_during_the_legacy_grace_window( gateway: Gateway, tmp_path: Path, kdf: str @@ -358,15 +396,21 @@ def test_bridge_refresh_envelope_minted_under_hkdf_sha256_keys_is_renewed(gatewa assert isinstance(opened, OpenedRefreshEnvelope), opened -def test_bridge_refresh_envelope_minted_under_legacy_scrypt_keys_is_rejected_without_grace( +def test_bridge_refresh_envelope_minted_under_legacy_scrypt_keys_is_rejected_when_grace_is_explicitly_disabled( gateway: Gateway, + tmp_path: Path, ) -> None: - with mcp_peer() as peer, oauth_server() as auth, gateway.scenario() as scenario: + with ( + owned_proxy(gateway, tmp_path, NO_GRACE) as strict, + mcp_peer() as peer, + oauth_server() as auth, + strict.scenario() as scenario, + ): alias: Final = "kdf" + uuid.uuid4().hex[:8] identity: Final = _register_bridge(scenario, peer, alias, auth.issuer) key: Final = scenario.key(user_id=scenario.user(), object_permission=_ObjectPermission(mcp_servers=(identity,))) - envelope: Final = _refresh_envelope(identity, key, _scrypt_envelope_keys(gateway.key), _upstream_refresh(auth)) - response: Final = gateway.client.post( + envelope: Final = _refresh_envelope(identity, key, _scrypt_envelope_keys(strict.key), _upstream_refresh(auth)) + response: Final = strict.client.post( f"/{alias}/token", headers=(("x-litellm-api-key", key),), data=_RefreshGrantForm(grant_type="refresh_token", refresh_token=envelope, client_id="kdf-client"), @@ -430,11 +474,13 @@ def test_session_refresh_token_minted_under_hkdf_sha256_key_is_renewed(gateway: assert isinstance(opened, OpenedSessionToken), opened -def test_session_refresh_token_minted_under_legacy_scrypt_key_is_rejected_without_grace(gateway: Gateway) -> None: - with gateway.scenario() as scenario: - client_id: Final = _register_gateway_client(gateway.client) - refresh: Final = _session_refresh(scenario.user(), client_id, _scrypt_session_keys(gateway.key)) - response: Final = gateway.client.post( +def test_session_refresh_token_minted_under_legacy_scrypt_key_is_rejected_when_grace_is_explicitly_disabled( + gateway: Gateway, tmp_path: Path +) -> None: + with owned_proxy(gateway, tmp_path, NO_GRACE) as strict, strict.scenario() as scenario: + client_id: Final = _register_gateway_client(strict.client) + refresh: Final = _session_refresh(scenario.user(), client_id, _scrypt_session_keys(strict.key)) + response: Final = strict.client.post( "/token", data=_RefreshGrantForm(grant_type="refresh_token", refresh_token=refresh, client_id=client_id) ) assert response.status_code == 400, response.text @@ -536,7 +582,21 @@ def test_claude_code_gateway_refresh_of_an_hkdf_session_token_is_renewed_without assert isinstance(opened, OpenedSessionToken), opened -def test_claude_code_gateway_refresh_of_a_legacy_scrypt_session_token_is_rejected_without_grace( +def test_claude_code_gateway_refresh_of_a_legacy_scrypt_session_token_is_rejected_when_grace_is_explicitly_disabled( + gateway: Gateway, tmp_path: Path +) -> None: + config: Final = _claude_code_gateway_config(tmp_path) + with owned_proxy(gateway, tmp_path, NO_GRACE, config=config) as graced, graced.scenario() as scenario: + refresh: Final = _proxy_api_session_refresh(scenario.user(), _scrypt_session_keys(graced.key)) + response: Final = graced.client.post( + "/claude_code_gateway/oauth/token", + data=_ClaudeCodeTokenForm(grant_type="refresh_token", refresh_token=refresh), + ) + assert response.status_code == 400, response.text + assert response.json()["error"] == "invalid_grant", response.text + + +def test_claude_code_gateway_refresh_of_a_legacy_scrypt_session_token_is_renewed_by_default_without_the_grace_variable( gateway: Gateway, tmp_path: Path ) -> None: config: Final = _claude_code_gateway_config(tmp_path) @@ -546,8 +606,12 @@ def test_claude_code_gateway_refresh_of_a_legacy_scrypt_session_token_is_rejecte "/claude_code_gateway/oauth/token", data=_ClaudeCodeTokenForm(grant_type="refresh_token", refresh_token=refresh), ) - assert response.status_code == 400, response.text - assert response.json()["error"] == "invalid_grant", response.text + assert response.status_code == 200, response.text + renewed: Final = response.json() + opened: Final = open_session_refresh_token( + renewed["refresh_token"], _hkdf_session_keys(graced.key), datetime.now(timezone.utc) + ) + assert isinstance(opened, OpenedSessionToken), opened def test_claude_code_gateway_refresh_of_a_legacy_scrypt_session_token_is_renewed_during_the_legacy_grace_window( diff --git a/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py b/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py index 63d73730950..683d1987af1 100644 --- a/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py +++ b/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_bridge_credentials.py @@ -411,13 +411,29 @@ def test_legacy_envelope_opens_during_the_grace_window(): assert result.identity == _IDENTITY -def test_legacy_envelope_is_invalid_without_grace(): +def test_legacy_envelope_is_invalid_when_grace_is_explicitly_disabled(): token: Final = _sealed_token(_legacy_keys(_MASTER_KEY)) - assert legacy_envelope_keys_from_master_key(_MASTER_KEY, environ=_environ(MappingProxyType({}))) is None + assert ( + legacy_envelope_keys_from_master_key( + _MASTER_KEY, environ=_environ(MappingProxyType({LEGACY_KDF_GRACE_ENV_VAR: "false"})) + ) + is None + ) result: Final = resolve_bridge_envelope(token, envelope_keys_from_master_key(_MASTER_KEY), _NOW, _SERVER_ID) assert isinstance(result, BridgeEnvelopeInvalid) +def test_legacy_envelope_opens_by_default_without_the_grace_variable(): + legacy: Final = _legacy_keys(_MASTER_KEY) + token: Final = _sealed_token(legacy) + grace: Final = legacy_envelope_keys_from_master_key(_MASTER_KEY, environ=_environ(MappingProxyType({}))) + assert grace == legacy + result: Final = resolve_bridge_envelope( + token, envelope_keys_from_master_key(_MASTER_KEY), _NOW, _SERVER_ID, legacy_keys=grace + ) + assert isinstance(result, BridgeEnvelopeAdmitted) + + def test_grace_is_disabled_under_fips(): grace: Final = legacy_envelope_keys_from_master_key( _MASTER_KEY, @@ -426,13 +442,20 @@ def test_grace_is_disabled_under_fips(): assert grace is None -@pytest.mark.parametrize("value", ("yes", "1", "on", "TRUE ", "True")) -def test_grace_is_enabled_only_by_case_insensitive_true(value: str): +@pytest.mark.parametrize("value", ("false", "0", "no", "off", "FALSE ", " Off")) +def test_grace_is_disabled_only_by_explicit_false_style_values(value: str): result: Final = legacy_envelope_keys_from_master_key( _MASTER_KEY, environ=_environ(MappingProxyType({LEGACY_KDF_GRACE_ENV_VAR: value})) ) - expected: Final = value.strip().lower() == "true" - assert (result is not None) is expected + assert result is None + + +@pytest.mark.parametrize("value", ("true", "1", "yes", "on", "TRUE ", "True", "", "banana")) +def test_grace_stays_on_for_unset_true_style_and_unrecognized_values(value: str): + result: Final = legacy_envelope_keys_from_master_key( + _MASTER_KEY, environ=_environ(MappingProxyType({LEGACY_KDF_GRACE_ENV_VAR: value})) + ) + assert result is not None def test_legacy_refresh_envelope_opens_during_grace(): diff --git a/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_session_credentials.py b/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_session_credentials.py index 88ee58a00a5..533fc6c6b91 100644 --- a/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_session_credentials.py +++ b/tests/unit/proxy/_experimental/mcp_server/outbound_credentials/test_session_credentials.py @@ -17,6 +17,7 @@ from litellm.proxy._experimental.mcp_server.outbound_credentials.bridge_credenti ) from litellm.proxy._experimental.mcp_server.outbound_credentials.key_derivation import ( LEGACY_KDF_GRACE_ENV_VAR, + legacy_scrypt, ) from litellm.proxy._experimental.mcp_server.outbound_credentials.session_credentials import ( NotSessionBearer, @@ -276,10 +277,15 @@ def test_legacy_session_token_opens_during_the_grace_window(): assert result.principal == PRINCIPAL -def test_legacy_session_token_is_invalid_without_grace(): +def test_legacy_session_token_is_invalid_when_grace_is_explicitly_disabled(): minted: Final = mint_session_token(PRINCIPAL, _legacy_session_keys(MASTER_KEY), NOW) assert isinstance(minted, MintedSessionToken) - assert legacy_session_keys_from_master_key(MASTER_KEY, KEYS, environ=_environ(MappingProxyType({}))) is None + assert ( + legacy_session_keys_from_master_key( + MASTER_KEY, KEYS, environ=_environ(MappingProxyType({LEGACY_KDF_GRACE_ENV_VAR: "false"})) + ) + is None + ) result: Final = resolve_session_bearer(f"Bearer {minted.token.get_secret_value()}", KEYS, NOW) assert isinstance(result, SessionBearerInvalid) assert result.expired is False @@ -302,6 +308,43 @@ def test_grace_is_disabled_under_fips(): assert grace is None +def test_grace_stays_off_under_fips_even_with_the_default_on(): + """FIPS deployments get no scrypt fallback even though the grace window now defaults on.""" + grace: Final = legacy_session_keys_from_master_key( + MASTER_KEY, + KEYS, + environ=_environ(MappingProxyType({FIPS_MODE_ENV_VAR: "true"})), + ) + assert grace is None + + +def test_legacy_grace_is_on_by_default_without_the_variable(): + legacy: Final = _legacy_session_keys(MASTER_KEY) + grace: Final = legacy_session_keys_from_master_key(MASTER_KEY, KEYS, environ=_environ(MappingProxyType({}))) + assert grace == legacy + + +def test_legacy_scrypt_derivation_is_cached_per_process(monkeypatch): + """Repeated legacy-format token attempts must not re-run the memory-hard scrypt KDF.""" + calls: Final[list[int]] = [] + real_scrypt = hashlib.scrypt + + def counting_scrypt(password, **kwargs): + calls.append(1) + return real_scrypt(password, **kwargs) + + monkeypatch.setattr(hashlib, "scrypt", counting_scrypt) + legacy_scrypt.cache_clear() + try: + first: Final = legacy_scrypt(MASTER_KEY, b"litellm-mcp-gateway:session-signing:") + second: Final = legacy_scrypt(MASTER_KEY, b"litellm-mcp-gateway:session-signing:") + finally: + legacy_scrypt.cache_clear() + assert first is not None + assert first == second + assert len(calls) == 1 + + def test_legacy_session_refresh_token_opens_during_grace(): legacy: Final = _legacy_session_keys(MASTER_KEY) minted: Final = mint_session_refresh_token(PRINCIPAL, legacy, NOW) diff --git a/tests/unit/proxy/_experimental/mcp_server/test_gateway_dcr_flow.py b/tests/unit/proxy/_experimental/mcp_server/test_gateway_dcr_flow.py index 123b0a0d73c..4240ee0c202 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_gateway_dcr_flow.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_gateway_dcr_flow.py @@ -2632,10 +2632,43 @@ async def test_claude_code_refresh_of_a_legacy_scrypt_token_is_renewed_during_th @pytest.mark.asyncio -async def test_claude_code_refresh_of_a_legacy_scrypt_token_is_rejected_without_grace(monkeypatch): - """Without the grace variable the same scrypt-minted token answers invalid_grant and - the minter never runs.""" +async def test_claude_code_refresh_of_a_legacy_scrypt_token_is_renewed_by_default_without_the_grace_variable( + monkeypatch, +): + """The grace window defaults on: with no ``LITELLM_MCP_LEGACY_KDF_GRACE`` in the + environment a scrypt-minted refresh token still renews, so operators do not lose + main-era tokens on upgrade day because they never heard of the variable.""" monkeypatch.delenv("LITELLM_MCP_LEGACY_KDF_GRACE", raising=False) + monkeypatch.delenv("LITELLM_FIPS_MODE", raising=False) + minter: Final = _Minter() + renewed: Final = await refresh_proxy_credential( + refresh_token=_legacy_session_refresh(), + client_id="claude_code", + master_key=MASTER_KEY, + cache=DualCache(), + mint_proxy_credential=minter, + ) + assert renewed.status_code == 200, renewed.body + + +def _hkdf_session_refresh(user_id="u1", client_id="claude_code", team_id="team-b"): + minted: Final = mint_session_refresh_token( + SessionPrincipal(user_id=user_id, client_id=client_id, audience="proxy_api", team_id=team_id), + session_keys_from_master_key(MASTER_KEY), + datetime.now(timezone.utc), + ) + assert isinstance(minted, MintedSessionToken) + return minted.token.get_secret_value() + + +@pytest.mark.asyncio +async def test_claude_code_refresh_rejects_a_legacy_token_but_renews_an_hkdf_one_when_grace_is_explicitly_disabled( + monkeypatch, +): + """``LITELLM_MCP_LEGACY_KDF_GRACE=false`` is the operator's opt-out: the scrypt token + answers invalid_grant without running the minter, while an HKDF control renews fine.""" + monkeypatch.setenv("LITELLM_MCP_LEGACY_KDF_GRACE", "false") + monkeypatch.delenv("LITELLM_FIPS_MODE", raising=False) minter: Final = _Minter() refused: Final = await refresh_proxy_credential( refresh_token=_legacy_session_refresh(), @@ -2647,3 +2680,12 @@ async def test_claude_code_refresh_of_a_legacy_scrypt_token_is_rejected_without_ assert refused.status_code == 400 assert json.loads(refused.body)["error"] == "invalid_grant" assert minter.calls == [] + control: Final = await refresh_proxy_credential( + refresh_token=_hkdf_session_refresh(), + client_id="claude_code", + master_key=MASTER_KEY, + cache=DualCache(), + mint_proxy_credential=minter, + ) + assert control.status_code == 200, control.body + assert json.loads(control.body)["access_token"] == "sk-cli-u1"