Skip to content

Commit 21c26f9

Browse files
nikhilpurwantcopybara-github
authored andcommitted
chore: Added ADK Authentication End2End Samples
Merge #2960 1. All in one authentication sample (has an IDP, Agent and the application) under `contributing/samples/authn-adk-all-in-one/` 2. Documented for all the steps. 3. OAuth 2.0 Authorization Code Grant type used by the agent. COPYBARA_INTEGRATE_REVIEW=#2960 from nikhilpurwant:main dfcc821 PiperOrigin-RevId: 808672120
1 parent 2595824 commit 21c26f9

File tree

19 files changed

+2134
-0
lines changed

19 files changed

+2134
-0
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
## ADK Authentication Demo (All in one - Agent, IDP and The app)
2+
3+
This folder contains everything you need to run the ADK's `auth-code`
4+
grant type authentication demo completely locally
5+
6+
Here's the high level diagram.
7+
8+
![alt](doc_images/adk-auth-all-in-one.svg)
9+
10+
### Introduction
11+
More often than not the agents use some kind of system identity
12+
(especially for OpenAPI and MCP tools).
13+
But obviously this is insecure in that multiple end users
14+
are using the same identity with permissions to access ALL users' data on the
15+
backend.
16+
17+
ADK provides various [authentication mechanisms](https://google.github.io/adk-docs/tools/authentication/) to solve this.
18+
19+
However to properly test it you need various components.
20+
We provide everything that is needed so that you can test and run
21+
ADK authentication demo locally.
22+
23+
This folder comes with -
24+
25+
1. An IDP
26+
2. A hotel booking application backend
27+
3. A hotel assistant ADK agent (accessing the application using OpenAPI Tools)
28+
29+
### Details
30+
31+
You can read about the Auth Code grant / flow type in detail [here](https://developer.okta.com/blog/2018/04/10/oauth-authorization-code-grant-type). But for the purpose of this demo, following steps take place
32+
33+
1. The user asks the agent to find hotels in "New York".
34+
2. Agent realizes (based on LLM response) that it needs to call a tool and that the tool needs authentication.
35+
3. Agent redirects the user to the IDP's login page with callback / redirect URL back to ADK UI.
36+
4. The user enters credentials (`john.doe` and `password123`) and accepts the consent.
37+
5. The IDP sends the auth_code back to the redirect URL (from 3).
38+
6. ADK then exchanges this auth_code for an access token.
39+
7. ADK does the API call to get details on hotels and hands over that response to LLM, LLM formats the response.
40+
8. ADK sends a response back to the User.
41+
42+
### Setting up and running
43+
44+
1. Clone this repository
45+
2. Carry out following steps and create and activate the environment
46+
```bash
47+
# Go to the cloned directory
48+
cd adk-python
49+
# Navigate to the all in one authentication sample
50+
cd contributing/samples/authn-adk-all-in-one/
51+
52+
python3 -m venv .venv
53+
54+
. .venv/bin/activate
55+
56+
pip install -r requirements.txt
57+
58+
```
59+
3. Configure and Start the IDP. Our IDP needs a private key to sign the tokens and a JWKS with public key component to verify them. Steps are provided for that (please check the screenshots below)
60+
61+
🪧 **NOTE:**
62+
It is recommended that you execute the key pair creation and public
63+
key extraction commands (1-3 and 5 below) on Google cloud shell.
64+
65+
```bash
66+
cd idp
67+
68+
# Create .env file by copying the existing one.
69+
cp sample.env .env
70+
cp sample.jwks.json jwks.json
71+
72+
73+
# Carry out following steps
74+
# 1. Generate a key pair, When asked about passphrase please press enter (empty passphrase)
75+
ssh-keygen -t rsa -b 2048 -m PEM -f private_key.pem
76+
77+
# 2. Extract the public key
78+
openssl rsa -in private_key.pem -pubout > pubkey.pub
79+
80+
# 3. Generate the jwks.json content using https://jwkset.com/generate and this public key (choose key algorithm RS256 and Key use Signature) (Please check the screenshot)
81+
# 4. Update the jwks.json with the key jwks key created in 3 (please check the screenshot)
82+
# 5. Update the env file with the private key
83+
cat private_key.pem | tr -d "\n"
84+
# 6. Carefully copy output of the command above into the .env file to update the value of PRIVATE_KEY
85+
# 7. save jwks.json and .env
86+
87+
# Start the IDP
88+
python app.py
89+
```
90+
<details>
91+
92+
<summary><b>Screenshots</b></summary>
93+
Generating JWKS -
94+
95+
![alt](doc_images/jwksgen.png)
96+
97+
Updated `jwks.json` (notice the key is added in the existing array)
98+
99+
![alt](doc_images/jwks_updated.png)
100+
101+
</details>
102+
103+
4. In a separate shell - Start the backend API (Hotel Booking Application)
104+
```bash
105+
# Go to the cloned directory
106+
cd adk-python
107+
# Navigate to the all in one authentication sample
108+
cd contributing/samples/authn-adk-all-in-one/
109+
110+
# Activate Env for this shell
111+
. .venv/bin/activate
112+
113+
cd hotel_booker_app/
114+
115+
# Start the hotel booker application
116+
python main.py
117+
118+
```
119+
120+
5. In a separate shell - Start the ADK agent
121+
```bash
122+
# Go to the cloned directory
123+
cd adk-python
124+
# Navigate to the all in one authentication sample
125+
cd contributing/samples/authn-adk-all-in-one/
126+
127+
# Activate Env for this shell
128+
. .venv/bin/activate
129+
130+
cd adk_agents/
131+
132+
cp sample.env .env
133+
134+
# ⚠️ Make sure to update the API KEY (GOOGLE_API_KEY) in .env file
135+
136+
# Run the agent
137+
adk web
138+
139+
```
140+
6. Access the agent on http://localhost:8000
141+
142+
🪧 **NOTE:**
143+
144+
After first time authentication,
145+
it might take some time for the agent to respond,
146+
subsequent responses are significantly faster.
147+
148+
### Conclusion
149+
150+
You can exercise the ADK Authentication
151+
without any external components using this demo.
152+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2025 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+
from . import agent
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright 2025 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+
import logging
16+
import os
17+
18+
from google.adk.tools.openapi_tool.auth.auth_helpers import openid_url_to_scheme_credential
19+
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
20+
21+
credential_dict = {
22+
"client_id": os.environ.get("OAUTH_CLIENT_ID"),
23+
"client_secret": os.environ.get("OAUTH_CLIENT_SECRET"),
24+
}
25+
auth_scheme, auth_credential = openid_url_to_scheme_credential(
26+
openid_url="http://localhost:5000/.well-known/openid-configuration",
27+
credential_dict=credential_dict,
28+
scopes=[],
29+
)
30+
31+
32+
# Open API spec
33+
file_path = "./agent_openapi_tools/openapi.yaml"
34+
file_content = None
35+
36+
try:
37+
with open(file_path, "r") as file:
38+
file_content = file.read()
39+
except FileNotFoundError:
40+
# so that the execution does not continue when the file is not found.
41+
raise FileNotFoundError(f"Error: The API Spec '{file_path}' was not found.")
42+
43+
44+
# Example with a JSON string
45+
openapi_spec_yaml = file_content # Your OpenAPI YAML string
46+
openapi_toolset = OpenAPIToolset(
47+
spec_str=openapi_spec_yaml,
48+
spec_str_type="yaml",
49+
auth_scheme=auth_scheme,
50+
auth_credential=auth_credential,
51+
)
52+
53+
from google.adk.agents import LlmAgent
54+
55+
root_agent = LlmAgent(
56+
name="hotel_agent",
57+
instruction=(
58+
"Help user find and book hotels, fetch their bookings using the tools"
59+
" provided."
60+
),
61+
description="Hotel Booking Agent",
62+
model=os.environ.get("GOOGLE_MODEL"),
63+
tools=[openapi_toolset], # Pass the toolset
64+
# ... other agent config ...
65+
)

0 commit comments

Comments
 (0)