refactor(auth): Disable Nous legacy session key fallback

This commit is contained in:
Robin Fernandes
2026-05-29 02:24:48 -07:00
committed by kshitij
parent a87f0a82a5
commit 41ff6e5937
17 changed files with 484 additions and 860 deletions
+3 -4
View File
@@ -69,11 +69,11 @@ class UpstreamAdapter(ABC):
@abstractmethod
def get_credential(self) -> UpstreamCredential:
"""Return a fresh credential, refreshing/minting if necessary.
"""Return a fresh credential, refreshing or rotating if necessary.
Implementations should:
- refresh the access token if it's near expiry
- mint/rotate the upstream bearer key if it's near expiry
- rotate the upstream bearer key if it's near expiry
- persist any refreshed state back to disk
Raises:
@@ -90,8 +90,7 @@ class UpstreamAdapter(ABC):
"""Return an alternate credential after an upstream auth failure.
The default is no retry. Providers can override this for one-shot
fallback paths, such as switching from a preferred token type to a
legacy bearer after the upstream rejects the first request.
fallback paths after the upstream rejects the first request.
"""
_ = failed_credential, status_code
return None
+10 -22
View File
@@ -1,13 +1,8 @@
"""Nous Portal upstream adapter.
Reads the user's Nous OAuth state from ``~/.hermes/auth.json`` through the
shared runtime resolver, refreshes the access token and resolves the
``agent_key`` compatibility credential when needed, then exposes the upstream
base URL plus bearer for the proxy server to forward to.
The ``agent_key`` field may hold either a NAS invoke JWT or the legacy
opaque session key. The refresh helper handles both — see
:func:`hermes_cli.auth.resolve_nous_runtime_credentials`.
shared runtime resolver, validates or refreshes the inference JWT, then exposes
the upstream base URL plus bearer for the proxy server to forward to.
"""
from __future__ import annotations
@@ -20,7 +15,6 @@ from hermes_cli.auth import (
AuthError,
DEFAULT_NOUS_INFERENCE_URL,
NOUS_INFERENCE_AUTH_MODE_AUTO,
NOUS_INFERENCE_AUTH_MODE_LEGACY,
_load_auth_store,
_auth_store_lock,
_is_terminal_nous_refresh_error,
@@ -72,8 +66,8 @@ class NousPortalAdapter(UpstreamAdapter):
state = self._read_state()
if state is None:
return False
# We need either a usable agent_key OR (refresh_token + access_token)
# to recover. The refresh helper will mint/refresh as needed.
# We need either a usable inference JWT OR (refresh_token + access_token)
# to recover. The refresh helper validates and refreshes as needed.
return bool(
state.get("agent_key")
or (state.get("refresh_token") and state.get("access_token"))
@@ -90,14 +84,8 @@ class NousPortalAdapter(UpstreamAdapter):
failed_credential: UpstreamCredential,
status_code: int,
) -> Optional[UpstreamCredential]:
if status_code != 401:
return None
if failed_credential.bearer.count(".") != 2:
return None
logger.info("proxy: Nous upstream rejected bearer; retrying with legacy session key")
return self._get_credential(
inference_auth_mode=NOUS_INFERENCE_AUTH_MODE_LEGACY,
)
_ = failed_credential, status_code
return None
def _get_credential(self, *, inference_auth_mode: str) -> UpstreamCredential:
with self._lock:
@@ -131,10 +119,10 @@ class NousPortalAdapter(UpstreamAdapter):
f"Failed to refresh Nous Portal credentials: {exc}"
) from exc
agent_key = refreshed.get("api_key")
if not agent_key:
runtime_key = refreshed.get("api_key")
if not runtime_key:
raise RuntimeError(
"Nous Portal refresh did not return a usable agent_key. "
"Nous Portal refresh did not return a usable inference JWT. "
"Try `hermes auth add nous` to re-authenticate."
)
@@ -145,7 +133,7 @@ class NousPortalAdapter(UpstreamAdapter):
base_url = base_url.rstrip("/")
return UpstreamCredential(
bearer=agent_key,
bearer=runtime_key,
base_url=base_url,
expires_at=refreshed.get("expires_at"),
)