Skip to content

Commit 112a19b

Browse files
authored
Merge pull request #302 from zhxie/deploy-oauth
feat: deploy agent with authentication
2 parents 71ef584 + 734777c commit 112a19b

File tree

14 files changed

+527
-31
lines changed

14 files changed

+527
-31
lines changed

docs/content/9.deploy/1.from-scratch.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,19 @@ Run python `deploy.py` for deployment on Volcengine FaaS platform.
3131
它会提示你输入如下几个参数:
3232

3333
- 本地项目名称(即项目的本地目录名称)
34-
- VeFaaS应用名称(不可带下划线)
35-
- 火山引擎网关实例名称(可选)
36-
- 火山引擎网关服务名称(可选)
37-
- 火山引擎网关Upstream名称(可选)
34+
- 火山引擎函数服务应用名称(不可带下划线)
35+
- 火山引擎API网关实例名称(可选)
36+
- 火山引擎API网关服务名称(可选)
37+
- 火山引擎API网关Upstream名称(可选)
3838
- 部署模式
3939
1. A2A / MCP Server
4040
2. VeADK Web
41+
- 认证方式
42+
1. 不认证
43+
2. API key(仅限A2A / MCP Server的部署模式)
44+
3. OAuth2
45+
- 火山引擎Identity用户池名称(可选)
46+
- 火山引擎Identity客户端名称(可选,仅限VeADK Web的部署模式)
4147

4248
生成后的项目结构如下:
4349

docs/content/9.deploy/2.from-proj.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ navigation:
2121
| - | - | - |
2222
| `--access-key` | 字符串 | 火山引擎AK |
2323
| `--secret-key` | 字符串 | 火山引擎SK |
24-
| `--vefaas-app-name` | 字符串 | 火山引擎 VeFaaS 平台应用名称 |
25-
| `--veapig-instance-name` | 字符串 | 火山引擎 APIG 实例名称 |
26-
| `--veapig-service-name` | 字符串 | 火山引擎 APIG 服务名称 |
27-
| `--veapig-upstream-name` | 字符串 | 火山引擎 APIG Upstream 名称 |
24+
| `--vefaas-app-name` | 字符串 | 火山引擎函数服务应用名称 |
25+
| `--veapig-instance-name` | 字符串 | 火山引擎 API 网关实例名称 |
26+
| `--veapig-service-name` | 字符串 | 火山引擎 API 网关服务名称 |
27+
| `--veapig-upstream-name` | 字符串 | 火山引擎 API 网关 Upstream 名称 |
2828
| `--short-term-memory-backend` | `local` \| `mysql` | 短期记忆后端 |
2929
| `--use-adk-web` | FLAG | 设置后将会在云端启动 web,否则为 A2A / MCP 模式 |
3030
| `--path` | 字符串 | 本地项目路径,默认为当前目录 |
31+
| `--auth-method` | `none` \| `api-key` \| `oauth2` | 认证方式 |
32+
| `--user-pool-name` | 字符串 | 火山引擎 Identity 用户池名称 |
33+
| `--client-name` | 字符串 | 火山引擎 Identity 客户端名称 |

docs/content/9.deploy/3.from-code.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ cloud_app = engine.deploy(...)
2828
| 参数名称 | 类型 | 说明 |
2929
| --- | --- | --- |
3030
| path | str | 本地Agent项目路径 |
31-
| application_name | str | 云应用名称 |
32-
| gateway_name | str | 火山引擎网关实例名称 |
33-
| gateway_service_name | str | 火山引擎网关服务名称 |
34-
| gateway_upstream_name | str | 火山引擎网关Upstream名称 |
31+
| application_name | str | 函数服务应用名称 |
32+
| gateway_name | str | 火山引擎 API 网关实例名称 |
33+
| gateway_service_name | str | 火山引擎 API 网关服务名称 |
34+
| gateway_upstream_name | str | 火山引擎 API 网关Upstream名称 |
3535
| use_adk_web | bool | 是否在云端使用VeADK Web / Google Web |
36+
| auth_method | str | 认证方式 |
37+
| identity_user_pool_name | str | 火山引擎 Identity 用户池名称 |
38+
| identity_client_name | str | 火山引擎 Identity 客户端名称 |
3639

3740
### Cloud App
3841

