nvidia-nat-a2a 1.4.0a20260131__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nat/meta/pypi.md +36 -0
- nat/plugins/a2a/__init__.py +14 -0
- nat/plugins/a2a/auth/__init__.py +15 -0
- nat/plugins/a2a/auth/credential_service.py +418 -0
- nat/plugins/a2a/cli/__init__.py +15 -0
- nat/plugins/a2a/cli/commands.py +766 -0
- nat/plugins/a2a/client/__init__.py +14 -0
- nat/plugins/a2a/client/client_base.py +354 -0
- nat/plugins/a2a/client/client_config.py +72 -0
- nat/plugins/a2a/client/client_impl.py +324 -0
- nat/plugins/a2a/register.py +23 -0
- nat/plugins/a2a/server/__init__.py +14 -0
- nat/plugins/a2a/server/agent_executor_adapter.py +172 -0
- nat/plugins/a2a/server/front_end_config.py +131 -0
- nat/plugins/a2a/server/front_end_plugin.py +122 -0
- nat/plugins/a2a/server/front_end_plugin_worker.py +306 -0
- nat/plugins/a2a/server/oauth_middleware.py +121 -0
- nat/plugins/a2a/server/register_frontend.py +37 -0
- nvidia_nat_a2a-1.4.0a20260131.dist-info/METADATA +57 -0
- nvidia_nat_a2a-1.4.0a20260131.dist-info/RECORD +24 -0
- nvidia_nat_a2a-1.4.0a20260131.dist-info/WHEEL +5 -0
- nvidia_nat_a2a-1.4.0a20260131.dist-info/entry_points.txt +8 -0
- nvidia_nat_a2a-1.4.0a20260131.dist-info/licenses/LICENSE.md +201 -0
- nvidia_nat_a2a-1.4.0a20260131.dist-info/top_level.txt +1 -0
nat/meta/pypi.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
3
|
+
SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
|
16
|
+
-->
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# NVIDIA NeMo Agent Toolkit A2A Subpackage
|
|
22
|
+
Subpackage for A2A Protocol integration in NeMo Agent toolkit.
|
|
23
|
+
|
|
24
|
+
This package provides A2A (Agent-to-Agent) Protocol functionality, allowing NeMo Agent toolkit workflows to connect to remote A2A agents and invoke their skills as functions. This package includes both the client and server components of the A2A protocol.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
### Client
|
|
28
|
+
- Connect to remote A2A agents via HTTP with JSON-RPC transport
|
|
29
|
+
- Discover agent capabilities through Agent Cards
|
|
30
|
+
- Submit tasks to remote agents with async execution
|
|
31
|
+
|
|
32
|
+
### Server
|
|
33
|
+
- Serve A2A agents via HTTP with JSON-RPC transport
|
|
34
|
+
- Support for A2A agent executor pattern
|
|
35
|
+
|
|
36
|
+
For more information about the NVIDIA NeMo Agent Toolkit, please visit the [NeMo Agent Toolkit GitHub Repo](https://github.com/NVIDIA/NeMo-Agent-Toolkit).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""Authentication support for A2A clients."""
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""Bridge NAT AuthProviderBase to A2A SDK CredentialService."""
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
import logging
|
|
19
|
+
|
|
20
|
+
from a2a.client import ClientCallContext
|
|
21
|
+
from a2a.client import CredentialService
|
|
22
|
+
from a2a.types import AgentCard
|
|
23
|
+
from a2a.types import APIKeySecurityScheme
|
|
24
|
+
from a2a.types import HTTPAuthSecurityScheme
|
|
25
|
+
from a2a.types import OAuth2SecurityScheme
|
|
26
|
+
from a2a.types import OpenIdConnectSecurityScheme
|
|
27
|
+
from a2a.types import SecurityScheme
|
|
28
|
+
from nat.authentication.interfaces import AuthProviderBase
|
|
29
|
+
from nat.builder.context import Context
|
|
30
|
+
from nat.data_models.authentication import AuthResult
|
|
31
|
+
from nat.data_models.authentication import BasicAuthCred
|
|
32
|
+
from nat.data_models.authentication import BearerTokenCred
|
|
33
|
+
from nat.data_models.authentication import CookieCred
|
|
34
|
+
from nat.data_models.authentication import HeaderCred
|
|
35
|
+
from nat.data_models.authentication import QueryCred
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class A2ACredentialService(CredentialService):
|
|
41
|
+
"""
|
|
42
|
+
Adapts NAT AuthProviderBase to A2A SDK CredentialService interface.
|
|
43
|
+
|
|
44
|
+
This class bridges NAT's authentication system with the A2A SDK's authentication
|
|
45
|
+
mechanism, allowing A2A clients to use NAT's auth providers (API Key, OAuth2, etc.)
|
|
46
|
+
to authenticate with A2A agents.
|
|
47
|
+
|
|
48
|
+
The adapter:
|
|
49
|
+
- Calls NAT auth provider to obtain credentials
|
|
50
|
+
- Maps NAT credential types to A2A security scheme requirements
|
|
51
|
+
- Handles token expiration and automatic refresh
|
|
52
|
+
- Supports session-based multi-user authentication
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
auth_provider: NAT authentication provider instance
|
|
56
|
+
agent_card: Agent card containing security scheme definitions
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
auth_provider: AuthProviderBase,
|
|
62
|
+
agent_card: AgentCard | None = None,
|
|
63
|
+
):
|
|
64
|
+
self._auth_provider = auth_provider
|
|
65
|
+
self._agent_card = agent_card
|
|
66
|
+
self._cached_auth_result: AuthResult | None = None
|
|
67
|
+
self._auth_lock = asyncio.Lock()
|
|
68
|
+
|
|
69
|
+
# Validate provider compatibility with agent's security requirements
|
|
70
|
+
self._validate_provider_compatibility()
|
|
71
|
+
|
|
72
|
+
async def get_credentials(
|
|
73
|
+
self,
|
|
74
|
+
security_scheme_name: str,
|
|
75
|
+
context: ClientCallContext | None,
|
|
76
|
+
) -> str | None:
|
|
77
|
+
"""
|
|
78
|
+
Retrieve credentials for a security scheme.
|
|
79
|
+
|
|
80
|
+
This method:
|
|
81
|
+
1. Gets user_id from NAT context
|
|
82
|
+
2. Authenticates via NAT auth provider
|
|
83
|
+
3. Handles token expiration and refresh
|
|
84
|
+
4. Maps credentials to the requested security scheme
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
security_scheme_name: Name of the security scheme from AgentCard
|
|
88
|
+
context: Client call context with optional session information
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Credential string or None if not available
|
|
92
|
+
"""
|
|
93
|
+
# Get user_id from NAT context
|
|
94
|
+
user_id = Context.get().user_id
|
|
95
|
+
|
|
96
|
+
# Authenticate and get credentials from NAT provider
|
|
97
|
+
auth_result = await self._authenticate(user_id)
|
|
98
|
+
|
|
99
|
+
if not auth_result:
|
|
100
|
+
logger.warning("Authentication failed, no credentials available")
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
# Map NAT credentials to A2A format based on security scheme
|
|
104
|
+
credential = self._extract_credential_for_scheme(auth_result, security_scheme_name)
|
|
105
|
+
|
|
106
|
+
if credential:
|
|
107
|
+
logger.debug(
|
|
108
|
+
"Successfully retrieved credentials for scheme '%s'",
|
|
109
|
+
security_scheme_name,
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
logger.warning(
|
|
113
|
+
"No compatible credentials found for scheme '%s'",
|
|
114
|
+
security_scheme_name,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return credential
|
|
118
|
+
|
|
119
|
+
async def _authenticate(self, user_id: str | None) -> AuthResult | None:
|
|
120
|
+
"""
|
|
121
|
+
Authenticate and get credentials from NAT auth provider.
|
|
122
|
+
|
|
123
|
+
Handles token expiration by triggering re-authentication if needed.
|
|
124
|
+
Uses a lock to prevent concurrent authentication requests and race conditions.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
user_id: User identifier for authentication
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
AuthResult with credentials or None on failure
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
# Fast path: check cache without lock
|
|
134
|
+
auth_result = self._cached_auth_result
|
|
135
|
+
if auth_result and not auth_result.is_expired():
|
|
136
|
+
return auth_result
|
|
137
|
+
|
|
138
|
+
# Acquire lock to serialize authentication attempts
|
|
139
|
+
async with self._auth_lock:
|
|
140
|
+
# Double-check: another coroutine may have refreshed while we waited for lock
|
|
141
|
+
auth_result = self._cached_auth_result
|
|
142
|
+
if auth_result and not auth_result.is_expired():
|
|
143
|
+
logger.debug("Credentials were refreshed by another coroutine while waiting for lock")
|
|
144
|
+
return auth_result
|
|
145
|
+
|
|
146
|
+
# Log if we're refreshing expired credentials
|
|
147
|
+
if auth_result and auth_result.is_expired():
|
|
148
|
+
logger.info("Cached credentials expired, re-authenticating")
|
|
149
|
+
|
|
150
|
+
# Call NAT auth provider (provider is responsible for token refresh/validity)
|
|
151
|
+
auth_result = await self._auth_provider.authenticate(user_id=user_id)
|
|
152
|
+
|
|
153
|
+
# Cache the result while holding the lock
|
|
154
|
+
self._cached_auth_result = auth_result
|
|
155
|
+
|
|
156
|
+
# Warn if provider returned expired credentials (provider bug)
|
|
157
|
+
if auth_result and auth_result.is_expired():
|
|
158
|
+
logger.warning("Auth provider returned already-expired credentials. "
|
|
159
|
+
"This may indicate a bug in the auth provider's token refresh logic.")
|
|
160
|
+
|
|
161
|
+
return auth_result
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error("Authentication failed: %s", e, exc_info=True)
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
def _extract_credential_for_scheme(self, auth_result: AuthResult, security_scheme_name: str) -> str | None:
|
|
168
|
+
"""
|
|
169
|
+
Extract appropriate credential based on security scheme type.
|
|
170
|
+
|
|
171
|
+
Maps NAT credential types to A2A security scheme requirements:
|
|
172
|
+
- BearerTokenCred -> OAuth2, OIDC, HTTP Bearer
|
|
173
|
+
- HeaderCred -> API Key in header
|
|
174
|
+
- QueryCred -> API Key in query
|
|
175
|
+
- CookieCred -> API Key in cookie
|
|
176
|
+
- BasicAuthCred -> HTTP Basic
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
auth_result: Authentication result containing credentials
|
|
180
|
+
security_scheme_name: Name of the security scheme
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Credential string or None
|
|
184
|
+
"""
|
|
185
|
+
# Get scheme definition from agent card
|
|
186
|
+
scheme_def = self._get_scheme_definition(security_scheme_name)
|
|
187
|
+
|
|
188
|
+
# Try to match NAT credentials to security scheme
|
|
189
|
+
for cred in auth_result.credentials:
|
|
190
|
+
# Check compatibility and extract credential value
|
|
191
|
+
credential_value = None
|
|
192
|
+
|
|
193
|
+
if isinstance(cred, BearerTokenCred) and self._is_bearer_compatible(scheme_def):
|
|
194
|
+
credential_value = cred.token.get_secret_value()
|
|
195
|
+
elif isinstance(cred, HeaderCred) and self._is_header_compatible(scheme_def, cred.name):
|
|
196
|
+
credential_value = cred.value.get_secret_value()
|
|
197
|
+
elif isinstance(cred, QueryCred) and self._is_query_compatible(scheme_def, cred.name):
|
|
198
|
+
credential_value = cred.value.get_secret_value()
|
|
199
|
+
elif isinstance(cred, CookieCred) and self._is_cookie_compatible(scheme_def, cred.name):
|
|
200
|
+
credential_value = cred.value.get_secret_value()
|
|
201
|
+
elif isinstance(cred, BasicAuthCred) and self._is_basic_compatible(scheme_def):
|
|
202
|
+
# For HTTP Basic, encode username:password as base64
|
|
203
|
+
import base64
|
|
204
|
+
|
|
205
|
+
username = cred.username.get_secret_value()
|
|
206
|
+
password = cred.password.get_secret_value()
|
|
207
|
+
credentials = f"{username}:{password}"
|
|
208
|
+
credential_value = base64.b64encode(credentials.encode()).decode()
|
|
209
|
+
|
|
210
|
+
if credential_value:
|
|
211
|
+
return credential_value
|
|
212
|
+
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
def _get_scheme_definition(self, scheme_name: str) -> SecurityScheme | None:
|
|
216
|
+
"""
|
|
217
|
+
Get security scheme definition from agent card.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
scheme_name: Name of the security scheme
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
SecurityScheme definition or None
|
|
224
|
+
"""
|
|
225
|
+
if not self._agent_card or not self._agent_card.security_schemes:
|
|
226
|
+
return None
|
|
227
|
+
return self._agent_card.security_schemes.get(scheme_name)
|
|
228
|
+
|
|
229
|
+
def _validate_provider_compatibility(self) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Validate that the auth provider type is compatible with agent's security schemes.
|
|
232
|
+
|
|
233
|
+
This performs early validation at connection time to fail fast if there's a
|
|
234
|
+
configuration mismatch between the NAT auth provider and the A2A agent's
|
|
235
|
+
security requirements.
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
ValueError: If the provider is incompatible with all required security schemes
|
|
239
|
+
"""
|
|
240
|
+
if not self._agent_card or not self._agent_card.security_schemes:
|
|
241
|
+
# No security schemes defined, nothing to validate
|
|
242
|
+
logger.debug("No security schemes defined in agent card, skipping validation")
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
provider_type = type(self._auth_provider).__name__
|
|
246
|
+
schemes = self._agent_card.security_schemes
|
|
247
|
+
|
|
248
|
+
logger.info("Validating auth provider '%s' against agent security schemes: %s",
|
|
249
|
+
provider_type,
|
|
250
|
+
list(schemes.keys()))
|
|
251
|
+
|
|
252
|
+
# Check if provider type is compatible with at least one security scheme
|
|
253
|
+
compatible_schemes = []
|
|
254
|
+
incompatible_schemes = []
|
|
255
|
+
|
|
256
|
+
for scheme_name, scheme in schemes.items():
|
|
257
|
+
is_compatible = self._is_provider_compatible_with_scheme(scheme)
|
|
258
|
+
if is_compatible:
|
|
259
|
+
compatible_schemes.append(scheme_name)
|
|
260
|
+
else:
|
|
261
|
+
incompatible_schemes.append((scheme_name, type(scheme.root).__name__))
|
|
262
|
+
|
|
263
|
+
if not compatible_schemes:
|
|
264
|
+
# Provider is not compatible with any security scheme
|
|
265
|
+
scheme_details = ", ".join(f"{name} ({scheme_type})" for name, scheme_type in incompatible_schemes)
|
|
266
|
+
raise ValueError(f"Auth provider '{provider_type}' is not compatible with agent's "
|
|
267
|
+
f"security requirements. Agent requires: {scheme_details}")
|
|
268
|
+
|
|
269
|
+
logger.info("Auth provider '%s' is compatible with schemes: %s", provider_type, compatible_schemes)
|
|
270
|
+
|
|
271
|
+
def _is_provider_compatible_with_scheme(self, scheme: SecurityScheme) -> bool:
|
|
272
|
+
"""
|
|
273
|
+
Check if the current auth provider can satisfy a security scheme.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
scheme: Security scheme from agent card
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
True if provider is compatible with the scheme
|
|
280
|
+
"""
|
|
281
|
+
provider_type = type(self._auth_provider).__name__
|
|
282
|
+
|
|
283
|
+
# OAuth2/OIDC schemes require OAuth2 providers
|
|
284
|
+
if isinstance(scheme.root, OAuth2SecurityScheme | OpenIdConnectSecurityScheme):
|
|
285
|
+
return "OAuth2" in provider_type
|
|
286
|
+
|
|
287
|
+
# API Key schemes (can be in header, query, or cookie)
|
|
288
|
+
if isinstance(scheme.root, APIKeySecurityScheme):
|
|
289
|
+
return "APIKey" in provider_type
|
|
290
|
+
|
|
291
|
+
# HTTP Auth schemes (Basic or Bearer)
|
|
292
|
+
if isinstance(scheme.root, HTTPAuthSecurityScheme):
|
|
293
|
+
scheme_lower = scheme.root.scheme.lower()
|
|
294
|
+
if scheme_lower == "basic":
|
|
295
|
+
return "HTTPBasic" in provider_type or "BasicAuth" in provider_type
|
|
296
|
+
elif scheme_lower == "bearer":
|
|
297
|
+
# Bearer can be satisfied by OAuth2 or API Key providers
|
|
298
|
+
return "OAuth2" in provider_type or "APIKey" in provider_type
|
|
299
|
+
|
|
300
|
+
# Unknown or unsupported scheme type
|
|
301
|
+
logger.warning("Unknown security scheme type: %s", type(scheme.root).__name__)
|
|
302
|
+
return False
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def _is_bearer_compatible(scheme_def: SecurityScheme | None) -> bool:
|
|
306
|
+
"""
|
|
307
|
+
Check if security scheme accepts Bearer tokens.
|
|
308
|
+
|
|
309
|
+
Bearer tokens are compatible with:
|
|
310
|
+
- OAuth2SecurityScheme
|
|
311
|
+
- OpenIdConnectSecurityScheme
|
|
312
|
+
- HTTPAuthSecurityScheme with scheme='bearer'
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
scheme_def: Security scheme definition
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
True if Bearer token is compatible
|
|
319
|
+
"""
|
|
320
|
+
if not scheme_def:
|
|
321
|
+
return False
|
|
322
|
+
|
|
323
|
+
# Check for OAuth2 or OIDC schemes
|
|
324
|
+
if isinstance(scheme_def.root, OAuth2SecurityScheme | OpenIdConnectSecurityScheme):
|
|
325
|
+
return True
|
|
326
|
+
|
|
327
|
+
# Check for HTTP Bearer scheme
|
|
328
|
+
if isinstance(scheme_def.root, HTTPAuthSecurityScheme):
|
|
329
|
+
return scheme_def.root.scheme.lower() == "bearer"
|
|
330
|
+
|
|
331
|
+
return False
|
|
332
|
+
|
|
333
|
+
@staticmethod
|
|
334
|
+
def _is_header_compatible(scheme_def: SecurityScheme | None, header_name: str) -> bool:
|
|
335
|
+
"""
|
|
336
|
+
Check if security scheme accepts header-based API keys.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
scheme_def: Security scheme definition
|
|
340
|
+
header_name: Name of the header containing the credential
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
True if header credential is compatible
|
|
344
|
+
"""
|
|
345
|
+
if not scheme_def:
|
|
346
|
+
return False
|
|
347
|
+
|
|
348
|
+
# Check for API Key in header
|
|
349
|
+
if isinstance(scheme_def.root, APIKeySecurityScheme):
|
|
350
|
+
if scheme_def.root.in_ == "header":
|
|
351
|
+
# Match header name (case-insensitive)
|
|
352
|
+
return scheme_def.root.name.lower() == header_name.lower()
|
|
353
|
+
|
|
354
|
+
return False
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
def _is_query_compatible(scheme_def: SecurityScheme | None, param_name: str) -> bool:
|
|
358
|
+
"""
|
|
359
|
+
Check if security scheme accepts query parameter API keys.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
scheme_def: Security scheme definition
|
|
363
|
+
param_name: Name of the query parameter
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
True if query credential is compatible
|
|
367
|
+
"""
|
|
368
|
+
if not scheme_def:
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
# Check for API Key in query
|
|
372
|
+
if isinstance(scheme_def.root, APIKeySecurityScheme):
|
|
373
|
+
if scheme_def.root.in_ == "query":
|
|
374
|
+
return scheme_def.root.name == param_name
|
|
375
|
+
|
|
376
|
+
return False
|
|
377
|
+
|
|
378
|
+
@staticmethod
|
|
379
|
+
def _is_cookie_compatible(scheme_def: SecurityScheme | None, cookie_name: str) -> bool:
|
|
380
|
+
"""
|
|
381
|
+
Check if security scheme accepts cookie-based API keys.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
scheme_def: Security scheme definition
|
|
385
|
+
cookie_name: Name of the cookie
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
True if cookie credential is compatible
|
|
389
|
+
"""
|
|
390
|
+
if not scheme_def:
|
|
391
|
+
return False
|
|
392
|
+
|
|
393
|
+
# Check for API Key in cookie
|
|
394
|
+
if isinstance(scheme_def.root, APIKeySecurityScheme):
|
|
395
|
+
if scheme_def.root.in_ == "cookie":
|
|
396
|
+
return scheme_def.root.name == cookie_name
|
|
397
|
+
|
|
398
|
+
return False
|
|
399
|
+
|
|
400
|
+
@staticmethod
|
|
401
|
+
def _is_basic_compatible(scheme_def: SecurityScheme | None) -> bool:
|
|
402
|
+
"""
|
|
403
|
+
Check if security scheme accepts HTTP Basic authentication.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
scheme_def: Security scheme definition
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
True if Basic auth is compatible
|
|
410
|
+
"""
|
|
411
|
+
if not scheme_def:
|
|
412
|
+
return False
|
|
413
|
+
|
|
414
|
+
# Check for HTTP Basic scheme
|
|
415
|
+
if isinstance(scheme_def.root, HTTPAuthSecurityScheme):
|
|
416
|
+
return scheme_def.root.scheme.lower() == "basic"
|
|
417
|
+
|
|
418
|
+
return False
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""A2A CLI commands."""
|