@@ -159,112 +159,63 @@ func TestProxyAuthenticator_Authenticate(t *testing.T) {
159159 }
160160}
161161
162- func TestProxyAuthenticator_JWTWithAgentHeader (t * testing.T ) {
162+ func TestProxyAuthenticator_AgentCalls (t * testing.T ) {
163163 tests := []struct {
164164 name string
165- claims map [string ]any
166- agentName string
165+ headers map [string ]string
166+ queryParams map [ string ] string
167167 wantUserID string
168168 wantAgentID string
169+ wantErr bool
169170 }{
170171 {
171- name : "extracts agent identity from header when JWT is present " ,
172- claims : map [string ]any {
173- "sub" : "system:serviceaccount:kagent:kebab -agent" ,
174- "iss " : "https://kubernetes.default.svc.cluster.local " ,
175- "aud " : [] any { "kagent" } ,
172+ name : "agent with SA Bearer token and X-User-Id header uses header identity " ,
173+ headers : map [string ]string {
174+ "Authorization" : "Bearer " + createTestJWT ( map [ string ] any { " sub" : "system:serviceaccount:kagent:test -agent" }) ,
175+ "X-Agent-Name " : "kagent/test-agent " ,
176+ "X-User-Id " : "user@example.com" ,
176177 },
177- agentName : "kagent__NS__kebab_agent" ,
178- wantUserID : "system:serviceaccount:kagent:kebab-agent" ,
179- wantAgentID : "kagent__NS__kebab_agent" ,
178+ wantUserID : "user@example.com" ,
179+ wantAgentID : "kagent/test-agent" ,
180180 },
181181 {
182- name : "works with OIDC JWT and agent header " ,
183- claims : map [string ]any {
184- "sub " : "user123" ,
185- "email " : "user@example.com " ,
182+ name : "agent with SA Bearer token and user_id query param uses query identity " ,
183+ headers : map [string ]string {
184+ "Authorization " : "Bearer " + createTestJWT ( map [ string ] any { "sub" : "system:serviceaccount:kagent:test-agent" }) ,
185+ "X-Agent-Name " : "kagent/test-agent " ,
186186 },
187- agentName : "kagent__NS__my_agent" ,
188- wantUserID : "user123" ,
189- wantAgentID : "kagent__NS__my_agent" ,
190- },
191- {
192- name : "handles JWT without agent header" ,
193- claims : map [string ]any {
194- "sub" : "user123" ,
187+ queryParams : map [string ]string {
188+ "user_id" : "user@example.com" ,
195189 },
196- agentName : "" ,
197- wantUserID : "user123" ,
198- wantAgentID : "" ,
190+ wantUserID : "user@example.com" ,
191+ wantAgentID : "kagent/test-agent" ,
199192 },
200- }
201-
202- for _ , tt := range tests {
203- t .Run (tt .name , func (t * testing.T ) {
204- auth := authimpl .NewProxyAuthenticator ("" )
205-
206- headers := http.Header {}
207- token := createTestJWT (tt .claims )
208- headers .Set ("Authorization" , "Bearer " + token )
209- if tt .agentName != "" {
210- headers .Set ("X-Agent-Name" , tt .agentName )
211- }
212-
213- session , err := auth .Authenticate (context .Background (), headers , url.Values {})
214- if err != nil {
215- t .Fatalf ("unexpected error: %v" , err )
216- }
217-
218- principal := session .Principal ()
219- if principal .User .ID != tt .wantUserID {
220- t .Errorf ("User.ID = %q, want %q" , principal .User .ID , tt .wantUserID )
221- }
222- if principal .Agent .ID != tt .wantAgentID {
223- t .Errorf ("Agent.ID = %q, want %q" , principal .Agent .ID , tt .wantAgentID )
224- }
225- })
226- }
227- }
228-
229- func TestProxyAuthenticator_ServiceAccountFallback (t * testing.T ) {
230- tests := []struct {
231- name string
232- headers map [string ]string
233- queryParams map [string ]string
234- wantUserID string
235- wantAgentID string
236- wantErr bool
237- }{
238193 {
239- name : "authenticates via user_id query param with agent name" ,
240- queryParams : map [string ]string {
241- "user_id" : "system:serviceaccount:kagent:kebab-agent" ,
242- },
194+ name : "agent with no X-User-Id falls back to SA sub claim" ,
243195 headers : map [string ]string {
244- "X-Agent-Name" : "kagent/kebab-agent" ,
196+ "Authorization" : "Bearer " + createTestJWT (map [string ]any {"sub" : "system:serviceaccount:kagent:test-agent" }),
197+ "X-Agent-Name" : "kagent/test-agent" ,
245198 },
246- wantUserID : "system:serviceaccount:kagent:kebab-agent" ,
247- wantAgentID : "kagent/kebab-agent" ,
248- wantErr : false ,
199+ wantUserID : "system:serviceaccount:kagent:test-agent" ,
200+ wantAgentID : "kagent/test-agent" ,
249201 },
202+ // Error cases.
250203 {
251- name : "authenticates via X-User-Id header with agent name " ,
204+ name : "agent without Bearer token is rejected " ,
252205 headers : map [string ]string {
253- "X-User-Id" : "system:serviceaccount:kagent:test-agent" ,
254206 "X-Agent-Name" : "kagent/test-agent" ,
207+ "X-User-Id" : "user@example.com" ,
255208 },
256- wantUserID : "system:serviceaccount:kagent:test-agent" ,
257- wantAgentID : "kagent/test-agent" ,
258- wantErr : false ,
209+ wantErr : true ,
259210 },
260211 {
261- name : "returns error when no auth method available " ,
212+ name : "no token and no X-Agent-Name is rejected " ,
262213 wantErr : true ,
263214 },
264215 {
265- name : "returns error when no X-Agent-Name header for fallback " ,
216+ name : "user_id without X-Agent-Name is rejected " ,
266217 queryParams : map [string ]string {
267- "user_id" : "system:serviceaccount:kagent:kebab-agent " ,
218+ "user_id" : "user@example.com " ,
268219 },
269220 wantErr : true ,
270221 },
@@ -339,4 +290,9 @@ func TestProxyAuthenticator_UpstreamAuth(t *testing.T) {
339290 if got := req .Header .Get ("Authorization" ); got != authHeader {
340291 t .Errorf ("Authorization header = %q, want %q" , got , authHeader )
341292 }
293+
294+ // Verify X-User-Id is forwarded so downstream A2A runtimes receive the real user identity
295+ if got := req .Header .Get ("X-User-Id" ); got != "user123" {
296+ t .Errorf ("X-User-Id header = %q, want %q" , got , "user123" )
297+ }
342298}
0 commit comments