tests/test_cloud.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ async def test_cloud():
6161
mock_vefaas_service.find_app_id_by_name.return_value = "app-123"
6262
mock_vefaas_service.delete.return_value = None
6363

64+
mock_vefaas_service.get_application_route.return_value = (
65+
"gw-123",
66+
"svc-456",
67+
"route-789",
68+
)
69+
6470
# Test CloudAgentEngine creation and deploy functionality
6571
engine = CloudAgentEngine()
6672

veadk/cli/cli_deploy.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@
5050
help="Backend for short-term memory",
5151
)
5252
@click.option("--use-adk-web", is_flag=True, help="Whether to use ADK Web")
53+
@click.option(
54+
"--auth-method",
55+
default="none",
56+
type=click.Choice(["none", "api-key", "oauth2"]),
57+
help="=Authentication method for agent",
58+
)
59+
@click.option(
60+
"--user-pool-name",
61+
default="",
62+
help="Expected Volcengine Identity user pool name",
63+
)
64+
@click.option(
65+
"--client-name",
66+
default="",
67+
help="Expected Volcengine Identity client name",
68+
)
5369
@click.option("--path", default=".", help="Local project path")
5470
def deploy(
5571
volcengine_access_key: str,
@@ -60,6 +76,9 @@ def deploy(
6076
veapig_upstream_name: str,
6177
short_term_memory_backend: str,
6278
use_adk_web: bool,
79+
auth_method: str,
80+
user_pool_name: str,
81+
client_name: str,
6382
path: str,
6483
) -> None:
6584
"""Deploy a user project to Volcengine FaaS application.
@@ -90,6 +109,10 @@ def deploy(
90109
short_term_memory_backend: Backend type for short-term memory storage.
91110
Choices are 'local' or 'mysql'
92111
use_adk_web: Flag to enable ADK Web interface for the deployed agent
112+
auth_method: Authentication for the agent.
113+
Choices are 'none', 'api-key' or 'oauth2'.
114+
veidentity_user_pool_name: Optional Volcengine Identity user pool name
115+
veidentity_client_name: Optional Volcengine Identity client name
93116
path: Local directory path containing the VeADK project to deploy
94117
95118
Note:
@@ -131,6 +154,9 @@ def deploy(
131154
"veapig_service_name": veapig_service_name,
132155
"veapig_upstream_name": veapig_upstream_name,
133156
"use_adk_web": use_adk_web,
157+
"auth_method": auth_method,
158+
"veidentity_user_pool_name": user_pool_name,
159+
"veidentity_client_name": client_name,
134160
"veadk_version": VERSION,
135161
}
136162

veadk/cli/cli_init.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,49 @@ def _render_prompts() -> dict[str, Any]:
6161
deploy_mode = click.prompt(
6262
"Enter your choice", type=click.Choice(deploy_mode_options.keys())
6363
)
64+
use_adk_web = deploy_mode == "2"
65+
66+
auth_method_options = {
67+
"1": "None",
68+
"2": "API key",
69+
"3": "OAuth2",
70+
}
71+
auth_methods = {
72+
"1": "none",
73+
"2": "api-key",
74+
"3": "oauth2",
75+
}
76+
77+
click.echo("Choose an authentication method:")
78+
for key, value in auth_method_options.items():
79+
click.echo(f" {key}. {value}")
80+
81+
auth_method_idx = click.prompt(
82+
"Enter your choice", type=click.Choice(auth_method_options.keys())
83+
)
84+
auth_method = auth_methods[auth_method_idx]
85+
86+
veidentity_user_pool_name = ""
87+
veidentity_client_name = ""
88+
if auth_method == "oauth2":
89+
veidentity_user_pool_name = click.prompt(
90+
"Volcengine Identity user pool name", default="", show_default=True
91+
)
92+
93+
if use_adk_web:
94+
veidentity_client_name = click.prompt(
95+
"Volcengine Identity client name", default="", show_default=True
96+
)
6497

6598
return {
6699
"vefaas_application_name": vefaas_application_name,
67100
"veapig_instance_name": veapig_instance_name,
68101
"veapig_service_name": veapig_service_name,
69102
"veapig_upstream_name": veapig_upstream_name,
70-
"use_adk_web": deploy_mode == "2",
103+
"use_adk_web": use_adk_web,
104+
"auth_method": auth_method,
105+
"veidentity_user_pool_name": veidentity_user_pool_name,
106+
"veidentity_client_name": veidentity_client_name,
71107
"veadk_version": VERSION,
72108
}
73109

veadk/cloud/cloud_agent_engine.py

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

15+
import json
1516
import os
1617
import socket
1718
import subprocess
@@ -23,7 +24,9 @@
2324

2425
from veadk.cloud.cloud_app import CloudApp
2526
from veadk.config import getenv, veadk_environments
27+
from veadk.integrations.ve_apig.ve_apig import APIGateway
2628
from veadk.integrations.ve_faas.ve_faas import VeFaaS
29+
from veadk.integrations.ve_identity.identity_client import IdentityClient
2730
from veadk.utils.logger import get_logger
2831
from veadk.utils.misc import formatted_timestamp
2932

@@ -43,6 +46,8 @@ class CloudAgentEngine(BaseModel):
4346
Defaults to VOLCENGINE_SECRET_KEY environment variable.
4447
region (str): Region for Volcengine services. Defaults to "cn-beijing".
4548
_vefaas_service (VeFaaS): Internal VeFaaS client instance, initialized post-creation.
49+
_veapig_service (APIGateway): Internal VeAPIG client instance, initialized post-creation.
50+
_veidentity_service (IdentityClient): Internal Identity client instance, initialized post-creation.
4651
4752
Note:
4853
Credentials must be set via environment variables for default behavior.
@@ -85,6 +90,16 @@ def model_post_init(self, context: Any, /) -> None:
8590
secret_key=self.volcengine_secret_key,
8691
region=self.region,
8792
)
93+
self._veapig_service = APIGateway(
94+
access_key=self.volcengine_access_key,
95+
secret_key=self.volcengine_secret_key,
96+
region=self.region,
97+
)
98+
self._veidentity_service = IdentityClient(
99+
access_key=self.volcengine_access_key,
100+
secret_key=self.volcengine_secret_key,
101+
region=self.region,
102+
)
88103

89104
def _prepare(self, path: str, name: str):
90105
"""Prepares the local project for deployment by validating path and name.
@@ -202,6 +217,9 @@ def deploy(
202217
gateway_service_name: str = "",
203218
gateway_upstream_name: str = "",
204219
use_adk_web: bool = False,
220+
auth_method: str = "none",
221+
identity_user_pool_name: str = "",
222+
identity_client_name: str = "",
205223
local_test: bool = False,
206224
) -> CloudApp:
207225
"""Deploys a local agent project to Volcengine FaaS, creating necessary resources.
@@ -215,6 +233,9 @@ def deploy(
215233
gateway_service_name (str, optional): Custom service name. Defaults to timestamped.
216234
gateway_upstream_name (str, optional): Custom upstream name. Defaults to timestamped.
217235
use_adk_web (bool): Enable ADK Web configuration. Defaults to False.
236+
auth_method (str, optional): Authentication for the agent. Defaults to none.
237+
identity_user_pool_name (str, optional): Custom user pool name. Defaults to timestamped.
238+
identity_client_name (str, optional): Custom client name. Defaults to timestamped.
218239
local_test (bool): Perform FastAPI server test before deploy. Defaults to False.
219240
220241
Returns:
@@ -236,6 +257,10 @@ def deploy(
236257
# prevent deepeval writing operations
237258
veadk_environments["DEEPEVAL_TELEMETRY_OPT_OUT"] = "YES"
238259

260+
enable_key_auth = False
261+
if auth_method == "api-key":
262+
enable_key_auth = True
263+
239264
if use_adk_web:
240265
veadk_environments["USE_ADK_WEB"] = "True"
241266
else:
@@ -254,6 +279,12 @@ def deploy(
254279
gateway_service_name = f"{application_name}-gw-svr-{formatted_timestamp()}"
255280
if not gateway_upstream_name:
256281
gateway_upstream_name = f"{application_name}-gw-us-{formatted_timestamp()}"
282+
if not identity_user_pool_name:
283+
identity_user_pool_name = (
284+
f"{application_name}-id-up-{formatted_timestamp()}"
285+
)
286+
if not identity_client_name:
287+
identity_client_name = f"{application_name}-id-cli-{formatted_timestamp()}"
257288

258289
try:
259290
vefaas_application_url, app_id, function_id = self._vefaas_service.deploy(
@@ -262,9 +293,102 @@ def deploy(
262293
gateway_name=gateway_name,
263294
gateway_service_name=gateway_service_name,
264295
gateway_upstream_name=gateway_upstream_name,
296+
enable_key_auth=enable_key_auth,
265297
)
266298
_ = function_id # for future use
267299

300+
veapig_gateway_id, _, veapig_route_id = (
301+
self._vefaas_service.get_application_route(app_id=app_id)
302+
)
303+
304+
if auth_method == "oauth2":
305+
# Get or create the Identity user pool.
306+
identity_user_pool_id = self._veidentity_service.get_user_pool(
307+
name=identity_user_pool_name,
308+
)
309+
if not identity_user_pool_id:
310+
identity_user_pool_id = self._veidentity_service.create_user_pool(
311+
name=identity_user_pool_name,
312+
)
313+
issuer = f"https://auth.id.{self.region}.volces.com/userpool/{identity_user_pool_id}"
314+
315+
# Create APIG upstream for Identity.
316+
identity_domain = f"auth.id.{self.region}.volces.com"
317+
veapig_identity_upstream_id = (
318+
self._veapig_service.check_domain_upstream_exist(
319+
domain=identity_domain,
320+
port=443,
321+
gateway_id=veapig_gateway_id,
322+
)
323+
)
324+
if not veapig_identity_upstream_id:
325+
veapig_identity_upstream_id = (
326+
self._veapig_service.create_domain_upstream(
327+
domain=f"auth.id.{self.region}.volces.com",
328+
port=443,
329+
is_https=True,
330+
gateway_id=veapig_gateway_id,
331+
upstream_name=f"id-{formatted_timestamp()}",
332+
)
333+
)
334+
335+
# Create plugin binding.
336+
plugin_name = ""
337+
plugin_config = {}
338+
if use_adk_web:
339+
# Get or create the Identity client.
340+
identity_client_id = ""
341+
identity_client_secret = ""
342+
identity_client = self._veidentity_service.get_user_pool_client(
343+
user_pool_uid=identity_user_pool_id,
344+
name=identity_client_name,
345+
)
346+
if identity_client:
347+
identity_client_id = identity_client[0]
348+
identity_client_secret = identity_client[1]
349+
else:
350+
identity_client_id, identity_client_secret = (
351+
self._veidentity_service.create_user_pool_client(
352+
user_pool_uid=identity_user_pool_id,
353+
name=identity_client_name,
354+
client_type="WEB_APPLICATION",
355+
)
356+
)
357+
358+
self._veidentity_service.register_callback_for_user_pool_client(
359+
user_pool_uid=identity_user_pool_id,
360+
client_uid=identity_client_id,
361+
callback_url=f"{vefaas_application_url}/callback",
362+
web_origin=vefaas_application_url,
363+
)
364+
365+
plugin_name = "wasm-oauth2-sso"
366+
plugin_config = {
367+
"AuthorizationUrl": f"{issuer}/authorize",
368+
"UpstreamId": veapig_identity_upstream_id,
369+
"TokenUrl": f"{issuer}/oauth/token",
370+
"RedirectPath": "/callback",
371+
"SignoutPath": "/signout",
372+
"ClientId": identity_client_id,
373+
"ClientSecret": identity_client_secret,
374+
}
375+
else:
376+
plugin_name = "wasm-jwt-auth"
377+
plugin_config = {
378+
"RemoteJwks": {
379+
"UpstreamId": veapig_identity_upstream_id,
380+
"Url": f"{issuer}/keys",
381+
},
382+
"Issuer": issuer,
383+
"ValidateConsumer": False,
384+
}
385+
self._vefaas_service.apig_client.create_plugin_binding(
386+
scope="ROUTE",
387+
target=veapig_route_id,
388+
plugin_name=plugin_name,
389+
plugin_config=json.dumps(plugin_config),
390+
)
391+
268392
return CloudApp(
269393
vefaas_application_name=application_name,
270394
vefaas_endpoint=vefaas_application_url,

0 commit comments

Comments
 (0)