google / google/adk-python

OpenAPIToolset does not support tools (endpoints) that have multiple security definitions.

Aperta
#2,228 6 commenti 0 reazioni 1 assegnatario Rivendicata da @seanzhou1023 Vedi su GitHub
needs review tools
Lingua principale
Python
Stelle
21.5k
Fork
4k
Merge medio
1g 14h
PR unite (30g)
37

Descrizione

**Describe the bug**
OpenAPIToolset and APIHubToolset only allow each tool to have a single security schema.
For example:
If your endpoint(tool) required multiple security, then OpenAPIToolset and APIHubToolset will only choose the first one become its auth_scheme [1]

[1] https://github.com/google/adk-python/blob/main/src/google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py#L129-L136

**To Reproduce**
Steps to reproduce the behavior:
1. Create python environment
```cmd
uv venv --python 3.12.0
source .venv/bin/activate
uv pip install google-adk==1.8.0
```
2. Use this openapi.json
```json
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"servers": [
{
"url": "http://localhost:8080"
}
],
"paths": {
"/helloworld": {
"get": {
"summary": "Helloworld",
"operationId": "helloworld_helloworld_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {

}
}
}
}
},
"security": [
{
"api1": []
},
{
"api2": []
}
]
}
}
},
"components": {
"securitySchemes": {
"api1": {
"type": "apiKey",
"in": "header",
"name": "x-api-key-1"
},
"api2": {
"type": "apiKey",
"in": "header",
"name": "x-api-key-2"
}
}
}
}
```
3. use test_tool.py to testing.
```python
import os
import json
from google.auth import default
from google.auth.transport.requests import Request
from google.cloud import secretmanager
from google.adk.tools.apihub_tool.apihub_toolset import APIHubToolset
from google.adk.agents.llm_agent import LlmAgent
from google.adk.auth.auth_credential import AuthCredential, AuthCredentialTypes, HttpAuth, HttpCredentials
from google.adk.tools.openapi_tool.auth.auth_helpers import token_to_scheme_credential
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
import asyncio

OPENAPI_SPEC_FILENAME="hello_world_openapi.json"
TOKEN="secret-key-1"

with open(os.path.join(os.path.dirname(__file__), OPENAPI_SPEC_FILENAME), 'r') as f:
spec_content = f.read()

auth_scheme, auth_credential = token_to_scheme_credential(
"apikey", "header", "x-api-key-1", TOKEN
)

smorch_toolset = OpenAPIToolset(
spec_str=spec_content,
spec_str_type='json',
auth_scheme=auth_scheme,
auth_credential=auth_credential
)

async def main():
tools = await smorch_toolset.get_tools()
for tool in tools:
print(tool.auth_scheme)

asyncio.run(main())

```
4. tool only have one auth_scheme (x-api-key-1), x-api-key-2 is missing....
```
(adk_with_different_auth) user@abehsu-us-vscode:~/abehsu/fastapi_with_multiple_security$ python /home/user/abehsu/adk_with_different_auth/api_key/openapi/test_tool.py
type_= description=None in_= name='x-api-key-1'
```

5. The other way you can test it is using this agent.py
```python
import os
from google.adk.agents.llm_agent import LlmAgent
from google.adk.tools.openapi_tool.auth.auth_helpers import token_to_scheme_credential
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset

OPENAPI_SPEC_FILENAME="hello_world_openapi.json"
TOKEN="secret-key-1"

with open(os.path.join(os.path.dirname(__file__), OPENAPI_SPEC_FILENAME), 'r') as f:
spec_content = f.read()

auth_scheme, auth_credential = token_to_scheme_credential(
"apikey", "header", "x-api-key-1", TOKEN
)

hello_world_toolset = OpenAPIToolset(
spec_str=spec_content,
spec_str_type='json',
auth_scheme=auth_scheme,
auth_credential=auth_credential
)

# --- Agent Configuration ---
# Configure and create the main LLM Agent.
root_agent = LlmAgent(
model='gemini-2.0-flash',
name='hello_world_agent',
instruction='when user say hi you will use hello world tool to response them',
tools=[hello_world_toolset],
)

```

Backend server example code:
```python
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import APIKeyHeader

api_key_header_1 = APIKeyHeader(name="x-api-key-1", auto_error=False, scheme_name="api1")
api_key_header_2 = APIKeyHeader(name="x-api-key-2", auto_error=False, scheme_name="api2")

SECRET_KEY_1 = "secret-key-1"
SECRET_KEY_2 = "secret-key-2"

async def get_api_keys_1(
key1: str = Depends(api_key_header_1),
key2: str = Depends(api_key_header_2),
):
if not key1 or key1 != SECRET_KEY_1:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing API Key 1",
)
if not key2 or key2 != SECRET_KEY_2:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing API Key 2",
)
return key1, key2

app = FastAPI()

@app.get("/helloworld", dependencies=[Depends(get_api_keys_1)])
async def helloworld():
return {"message": "Hello World"}

```

**Screenshots**

Image

**Desktop (please complete the following information):**
- OS: Linux
- Python version(python -V): Python 3.12.10
- ADK version(pip show google-adk): 1.18.0

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.