1818from google .auth import compute_engine
1919from google .oauth2 import id_token
2020import google .auth
21+ from contextvars import ContextVar
2122
2223from .credentials import CredentialConfig , CredentialType
2324
25+ # Global ContextVar for User Identity (3LO) tokens to be injected per-request
26+ USER_TOKEN_CONTEXT_VAR : ContextVar [Optional [str ]] = ContextVar ("toolbox_user_token" , default = None )
27+
2428
2529class ToolboxClient :
2630 """
@@ -60,7 +64,7 @@ def __init__(
6064 self ._configure_auth (credentials )
6165
6266 self ._client = toolbox_core .ToolboxClient (
63- server_url = server_url ,
67+ url = server_url ,
6468 client_headers = self ._core_client_headers ,
6569 ** kwargs
6670 )
@@ -94,13 +98,18 @@ def _configure_auth(self, creds: CredentialConfig):
9498
9599 elif creds .type == CredentialType .USER_IDENTITY :
96100 # For USER_IDENTITY (3LO), the *Tool* handles the interactive flow at runtime.
97- # The client itself doesn't set a global header because the token is per-user
98- # and passed via the tool's execution context (in the future) or handled by the tool wrapper.
99- # The ToolboxTool wrapper will need to inject the token per-request or we rely on
100- # toolbox-core's per-request auth support if widely available, but ADK flow involves
101- # getting the token in `run_async`.
102- # For now, we leave client-level headers empty for this strategy.
103- pass
101+ # We use a ContextVar to inject the token per-request.
102+
103+ def get_user_token () -> str :
104+ token = USER_TOKEN_CONTEXT_VAR .get ()
105+ if not token :
106+ # If this is called but no token is set in context, it means
107+ # the tool wrapper failed to set it or we are in a context where
108+ # we expected it. We return empty string which might cause 401.
109+ return ""
110+ return f"Bearer { token } "
111+
112+ self ._core_client_headers ["Authorization" ] = get_user_token
104113
105114 def _create_adc_token_getter (self , audience : str ) -> Callable [[], str ]:
106115 """Returns a callable that fetches a fresh ID token using ADC."""
@@ -127,8 +136,8 @@ def get_token() -> str:
127136 # If we are here, we might need to manually sign via IAM or similar if it's a generic SA.
128137 # For simplicity in this v1, we assume fetch_id_token works or standard creds work.
129138 # Re-attempt fetch_id_token on the credentials object if possible
130- curr_token = creds . token
131- return f"Bearer { curr_token } "
139+ curr_token = getattr ( creds , " token" , None )
140+ return f"Bearer { curr_token } " if curr_token else ""
132141
133142 return get_token
134143
@@ -140,6 +149,10 @@ def get_token() -> str:
140149 return f"Bearer { credentials .token } "
141150 return get_token
142151
152+ @property
153+ def credential_config (self ) -> Optional [CredentialConfig ]:
154+ return self ._credentials
155+
143156 async def load_toolset (self , toolset_name : str , ** kwargs ):
144157 return await self ._client .load_toolset (toolset_name , ** kwargs )
145158
0 commit comments