revert: keep Google Chat OAuth secret + active_provider profile-scoped (#39398)

* Revert "fix(gateway): anchor Google Chat OAuth client secret to default Hermes root"

This reverts commit fff0561441.

* Revert "fix(cli): honor global-root active_provider fallback for named profiles"

This reverts commit 3858cf4307.

* docs(google_chat): describe OAuth client secret as profile-scoped, not host-wide

The setup docs, oauth docstring, and the adapter's 'no credentials'
error message all described the Google Chat OAuth client secret as
host-wide shared infrastructure. That contradicts profile isolation:
profiles are separate auth boundaries, so two profiles can point at
different Google OAuth apps / accounts. Reword all three to say the
secret is profile-scoped and each profile registers its own.
This commit is contained in:
Teknium
2026-06-04 16:54:40 -07:00
committed by GitHub
parent 6ad015255d
commit 5300727a08
6 changed files with 32 additions and 245 deletions
+1 -1
View File
@@ -1390,7 +1390,7 @@ class GoogleChatAdapter(BasePlatformAdapter):
if arg == "start":
if not oauth_helper._client_secret_path().exists():
await _reply(
"⚠️ No client credentials stored on the host. Send "
"⚠️ No client credentials stored for this profile. Send "
"`/setup-files` (no args) for setup instructions."
)
return True
+4 -45
View File
@@ -50,10 +50,8 @@ Token storage layout
``${HERMES_HOME}/google_chat_user_oauth_pending/<sanitized_email>.json``
- Legacy pending state:
``${HERMES_HOME}/google_chat_user_oauth_pending.json``
- Shared OAuth client (one per host, anchored at the default Hermes root so
every profile sees it; a profile-local copy under ``${HERMES_HOME}`` wins
when present):
``<default-root>/google_chat_user_client_secret.json`` (default ``~/.hermes``)
- OAuth client secret (profile-scoped — each profile registers its own):
``${HERMES_HOME}/google_chat_user_client_secret.json``
"""
from __future__ import annotations
@@ -77,11 +75,7 @@ logger = logging.getLogger("gateway.platforms.google_chat_user_oauth")
# Use the project's HERMES_HOME helper so the token follows the user's
# profile (e.g. tests can override via HERMES_HOME=/tmp/...).
try:
from hermes_constants import (
display_hermes_home,
get_default_hermes_root,
get_hermes_home,
)
from hermes_constants import display_hermes_home, get_hermes_home
except (ModuleNotFoundError, ImportError):
# Fallback for environments where hermes_constants isn't importable
# (mirrors the same fallback used by the google-workspace skill's
@@ -90,24 +84,6 @@ except (ModuleNotFoundError, ImportError):
val = os.environ.get("HERMES_HOME", "").strip()
return Path(val) if val else Path.home() / ".hermes"
def get_default_hermes_root() -> Path:
# Mirror hermes_constants.get_default_hermes_root(): resolve the
# profile root so host-wide files (the shared client secret) are
# found regardless of which profile is active.
native_home = Path.home() / ".hermes"
env_home = os.environ.get("HERMES_HOME", "").strip()
if not env_home:
return native_home
env_path = Path(env_home)
try:
env_path.resolve().relative_to(native_home.resolve())
return native_home
except ValueError:
pass
if env_path.parent.name == "profiles":
return env_path.parent.parent
return env_path
def display_hermes_home() -> str:
home = get_hermes_home()
try:
@@ -164,24 +140,7 @@ def _token_path(email: Optional[str] = None) -> Path:
def _client_secret_path() -> Path:
"""Path to the shared OAuth client secret (one per host).
The client secret identifies the OAuth *app*, not a user or a profile,
so it is anchored at the default Hermes root (``~/.hermes`` — or the
Docker root) rather than the active profile's ``HERMES_HOME``. That way
the one-time ``--client-secret`` host setup is visible to gateways
running under any named profile, exactly as the docs describe ("one
file per host is enough no matter how many users authorize later").
A profile-local secret (``$HERMES_HOME/google_chat_user_client_secret.json``)
still takes precedence when present, for installs that seeded one under
the previous profile-scoped behavior or that deliberately run a separate
OAuth app per profile.
"""
profile_local = _hermes_home() / "google_chat_user_client_secret.json"
if profile_local.exists():
return profile_local
return get_default_hermes_root() / "google_chat_user_client_secret.json"
return _hermes_home() / "google_chat_user_client_secret.json"
def _pending_auth_path(email: Optional[str] = None) -> Path: