fix(auth): resolve fallback api keys through secret_scope, not raw env

resolve_entry_api_key() and the duplicated _fallback_entry_api_key()
read key_env via a raw os.getenv(), bypassing per-profile secret
scoping in the multiplexed gateway. Under multiplexing this can hand
a fallback request another profile's credential. Both now resolve
through agent.secret_scope.get_secret(), which reads the active
profile scope when multiplexing is on and falls back to os.environ
unchanged when it's off, so single-profile behavior is preserved.

Closes #74311
This commit is contained in:
joaomarcos
2026-07-29 16:30:28 -03:00
committed by Teknium
parent 87f5c5351a
commit d52a1c25e0
3 changed files with 39 additions and 10 deletions
+11 -2
View File
@@ -2,7 +2,6 @@
from __future__ import annotations
import os
from typing import Any
@@ -19,6 +18,14 @@ def resolve_entry_api_key(entry: dict[str, Any] | None) -> str | None:
holding the key; ``api_key_env`` accepted as an alias). Returns None when
neither yields a non-empty value, letting ``resolve_runtime_provider``
fall through to the provider's standard credential resolution.
``key_env`` is resolved through ``agent.secret_scope.get_secret`` rather
than a raw ``os.getenv`` — in a multiplexed gateway a bare env read would
ignore the active profile's scope and can return another profile's
credential. ``get_secret`` already implements the right fallback: it
reads ``os.environ`` when there's no active multiplexed scope (matching
prior single-profile behavior), and fails closed only when multiplexing
is active with no scope installed.
"""
if not isinstance(entry, dict):
return None
@@ -27,7 +34,9 @@ def resolve_entry_api_key(entry: dict[str, Any] | None) -> str | None:
return inline
key_env = str(entry.get("key_env") or entry.get("api_key_env") or "").strip()
if key_env:
return os.getenv(key_env, "").strip() or None
from agent.secret_scope import get_secret
return (get_secret(key_env) or "").strip() or None
return None