-
Notifications
You must be signed in to change notification settings - Fork 553
Description
** Please make sure you read the contribution guide and file the issues in the right place. **
Contribution guide.
🔴 Required Information
Please ensure all items in this section are completed to allow for efficient
triaging. If an item is not applicable to you - please mark it as N/A
Is your feature request related to a specific problem?
adk-go currently lacks first-class support for authenticated tools that require OAuth or other user credentials. The Python ADK provides tool_context.request_credential(auth_config), generate_auth_event() in the flow, and an auth preprocessor for callbacks. adk-go has references to adk_request_credential (e.g. in contents_processor.go) and a stub authPreprocessor, but no working implementation.
As a result:
- No
tool/toolauthpackage: types (AuthConfig,OAuth2Credential), OAuth URL generation, token exchange, and StateDelta conventions are absent. - authPreprocessor is a no-op: the callback
FunctionResponseforadk_request_credentialis never handled; OAuth code is never exchanged for tokens. - REST events lack the correct format: when a tool stores auth config in
StateDelta, the runner yields an event with raw tool response +StateDelta. adk-web expectslongRunningToolIdsandcontent.partswith aFunctionCallnamedadk_request_credentialcontainingauthConfig.exchangedAuthCredential.oauth2.authUri. The raw event does not satisfy these, so the OAuth popup never opens.
Proposed Solution
Add complete tool authentication support to adk-go, achieving parity with adk-python for the adk_request_credential protocol:
tool/toolauthpackage — Types (AuthConfig,AuthCredential,OAuth2Credential), constants (StateDeltaKeyPrefix,CredentialStatePrefix),AuthConfigFromResponseMap,ExchangeAndStore,ExchangeAndStoreServiceAccount,GenerateAuthRequest,IsAuthRequired,ExtractAuthRequest,BuildAuthRequestEvent.- authPreprocessor implementation — When a user message contains a
FunctionResponsewithname == adk_request_credential, parse the response intoAuthConfig, callExchangeAndStoreto exchange the OAuth code for tokens and store in session state, find the original tool call byfunctionCallID, and re-run it viahandleFunctionCalls. - REST-layer auth-event transformation — Before sending each event in
RunSSEHandlerandRunHandler, ifIsAuthRequired(event)is true, extract functionCallID and authConfig viaExtractAuthRequest, build anadk_request_credentialevent viaBuildAuthRequestEvent(withContent.Parts=FunctionCall,LongRunningToolIDsset,authConfigin camelCase), and send that instead of the raw event. tool.Context.SessionState()— AddSessionState() session.ReadonlyStatetotool.Contextso tools can read credentials from session state after the OAuth callback.
Impact on your work
We run Go ADK agents with the bundled adk-web UI for development and testing. Without this feature, authenticated tools (OAuth2) do not work—the OAuth popup never appears and users cannot complete the authorization flow. This is critical for any deployment using adk-web or similar clients expecting the adk_request_credential event structure. We have a working implementation and would like to upstream it so we can consume the official release instead of maintaining a fork.
🟡 Recommended Information
Alternatives Considered
- Implement event emission in the flow (like Python) — Adding
generate_auth_event()inbase_flow.gowould require detectingStateDeltaauth keys and yielding an additional event. This is a larger change and could affect A2A and other transports. The REST-layer transformation is localized and achieves the same client-facing result. - Client-side workaround — Modifying adk-web to also detect auth requests via
StateDeltawould duplicate protocol logic in every client and diverge from the Python-based adk-web. - Agent-only implementation — Agents could implement tool auth in their own code, but this would require duplicating types, OAuth exchange logic, and REST transformation in every agent. Baking it into adk-go keeps behavior consistent and discoverable.
Willingness to contribute
Yes. We have implemented and tested the full feature and are willing to contribute a PR.
Proposed API / Implementation
tool/toolauth package:
// toolauth.go
const FunctionCallName = "adk_request_credential"
type AuthConfig struct {
RawAuthCredential *AuthCredential
ExchangedAuthCredential *AuthCredential
CredentialKey string
AuthType AuthCredentialType
}
type OAuth2Credential struct { ClientID, AuthURI, TokenURI, RedirectURI, AuthResponseURI, State, Scopes, AccessToken, RefreshToken, ExpiresAt ... }
// constants.go
const CredentialStatePrefix = "temp:"
const StateDeltaKeyPrefix = "adk_auth_request_"
// handler.go
func AuthConfigFromResponseMap(m map[string]any) (AuthConfig, error)
func ExchangeAndStore(ctx context.Context, cfg AuthConfig, state session.State) error
func ExchangeAndStoreServiceAccount(ctx context.Context, cfg AuthConfig, state session.State) error
// auth_request.go
func GenerateAuthRequest(cfg AuthConfig) (AuthConfig, error)
func IsAuthRequired(event *session.Event) bool
func ExtractAuthRequest(event *session.Event) (functionCallID string, authConfig AuthConfig, ok bool)
func BuildAuthRequestEvent(sourceEvent *session.Event, functionCallID string, authConfig AuthConfig) *session.EventREST controller (server/adkrest/controllers/runtime.go):
// Before sending each event in RunSSEHandler and RunHandler:
if toolauth.IsAuthRequired(event) {
if fnCallID, authCfg, ok := toolauth.ExtractAuthRequest(event); ok {
if authEvent := toolauth.BuildAuthRequestEvent(event, fnCallID, authCfg); authEvent != nil {
event = authEvent
}
}
}tool.Context extension:
// tool/tool.go
SessionState() session.ReadonlyStateauthPreprocessor: For each FunctionResponse with name == toolauth.FunctionCallName, parse response → ExchangeAndStore → find original tool call → re-run via handleFunctionCalls.
Additional Context
adk-web expectations: For the OAuth popup to open, adk-web requires (1) e.longRunningToolIds non-empty, (2) a FunctionCall in e.content.parts whose id is in longRunningToolIds, (3) func.args.authConfig.exchangedAuthCredential.oauth2.authUri exists. The transformed event satisfies all of these.
Protocol alignment: This achieves parity with adk-python: tools store auth requests in StateDelta; the REST layer transforms them into the canonical adk_request_credential event format; the auth preprocessor handles the callback and re-runs the tool. Dependencies: golang.org/x/oauth2, google.golang.org/genai.