chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# ADK Workflow Auth Config Sample
|
||||
|
||||
## Overview
|
||||
|
||||
This sample demonstrates how to use `auth_config` on a `FunctionNode` to require user authentication before the node runs.
|
||||
|
||||
When a node has `auth_config`, the workflow automatically:
|
||||
|
||||
1. Pauses the node and emits an `adk_request_credential` FunctionCall event
|
||||
1. The invocation ends — the node is marked as waiting
|
||||
1. The client sends a new request with the credential as a FunctionResponse
|
||||
1. The workflow stores the credential in session state and re-runs the node
|
||||
|
||||
The **ADK web UI** (`adk web`) handles step 3 automatically — it recognizes auth
|
||||
requests and presents an auth dialog. If you use a custom client, you need to
|
||||
handle the `adk_request_credential` FunctionCall and respond with the credential
|
||||
yourself.
|
||||
|
||||
This sample uses **API key** authentication (the simplest credential type).
|
||||
|
||||
## No External Setup Required
|
||||
|
||||
This sample uses a mock weather lookup. No external API key or server is needed. When the auth UI prompts for a key, you can enter any value (e.g., `my-test-key-123`).
|
||||
|
||||
## Sample Inputs
|
||||
|
||||
Send any message (e.g., `go`) to start the workflow.
|
||||
|
||||
## Graph
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
START --> fetch_weather[fetch_weather <br/>pauses for auth on first run]
|
||||
fetch_weather --> summarize
|
||||
```
|
||||
|
||||
## How To
|
||||
|
||||
1. Define an `AuthConfig` with the auth scheme and credential type:
|
||||
|
||||
```python
|
||||
from google.adk.auth.auth_tool import AuthConfig
|
||||
from google.adk.auth.auth_credential import AuthCredential, AuthCredentialTypes
|
||||
|
||||
auth_config = AuthConfig(
|
||||
auth_scheme=APIKey(**{'in': APIKeyIn.header, 'name': 'X-Api-Key'}),
|
||||
raw_auth_credential=AuthCredential(
|
||||
auth_type=AuthCredentialTypes.API_KEY,
|
||||
api_key='placeholder',
|
||||
),
|
||||
credential_key='weather_api_key',
|
||||
)
|
||||
```
|
||||
|
||||
1. Use the `@node` decorator with `auth_config` and `rerun_on_resume=True`:
|
||||
|
||||
```python
|
||||
@node(auth_config=auth_config, rerun_on_resume=True)
|
||||
def fetch_weather(ctx: Context):
|
||||
...
|
||||
```
|
||||
|
||||
1. Inside the function, retrieve the credential from `ctx`:
|
||||
|
||||
```python
|
||||
def fetch_weather(ctx: Context):
|
||||
cred = ctx.get_auth_response(auth_config)
|
||||
api_key = cred.api_key
|
||||
# Use api_key to call your API...
|
||||
```
|
||||
|
||||
## OAuth2
|
||||
|
||||
The same `auth_config` pattern works with OAuth2 and OpenID Connect. The key
|
||||
differences:
|
||||
|
||||
- **Auth scheme**: Use `OAuth2` (from `fastapi.openapi.models`) instead of
|
||||
`APIKey`. Configure the authorization and token URLs in the OAuth2 flows.
|
||||
- **Raw credential**: Set `auth_type=AuthCredentialTypes.OAUTH2` and provide
|
||||
`client_id`, `client_secret`, and `redirect_uri` in the `oauth2` field.
|
||||
- **Web UI flow**: The ADK web UI recognizes OAuth2 auth requests and opens
|
||||
an authorization popup automatically. The user authenticates with the
|
||||
provider, and the UI sends the full `AuthConfig` response back. No special
|
||||
handling is needed in the node.
|
||||
- **Token exchange**: The framework automatically exchanges the authorization
|
||||
code for an access token via `AuthHandler.exchange_auth_token()`.
|
||||
|
||||
```python
|
||||
from fastapi.openapi.models import OAuth2, OAuthFlowAuthorizationCode, OAuthFlows
|
||||
|
||||
auth_config = AuthConfig(
|
||||
auth_scheme=OAuth2(
|
||||
flows=OAuthFlows(
|
||||
authorizationCode=OAuthFlowAuthorizationCode(
|
||||
authorizationUrl='https://provider.com/authorize',
|
||||
tokenUrl='https://provider.com/token',
|
||||
scopes={'read': 'Read access'},
|
||||
)
|
||||
)
|
||||
),
|
||||
raw_auth_credential=AuthCredential(
|
||||
auth_type=AuthCredentialTypes.OAUTH2,
|
||||
oauth2=OAuth2Auth(
|
||||
client_id='YOUR_CLIENT_ID',
|
||||
client_secret='YOUR_CLIENT_SECRET',
|
||||
redirect_uri='http://localhost:8000/callback',
|
||||
),
|
||||
),
|
||||
credential_key='my_oauth_credential',
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,83 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Auth API Key sample: FunctionNode with API key authentication.
|
||||
|
||||
Demonstrates how to use `auth_config` on a FunctionNode to pause
|
||||
the workflow and request user credentials before running the node.
|
||||
|
||||
Flow:
|
||||
1. User sends any message to start the workflow.
|
||||
2. The `fetch_weather` node pauses and requests an API key.
|
||||
3. The user provides the API key through the auth UI.
|
||||
4. The node runs with the credential available in session state.
|
||||
5. The `summarize` node displays the result.
|
||||
"""
|
||||
|
||||
from fastapi.openapi.models import APIKey
|
||||
from fastapi.openapi.models import APIKeyIn
|
||||
from google.adk import Event
|
||||
from google.adk import Workflow
|
||||
from google.adk.agents.context import Context
|
||||
from google.adk.auth.auth_credential import AuthCredential
|
||||
from google.adk.auth.auth_credential import AuthCredentialTypes
|
||||
from google.adk.auth.auth_tool import AuthConfig
|
||||
from google.adk.workflow import node
|
||||
|
||||
# --- Auth configuration ---
|
||||
# Uses API key auth: the simplest credential type.
|
||||
# The user will be prompted to provide an API key via the auth UI.
|
||||
auth_config = AuthConfig(
|
||||
auth_scheme=APIKey(**{'in': APIKeyIn.header, 'name': 'X-Api-Key'}),
|
||||
raw_auth_credential=AuthCredential(
|
||||
auth_type=AuthCredentialTypes.API_KEY,
|
||||
api_key='placeholder',
|
||||
),
|
||||
credential_key='weather_api_key',
|
||||
)
|
||||
|
||||
|
||||
@node(auth_config=auth_config, rerun_on_resume=True)
|
||||
def fetch_weather(ctx: Context):
|
||||
"""Fetches weather data using the authenticated API key."""
|
||||
# After auth completes, the credential is available via ctx.
|
||||
cred = ctx.get_auth_response(auth_config)
|
||||
api_key = cred.api_key if cred else 'unknown'
|
||||
|
||||
# In a real agent, you would use the api_key to call an external API.
|
||||
# For this sample, we just echo it back (masked).
|
||||
masked = api_key[:4] + '****' if len(api_key) > 4 else '****'
|
||||
return {
|
||||
'city': 'San Francisco',
|
||||
'temperature': '18C',
|
||||
'condition': 'Sunny',
|
||||
'api_key_used': masked,
|
||||
}
|
||||
|
||||
|
||||
def summarize(node_input: dict):
|
||||
"""Displays the weather result."""
|
||||
yield Event(
|
||||
message=(
|
||||
f"Weather for {node_input['city']}:"
|
||||
f" {node_input['temperature']}, {node_input['condition']}."
|
||||
f" (Authenticated with key: {node_input['api_key_used']})"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
root_agent = Workflow(
|
||||
name='auth_api_key',
|
||||
edges=[('START', fetch_weather, summarize)],
|
||||
)
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"appName": "auth_api_key",
|
||||
"events": [
|
||||
{
|
||||
"author": "user",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "go"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"id": "e-1",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"author": "auth_api_key",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"functionCall": {
|
||||
"args": {
|
||||
"authConfig": {
|
||||
"authScheme": {
|
||||
"in": "header",
|
||||
"name": "X-Api-Key",
|
||||
"type": "apiKey"
|
||||
},
|
||||
"credentialKey": "weather_api_key",
|
||||
"rawAuthCredential": {
|
||||
"apiKey": "placeholder",
|
||||
"authType": "apiKey"
|
||||
}
|
||||
},
|
||||
"functionCallId": "fc-1",
|
||||
"message": "Please provide your API key for X-Api-Key."
|
||||
},
|
||||
"id": "fc-1",
|
||||
"name": "adk_request_credential"
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-2",
|
||||
"invocationId": "i-1",
|
||||
"longRunningToolIds": [
|
||||
"fc-1"
|
||||
],
|
||||
"nodeInfo": {
|
||||
"path": "auth_api_key@1/fetch_weather@1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"author": "user",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"functionResponse": {
|
||||
"id": "fc-1",
|
||||
"name": "adk_request_credential",
|
||||
"response": {
|
||||
"result": "12345678"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"author": "auth_api_key",
|
||||
"id": "e-4",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"outputFor": [
|
||||
"auth_api_key@1/fetch_weather@1"
|
||||
],
|
||||
"path": "auth_api_key@1/fetch_weather@1"
|
||||
},
|
||||
"output": {
|
||||
"api_key_used": "1234****",
|
||||
"city": "San Francisco",
|
||||
"condition": "Sunny",
|
||||
"temperature": "18C"
|
||||
}
|
||||
},
|
||||
{
|
||||
"author": "auth_api_key",
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"text": "Weather for San Francisco: 18C, Sunny. (Authenticated with key: 1234****)"
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
},
|
||||
"id": "e-5",
|
||||
"invocationId": "i-1",
|
||||
"nodeInfo": {
|
||||
"path": "auth_api_key@1/summarize@1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "3bb54ead-8f56-468e-b64f-f0d534a66c69",
|
||||
"state": {
|
||||
"__session_metadata__": {
|
||||
"displayName": "go"
|
||||
}
|
||||
},
|
||||
"userId": "user"
|
||||
}
|
||||
Reference in New Issue
Block a user