Skip to content

Commit c560c84

Browse files
committed
feat(samples): Add GitHub OAuth sample to demonstrate token requests
Add a new sample 'auth_oauth' demonstrating how to request a GitHub OAuth token in a workflow and use it to list repositories. Also rename the 'auth_config' sample to 'auth_api_key' for consistency. Change-Id: I014b2f46f74b3017be89efc092380087179cf533
1 parent 365fdda commit c560c84

6 files changed

Lines changed: 255 additions & 10 deletions

File tree

File renamed without changes.

contributing/workflow_samples/auth_config/agent.py renamed to contributing/workflow_samples/auth_api_key/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""Auth Config sample: FunctionNode with API key authentication.
15+
"""Auth API Key sample: FunctionNode with API key authentication.
1616
1717
Demonstrates how to use `auth_config` on a FunctionNode to pause
1818
the workflow and request user credentials before running the node.
@@ -78,6 +78,6 @@ def summarize(node_input: dict):
7878

7979

8080
root_agent = Workflow(
81-
name='auth_config',
81+
name='auth_api_key',
8282
edges=[('START', fetch_weather, summarize)],
8383
)

contributing/workflow_samples/auth_config/tests/go.json renamed to contributing/workflow_samples/auth_api_key/tests/go.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"appName": "auth_config",
2+
"appName": "auth_api_key",
33
"events": [
44
{
55
"author": "user",
@@ -18,7 +18,7 @@
1818
}
1919
},
2020
{
21-
"author": "auth_config",
21+
"author": "auth_api_key",
2222
"content": {
2323
"parts": [
2424
{
@@ -51,7 +51,7 @@
5151
"fc-1"
5252
],
5353
"nodeInfo": {
54-
"path": "auth_config@1/fetch_weather@1"
54+
"path": "auth_api_key@1/fetch_weather@1"
5555
}
5656
},
5757
{
@@ -77,14 +77,14 @@
7777
}
7878
},
7979
{
80-
"author": "auth_config",
80+
"author": "auth_api_key",
8181
"id": "e-4",
8282
"invocationId": "i-1",
8383
"nodeInfo": {
8484
"outputFor": [
85-
"auth_config@1/fetch_weather@1"
85+
"auth_api_key@1/fetch_weather@1"
8686
],
87-
"path": "auth_config@1/fetch_weather@1"
87+
"path": "auth_api_key@1/fetch_weather@1"
8888
},
8989
"output": {
9090
"api_key_used": "1234****",
@@ -94,7 +94,7 @@
9494
}
9595
},
9696
{
97-
"author": "auth_config",
97+
"author": "auth_api_key",
9898
"content": {
9999
"parts": [
100100
{
@@ -106,7 +106,7 @@
106106
"id": "e-5",
107107
"invocationId": "i-1",
108108
"nodeInfo": {
109-
"path": "auth_config@1/summarize@1"
109+
"path": "auth_api_key@1/summarize@1"
110110
}
111111
}
112112
],
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# GitHub OAuth Authentication Sample
2+
3+
## Overview
4+
5+
This sample demonstrates how to use `AuthConfig` with GitHub OAuth2 on a `FunctionNode` in a workflow. It shows how to pause execution to request a GitHub OAuth token from the user and then use that token to list the user's owned repositories via the GitHub API.
6+
7+
## Prerequisites
8+
9+
To run this sample and actually log in, you need to:
10+
11+
1. **Register an OAuth Application on GitHub**:
12+
* Go to your GitHub account settings.
13+
* Navigate to **Developer settings** > **OAuth Apps** > **New OAuth App**.
14+
* Set the **Homepage URL** and **Authorization callback URL** appropriate for your testing environment (e.g., `http://localhost:8000` if running locally).
15+
2. **Get Credentials**:
16+
* Copy the **Client ID** and **Client Secret**.
17+
3. **Configure Environment Variables**:
18+
* Set the following environment variables in your terminal before running the sample:
19+
```bash
20+
export GITHUB_CLIENT_ID="your_actual_client_id"
21+
export GITHUB_CLIENT_SECRET="your_actual_client_secret"
22+
```
23+
* Alternatively, you can create a `.env` file in the sample directory (`contributing/workflow_samples/auth_oauth/.env`) with the following content:
24+
```env
25+
GITHUB_CLIENT_ID="your_actual_client_id"
26+
GITHUB_CLIENT_SECRET="your_actual_client_secret"
27+
```
28+
The ADK CLI automatically loads `.env` files from the agent directory.
29+
30+
## Sample Inputs
31+
32+
- "start"
33+
- "list my repos"
34+
35+
## Graph
36+
37+
```mermaid
38+
graph TD
39+
START --> list_github_repos
40+
list_github_repos --> display_result
41+
```
42+
43+
## How To
44+
45+
### 1. Define the AuthConfig for GitHub
46+
47+
We define an `AuthConfig` that specifies the GitHub OAuth2 endpoints and reads credentials from environment variables.
48+
49+
```python
50+
auth_config = AuthConfig(
51+
auth_scheme=OAuth2(
52+
flows=OAuthFlows(
53+
authorizationCode=OAuthFlowAuthorizationCode(
54+
authorizationUrl="https://github.com/login/oauth/authorize",
55+
tokenUrl="https://github.com/login/oauth/access_token",
56+
scopes={
57+
"user": "Read user profile",
58+
"repo": "Access public repositories",
59+
},
60+
)
61+
)
62+
),
63+
raw_auth_credential=AuthCredential(
64+
auth_type=AuthCredentialTypes.OAUTH2,
65+
oauth2=OAuth2Auth(
66+
client_id=os.environ.get("GITHUB_CLIENT_ID", "YOUR_GITHUB_CLIENT_ID"),
67+
client_secret=os.environ.get("GITHUB_CLIENT_SECRET", "YOUR_GITHUB_CLIENT_SECRET"),
68+
),
69+
),
70+
credential_key="github_oauth_token",
71+
)
72+
```
73+
74+
### 2. Apply to a Node
75+
76+
We apply the `auth_config` to the `list_github_repos` node.
77+
78+
```python
79+
@node(auth_config=auth_config, rerun_on_resume=True)
80+
def list_github_repos(ctx: Context):
81+
# ...
82+
```
83+
84+
### 3. Call GitHub API
85+
86+
Inside the node, we retrieve the token and use the `requests` library to call the GitHub API.
87+
88+
```python
89+
cred = ctx.get_auth_response(auth_config)
90+
access_token = cred.oauth2.access_token if cred and cred.oauth2 else None
91+
92+
# ... (headers setup) ...
93+
94+
response = requests.get("https://api.github.com/user/repos", headers=headers)
95+
repos_data = response.json()
96+
repo_names = [repo["name"] for repo in repos_data]
97+
```
98+
99+
## Running the Sample
100+
101+
To run this sample interactively, use the ADK CLI:
102+
103+
```bash
104+
adk run contributing/workflow_samples/auth_oauth
105+
```
106+
107+
Or use the Web UI:
108+
109+
```bash
110+
adk web contributing/workflow_samples/
111+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import agent
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""OAuth Authentication sample: FunctionNode with GitHub OAuth2 token request.
16+
17+
Demonstrates how to use `auth_config` with GitHub OAuth2 on a FunctionNode to pause
18+
the workflow, request an OAuth token from the user, and use it to list the user's
19+
GitHub repositories.
20+
21+
Flow:
22+
1. User sends any message to start the workflow.
23+
2. The `list_github_repos` node pauses and requests GitHub OAuth credentials.
24+
3. The user provides the credentials (after logging in to GitHub).
25+
4. The node runs, calls the GitHub API to list repos, and returns the list.
26+
5. The `display_result` node displays the repository names.
27+
28+
Sample queries:
29+
- "start"
30+
- "list my repos"
31+
"""
32+
33+
import os
34+
from fastapi.openapi.models import OAuth2
35+
from fastapi.openapi.models import OAuthFlowAuthorizationCode
36+
from fastapi.openapi.models import OAuthFlows
37+
from google.adk import Event
38+
from google.adk import Workflow
39+
from google.adk.agents.context import Context
40+
from google.adk.auth.auth_credential import AuthCredential
41+
from google.adk.auth.auth_credential import AuthCredentialTypes
42+
from google.adk.auth.auth_credential import OAuth2Auth
43+
from google.adk.auth.auth_tool import AuthConfig
44+
from google.adk.workflow import node
45+
import requests
46+
47+
# --- Auth configuration ---
48+
# Uses GitHub OAuth2 authorization code flow.
49+
# To use this sample, you need to register an OAuth application on GitHub
50+
# and get a Client ID and Client Secret.
51+
# Set the GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET environment variables.
52+
auth_config = AuthConfig(
53+
auth_scheme=OAuth2(
54+
flows=OAuthFlows(
55+
authorizationCode=OAuthFlowAuthorizationCode(
56+
authorizationUrl="https://github.com/login/oauth/authorize",
57+
tokenUrl="https://github.com/login/oauth/access_token",
58+
scopes={
59+
"user": "Read user profile",
60+
"repo": "Access public repositories",
61+
},
62+
)
63+
)
64+
),
65+
raw_auth_credential=AuthCredential(
66+
auth_type=AuthCredentialTypes.OAUTH2,
67+
oauth2=OAuth2Auth(
68+
client_id=os.environ.get(
69+
"GITHUB_CLIENT_ID", "YOUR_GITHUB_CLIENT_ID"
70+
),
71+
client_secret=os.environ.get(
72+
"GITHUB_CLIENT_SECRET", "YOUR_GITHUB_CLIENT_SECRET"
73+
),
74+
),
75+
),
76+
credential_key="github_oauth_token",
77+
)
78+
79+
80+
@node(auth_config=auth_config, rerun_on_resume=True)
81+
def list_github_repos(ctx: Context):
82+
"""Fetches GitHub repositories for the authenticated user."""
83+
# After auth completes, the credential is available via ctx.
84+
cred = ctx.get_auth_response(auth_config)
85+
86+
access_token = (
87+
cred.oauth2.access_token if cred and cred.oauth2 else None
88+
)
89+
90+
if not access_token:
91+
return {"status": "Error", "message": "No access token found"}
92+
93+
# GitHub API requires a User-Agent header
94+
headers = {
95+
"Authorization": f"Bearer {access_token}",
96+
"User-Agent": "ADK-Sample-Agent",
97+
"Accept": "application/json",
98+
}
99+
100+
try:
101+
response = requests.get(
102+
"https://api.github.com/user/repos", headers=headers
103+
)
104+
response.raise_for_status()
105+
repos_data = response.json()
106+
# Extract repo names
107+
repo_names = [repo["name"] for repo in repos_data]
108+
return {
109+
"status": "Success",
110+
"repos": repo_names,
111+
}
112+
except Exception as e:
113+
return {
114+
"status": "Error",
115+
"message": f"Failed to fetch repos: {e}",
116+
}
117+
118+
119+
def display_result(node_input: dict):
120+
"""Displays the result of accessing the resource."""
121+
if node_input["status"] == "Success":
122+
repos_str = ", ".join(node_input["repos"])
123+
yield Event(message=f"Successfully fetched repositories: {repos_str}")
124+
else:
125+
yield Event(
126+
message=f"Failed to fetch repositories. Error: {node_input.get('message', 'Unknown error')}"
127+
)
128+
129+
130+
root_agent = Workflow(
131+
name="auth_oauth",
132+
edges=[("START", list_github_repos, display_result)],
133+
)

0 commit comments

Comments
 (0)