Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Security Critical: Client_secret is shared in OAuth2 flow with authenticated function tool #3845

Copy link
Copy link
@guillaumeblaquiere

Description

@guillaumeblaquiere
Issue body actions

Describe the bug
The protocol publicly share the client_secret to the ADK client. This information MUST stay confidential and never shared

To Reproduce

  1. Create a simple agent, with a simple tool, and use authenticated function tool
from google.adk.agents.llm_agent import Agent
from fastapi.openapi.models import OAuth2, OAuthFlows, OAuthFlowAuthorizationCode
from google.adk.auth import AuthCredential, AuthConfig, AuthCredentialTypes, OAuth2Auth
from google.adk.tools import ToolContext
from google.adk.tools.authenticated_function_tool import AuthenticatedFunctionTool

YOUR_OAUTH_CLIENT_ID = "513069150666-mkspihfonim1j4f0mfp8j2v6hkdtep6k.apps.googleusercontent.com"
YOUR_OAUTH_CLIENT_SECRET = "GOCSPX-<redacted>


def log_tool(prompt: str, tool_context: ToolContext) -> None:
    """
    Log the user query with the log_tool

    :param prompt: The user query to log
    :param tool_context: The tool context to access credentials

    :return:     None

    """

    print(f"DEBUG: Received prompt: {prompt}")

    headers = {}
    
    # Retrieve the credential using the context and auth config
    exchanged_credential = tool_context.get_auth_response(oauth_config)
    if not exchanged_credential:
        exchanged_credential = tool_context.get_auth_response(oidc_config)
    
    if exchanged_credential and exchanged_credential.oauth2:
        # Log the token for debug purposes as requested
        print(f"DEBUG: Found credential for user {tool_context.user_id}")

        access_token = exchanged_credential.oauth2.access_token

        print(f"DEBUG: Access Token: {access_token}")

    else:
        print("DEBUG: No credential found in tool_context")

    
oauth_scheme = OAuth2(
    flows=OAuthFlows(
        authorizationCode=OAuthFlowAuthorizationCode(
            authorizationUrl="https://accounts.google.com/o/oauth2/auth",
            tokenUrl="https://oauth2.googleapis.com/token",
            scopes={
                "https://www.googleapis.com/auth/cloud-platform": "BigQuery"
            },
        )
    )
)


oauth_credential = AuthCredential(
    auth_type=AuthCredentialTypes.OAUTH2,
    oauth2=OAuth2Auth(
        client_id=YOUR_OAUTH_CLIENT_ID,
        client_secret=YOUR_OAUTH_CLIENT_SECRET
    ),
)

oauth_config = AuthConfig(
        auth_scheme=oauth_scheme,
        raw_auth_credential= oauth_credential
        )


oauth_authenticated_log_tool = AuthenticatedFunctionTool(func=log_tool, auth_config=oauth_config, response_for_auth_required="Pending User Authorization.")

root_agent = Agent(
    model='gemini-2.5-flash',
    name='root_agent',
    description='A helpful assistant for user questions.',
    instruction='Answer user questions to the best of your knowledge. Log the user query with the log_tool',
    tools=[oauth_authenticated_log_tool]   
)

  1. Create a session
  2. Invoke the secure tool

Sample of ADK response to indicate to the client to run a OAuth2 flow

[
  {
    "modelVersion": "gemini-2.5-flash",
    "content": {
      "parts": [
        {
          "text": "Okay, let's break this down. First, I'm starting with a very simple request, in French, to get information: \"Hello, a quel projet j'ai accès?\"  That's my way of asking, \"Hello, which project do I have access to?\" Pretty straightforward, really.\n\nNow, from an architectural standpoint, I know that translates directly into the capabilities of the `list_project_tool`.  That tool *is* designed to tell me exactly that: which Google Cloud projects are available to me. And the beauty of this is its simplicity –  `list_project_tool` doesn't need any additional parameters; it just does what it says on the tin. So, my action is clear: I need to invoke the `list_project_tool` immediately to get the project list and provide a rapid answer.  This is a core, fundamental action.\n",
          "thought": true
        },
        {
          "functionCall": {
            "id": "adk-278007a2-d9c0-4f42-9be3-813ade149f37",
            "args": {},
            "name": "list_project_tool"
          },
          "thoughtSignature": "CpoDAePx_16-kKCcDZLmBIV4VfmwSfm1hqaUlGKD8fEbq-gEZlt60Xr0pGKHyutmyUR3ICd_Mv5NNP2QIxfSx4qrAE9blXQG4YQesJPe0bxM3IiXZRn2FnclS7xBbRi9RfaFCGzJx1Hp_g4WQDaNZIWqaNSb7aM8Cunx7B1EXPHgNbvIJ3IeIsXEVBd24VGJmpWwjRXtbLOvFiPkRwqk4ryl27Eu3AJwzb2uIRXWGtGWdp4JpNW0nGbRgiZ7By25uVGi6ajw2TGXc8gbOmt-5Jw8yjF2ZHSNcJqcqVIT62jCw6S7ViOhrtYjGW4mw5iV4NqjLOd6HE3v4m2Kemohwqz_-6qftz7hyPiFuOFT3QAWu0SOdVC4b3UX97eBgmzikJbPuvcgYzMpMM4cXoG_19JBEii-Ih4SbfVbxoXj-v4iEU2RHvOiDocC3IXpqnTng_MsuacTLhVbd3xndeexkZNu4yqW94AH5dKKytCmM81n-DQDrEe-WuC2l-O3xTN-AUz4nlg7GlmwPN1fUAxfgTuGoByQSayiDcLZH2g="
        }
      ],
      "role": "model"
    },
    "finishReason": "STOP",
    "usageMetadata": {
      "candidatesTokenCount": 5,
      "candidatesTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 5
        }
      ],
      "promptTokenCount": 156,
      "promptTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 156
        }
      ],
      "thoughtsTokenCount": 86,
      "totalTokenCount": 247,
      "trafficType": "ON_DEMAND"
    },
    "avgLogprobs": -1.7120042800903321,
    "invocationId": "e-7a667312-57eb-4955-965f-b4faf1302ce2",
    "author": "space_agent",
    "actions": {
      "stateDelta": {},
      "artifactDelta": {},
      "requestedAuthConfigs": {},
      "requestedToolConfirmations": {}
    },
    "longRunningToolIds": [],
    "id": "734f13d7-d323-4a8a-bff1-da7fde72109e",
    "timestamp": 1764943012.128835
  },
  {
    "content": {
      "parts": [
        {
          "functionCall": {
            "id": "adk-4cab06d4-397d-4013-ab7b-d1ee3bafac12",
            "args": {
              "functionCallId": "adk-278007a2-d9c0-4f42-9be3-813ade149f37",
              "authConfig": {
                "authScheme": {
                  "type": "oauth2",
                  "flows": {
                    "authorizationCode": {
                      "scopes": {
                        "https://www.googleapis.com/auth/cloud-platform": "BigQuery"
                      },
                      "authorizationUrl": "https://accounts.google.com/o/oauth2/auth",
                      "tokenUrl": "https://oauth2.googleapis.com/token"
                    }
                  }
                },
                "rawAuthCredential": {
                  "authType": "oauth2",
                  "oauth2": {
                    "clientId": "513069150666-mkspihfonim1j4f0mfp8j2v6hkdtep6k.apps.googleusercontent.com",
                    "clientSecret": "GOCSPX-<redacted>"
                  }
                },
                "exchangedAuthCredential": {
                  "authType": "oauth2",
                  "oauth2": {
                    "clientId": "513069150666-mkspihfonim1j4f0mfp8j2v6hkdtep6k.apps.googleusercontent.com",
                    "clientSecret": "GOCSPX-<redacted>",
                    "authUri": "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=513069150666-mkspihfonim1j4f0mfp8j2v6hkdtep6k.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&state=0eEsoJ0HwJynIGynZ0GtOZmAiJWsTz&access_type=offline&prompt=consent",
                    "state": "0eEsoJ0HwJynIGynZ0GtOZmAiJWsTz"
                  }
                },
                "credentialKey": "adk_oauth2_-3537330412975045649_oauth2_-8782076668427256465"
              }
            },
            "name": "adk_request_credential"
          }
        }
      ],
      "role": "user"
    },
    "invocationId": "e-7a667312-57eb-4955-965f-b4faf1302ce2",
    "author": "space_agent",
    "actions": {
      "stateDelta": {},
      "artifactDelta": {},
      "requestedAuthConfigs": {},
      "requestedToolConfirmations": {}
    },
    "longRunningToolIds": [
      "adk-4cab06d4-397d-4013-ab7b-d1ee3bafac12"
    ],
    "id": "65986065-222e-4ab2-b9b1-3b40e9adfa73",
    "timestamp": 1764943015.619703
  },
  {
    "content": {
      "parts": [
        {
          "functionResponse": {
            "id": "adk-278007a2-d9c0-4f42-9be3-813ade149f37",
            "name": "list_project_tool",
            "response": {
              "result": "Pending User Authorization."
            }
          }
        }
      ],
      "role": "user"
    },
    "invocationId": "e-7a667312-57eb-4955-965f-b4faf1302ce2",
    "author": "space_agent",
    "actions": {
      "stateDelta": {},
      "artifactDelta": {},
      "requestedAuthConfigs": {
        "adk-278007a2-d9c0-4f42-9be3-813ade149f37": {
          "authScheme": {
            "type": "oauth2",
            "flows": {
              "authorizationCode": {
                "scopes": {
                  "https://www.googleapis.com/auth/cloud-platform": "BigQuery"
                },
                "authorizationUrl": "https://accounts.google.com/o/oauth2/auth",
                "tokenUrl": "https://oauth2.googleapis.com/token"
              }
            }
          },
          "rawAuthCredential": {
            "authType": "oauth2",
            "oauth2": {
              "clientId": "513069150666-mkspihfonim1j4f0mfp8j2v6hkdtep6k.apps.googleusercontent.com",
              "clientSecret": "GOCSPX-<redacted>"
            }
          },
          "exchangedAuthCredential": {
            "authType": "oauth2",
            "oauth2": {
              "clientId": "513069150666-mkspihfonim1j4f0mfp8j2v6hkdtep6k.apps.googleusercontent.com",
              "clientSecret": "GOCSPX-<redacted>",
              "authUri": "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=513069150666-mkspihfonim1j4f0mfp8j2v6hkdtep6k.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&state=0eEsoJ0HwJynIGynZ0GtOZmAiJWsTz&access_type=offline&prompt=consent",
              "state": "0eEsoJ0HwJynIGynZ0GtOZmAiJWsTz"
            }
          },
          "credentialKey": "adk_oauth2_-3537330412975045649_oauth2_-8782076668427256465"
        }
      },
      "requestedToolConfirmations": {}
    },
    "id": "d2beef7d-abb7-499d-8929-d4e5c11da93c",
    "timestamp": 1764943015.618363
  },
  {
    "modelVersion": "gemini-2.5-flash",
    "content": {
      "parts": [
        {
          "text": "I need your authorization to list the projects you have access to. Could you please grant it?"
        }
      ],
      "role": "model"
    },
    "finishReason": "STOP",
    "usageMetadata": {
      "candidatesTokenCount": 19,
      "candidatesTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 19
        }
      ],
      "promptTokenCount": 358,
      "promptTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 442
        }
      ],
      "totalTokenCount": 377,
      "trafficType": "ON_DEMAND"
    },
    "avgLogprobs": -0.19399306648655942,
    "invocationId": "e-7a667312-57eb-4955-965f-b4faf1302ce2",
    "author": "space_agent",
    "actions": {
      "stateDelta": {},
      "artifactDelta": {},
      "requestedAuthConfigs": {},
      "requestedToolConfirmations": {}
    },
    "id": "889fbcf7-1075-41a1-9c8c-4e46aac5acfd",
    "timestamp": 1764943015.620248
  }
]

Expected behavior
Not to see the client_secret, or see it redacted

Desktop (please complete the following information):

  • OS: Windows with WSL Ubuntu 24
  • Python version(python -V): 3.12
  • ADK version(pip show google-adk): 1.19.0

Model Information:

  • Which model is being used(e.g. gemini-2.5-pro): Gemini 2.5 flash

Additional context
I didn't find a way to solve this easily. There are many classes to handle the authentication (credential service, credential manager, authHandler, oauth exchanger,...)

My wish is to have a in memory map with the client_id as a key, and the client secret as value.
When the function response comes in, the auth_config must be enriched with the client_secret, based on the client secret contained in the exchanged

Reactions are currently unavailable

Metadata

Metadata

Assignees

Labels

auth[Component] This issue is related to authorization[Component] This issue is related to authorizationneeds review[Status] The PR/issue is awaiting review from the maintainer[Status] The PR/issue is awaiting review from the maintainer

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.