Skip to content

Commit fa39eb5

Browse files
committed
fix: normalize api key scheme in alias
1 parent 12fd75c commit fa39eb5

3 files changed

Lines changed: 80 additions & 1 deletion

File tree

src/a2a/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class APIKeySecurityScheme(A2ABaseModel):
3434
"""
3535
An optional description for the security scheme.
3636
"""
37-
in_: In
37+
in_: In = Field(..., alias='in')
3838
"""
3939
The location of the API key.
4040
"""
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from unittest import mock
2+
3+
import pytest
4+
5+
from httpx import ASGITransport, AsyncClient
6+
7+
from a2a.server.apps.rest.fastapi_app import A2ARESTFastAPIApplication
8+
from a2a.types import (
9+
APIKeySecurityScheme,
10+
AgentCapabilities,
11+
AgentCard,
12+
In,
13+
SecurityScheme,
14+
)
15+
16+
17+
@pytest.fixture
18+
def agent_card_with_api_key() -> AgentCard:
19+
api_key_scheme_data = {
20+
'type': 'apiKey',
21+
'name': 'X-API-KEY',
22+
'in': 'header',
23+
}
24+
api_key_scheme = APIKeySecurityScheme.model_validate(api_key_scheme_data)
25+
26+
return AgentCard(
27+
name='APIKeyAgent',
28+
description='An agent that uses API Key auth.',
29+
url='http://example.com/apikey-agent',
30+
version='1.0.0',
31+
capabilities=AgentCapabilities(),
32+
default_input_modes=['text/plain'],
33+
default_output_modes=['text/plain'],
34+
skills=[],
35+
security_schemes={'api_key_auth': SecurityScheme(root=api_key_scheme)},
36+
security=[{'api_key_auth': []}],
37+
)
38+
39+
40+
@pytest.mark.anyio
41+
async def test_rest_agent_card_with_api_key_scheme_alias(
42+
agent_card_with_api_key: AgentCard,
43+
):
44+
"""Ensures REST agent card serialization uses the 'in' alias."""
45+
handler = mock.AsyncMock()
46+
app_instance = A2ARESTFastAPIApplication(agent_card_with_api_key, handler)
47+
app = app_instance.build(
48+
agent_card_url='/.well-known/agent.json', rpc_url=''
49+
)
50+
51+
async with AsyncClient(
52+
transport=ASGITransport(app=app), base_url='http://test'
53+
) as client:
54+
response = await client.get('/.well-known/agent.json')
55+
56+
assert response.status_code == 200
57+
response_data = response.json()
58+
59+
security_scheme_json = response_data['securitySchemes']['api_key_auth']
60+
assert 'in' in security_scheme_json
61+
assert security_scheme_json['in'] == 'header'
62+
assert 'in_' not in security_scheme_json
63+
64+
parsed_card = AgentCard.model_validate(response_data)
65+
parsed_scheme_wrapper = parsed_card.security_schemes['api_key_auth']
66+
assert parsed_scheme_wrapper.root.in_ == In.header

tests/test_types.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,19 @@ def test_security_scheme_valid():
190190
assert scheme.root.name == 'X-API-KEY'
191191

192192

193+
def test_security_scheme_accepts_in_field_name():
194+
scheme = SecurityScheme.model_validate(
195+
{
196+
'type': 'apiKey',
197+
'in_': 'header',
198+
'name': 'X-API-KEY',
199+
}
200+
)
201+
assert isinstance(scheme.root, APIKeySecurityScheme)
202+
assert scheme.root.in_ == In.header
203+
assert scheme.model_dump(mode='json', exclude_none=True)['in'] == 'header'
204+
205+
193206
def test_security_scheme_invalid():
194207
with pytest.raises(ValidationError):
195208
APIKeySecurityScheme(

0 commit comments

Comments
 (0)