Skip to content

Commit 0fc5ab2

Browse files
authored
fix: share the caller principal decision as caller_may_confirm (1/5)
Review found a second consumer of human-in-the-loop answers, the workflow tool node, that the gate in the agent confirmation flow did not cover. The decision moves next to CallerPrincipal so both consumers refuse the same callers.
1 parent 55972cb commit 0fc5ab2

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

‎src/google/adk/agents/_caller_principal.py‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@
1616

1717
from __future__ import annotations
1818

19+
import logging
1920
from typing import Optional
2021

2122
from pydantic import BaseModel
2223
from pydantic import ConfigDict
2324

25+
from ..features import FeatureName
26+
from ..features import is_feature_enabled
27+
28+
logger = logging.getLogger("google_adk." + __name__)
29+
2430

2531
class CallerPrincipal(BaseModel):
2632
"""Who sent this invocation, and whether a serving layer vouched for them.
@@ -56,3 +62,65 @@ class CallerPrincipal(BaseModel):
5662
Informational only. Trust decisions must key off ``authenticated``, never
5763
off the source, so a transport can not be used as a proxy for identity.
5864
"""
65+
66+
67+
def caller_may_confirm(principal: Optional[CallerPrincipal]) -> bool:
68+
"""Decides whether a tool confirmation from this caller may be honored.
69+
70+
Every consumer of a human-in-the-loop answer asks this one question, so the
71+
agent pipeline and the workflow tool node refuse the same callers. Three
72+
states, and only the middle one is a refusal:
73+
74+
- No principal: nothing vouched for the caller because nothing had to. The
75+
invocation was started in process, so the caller is the operator.
76+
- Principal present, not authenticated: a serving layer handled this request
77+
and could not say who sent it, so the approval is not known to be the
78+
operator's.
79+
- Principal present and authenticated: the serving layer verified the caller.
80+
81+
The question asked here is deliberately about authentication and not about
82+
transport. A transport is a proxy for identity, and a proxy for identity
83+
fails in both directions: it refuses authenticated peers that happen to
84+
arrive over the wire, and it admits anyone who reaches an ungated path.
85+
86+
Strict mode is the STRICT_CALLER_PRINCIPAL feature, off by default for now
87+
so that upgrading cannot break a working deployment that runs its A2A server
88+
without an authenticator; those get a warning instead. The intent is to flip
89+
the registry default at the next major version.
90+
91+
Callers that get ``False`` should store the answer as ``confirmed=False``
92+
rather than drop it. Dropping it leaves the confirmation request pending with
93+
nothing left to resolve it, which is what made an earlier attempt at this
94+
guard stall every human-in-the-loop tool. ``confirmed=False`` is a state the
95+
framework already has a contract for -- it is what a human decline produces
96+
-- so the tool returns its rejection response and the turn ends with a
97+
reason the caller can see.
98+
99+
Args:
100+
principal: The ``caller_principal`` of the current invocation context.
101+
102+
Returns:
103+
True if the answer may be honored as given, False if it must be treated as
104+
a rejection.
105+
"""
106+
if principal is None or principal.authenticated:
107+
return True
108+
109+
if not is_feature_enabled(FeatureName.STRICT_CALLER_PRINCIPAL):
110+
logger.warning(
111+
"Honoring a tool confirmation from an unauthenticated caller"
112+
" (principal source %r). The serving layer could not say who sent this"
113+
" approval, so it is not known to be the operator's. Enable the"
114+
" STRICT_CALLER_PRINCIPAL feature to refuse these instead; that is"
115+
" intended to become the default in a future major version.",
116+
principal.source,
117+
)
118+
return True
119+
120+
logger.error(
121+
"Refusing a tool confirmation from an unauthenticated caller (principal"
122+
" source %r). Enable authentication on the serving layer, or disable the"
123+
" STRICT_CALLER_PRINCIPAL feature to downgrade this to a warning.",
124+
principal.source,
125+
)
126+
return False

0 commit comments

Comments
 (0)