44import logging
55import os
66from datetime import timedelta
7+ from urllib .parse import urljoin
78
89from a2a .auth .user import User
910from async_lru import alru_cache
@@ -76,12 +77,13 @@ def __init__(self, public_url: str | None = None, skip_audience_validation: bool
7677 if skip_audience_validation is not None
7778 else os .getenv ("PLATFORM_AUTH__SKIP_AUDIENCE_VALIDATION" , "false" ).lower () in ("true" , "1" )
7879 )
79- _audience = public_url or os .getenv ("PLATFORM_AUTH__PUBLIC_URL" , "http://host.docker.internal:8333" )
80- if not self .skip_audience_validation and not _audience :
81- raise ValueError (
82- "Public URL must be provided if audience validation is enabled (hint: set PLATFORM_AUTH__PUBLIC_URL env variable)"
80+ self ._audience : str | None = public_url or os .getenv ("PLATFORM_AUTH__PUBLIC_URL" , None )
81+ if not self .skip_audience_validation and not self ._audience :
82+ logger .warning (
83+ "Public URL is not provided and audience validation is enabled. Proceeding to check audience from the request target URL. "
84+ + "This may not work when requests to agents are proxied. (hint: set PLATFORM_AUTH__PUBLIC_URL env variable)"
8385 )
84- self . audience : str = _audience or "skip"
86+
8587 self .security : HTTPBearer = HTTPBearer (auto_error = False )
8688
8789 @override
@@ -95,7 +97,15 @@ async def authenticate(self, conn: HTTPConnection) -> tuple[AuthCredentials, Bas
9597 if not (auth := await self .security (request )):
9698 raise AuthenticationError ("Missing Authorization header" )
9799
100+ audiences : list [str ] = []
101+ if not self .skip_audience_validation :
102+ if self ._audience :
103+ audiences = [urljoin (self ._audience , path ) for path in ["/" , "/jsonrpc" ]]
104+ else :
105+ audiences = [str (request .url .replace (path = path )) for path in ["/" , "/jsonrpc" ]]
106+
98107 try :
108+ # check only hostname urljoin("http://host:port/a/b", "/") -> "http://host:port/"
99109 jwks = await discover_jwks ()
100110
101111 # Verify signature
@@ -107,7 +117,7 @@ async def authenticate(self, conn: HTTPConnection) -> tuple[AuthCredentials, Bas
107117 "exp" : {"essential" : True },
108118 # "iss": {"essential": True}, # Issuer validation might be tricky if internal/external URLs differ
109119 }
110- | ({"aud" : {"essential" : True , "value " : self . audience }} if not self .skip_audience_validation else {}),
120+ | ({"aud" : {"essential" : True , "values " : audiences }} if not self .skip_audience_validation else {}),
111121 )
112122 claims .validate ()
113123
0 commit comments