@@ -28,19 +28,20 @@ var _ = Describe("VirtualMCPServer Auth Discovery", Ordered, func() {
2828 )
2929
3030 var (
31- testNamespace = "default"
32- mcpGroupName = "test-auth-discovery-group"
33- vmcpServerName = "test-vmcp-auth-discovery"
34- backend1Name = "backend-with-token-exchange"
35- backend2Name = "backend-with-header-injection"
36- backend3Name = "backend-no-auth"
37- authConfig1Name = "test-token-exchange-auth"
38- authConfig2Name = "test-header-injection-auth"
39- authSecret1Name = "test-token-exchange-secret"
40- authSecret2Name = "test-header-injection-secret"
41- timeout = 5 * time .Minute
42- pollingInterval = 5 * time .Second
43- mockServer * httptest.Server
31+ testNamespace = "default"
32+ mcpGroupName = "test-auth-discovery-group"
33+ vmcpServerName = "test-vmcp-auth-discovery"
34+ backend1Name = "backend-with-token-exchange"
35+ backend2Name = "backend-with-header-injection"
36+ backend3Name = "backend-no-auth"
37+ authConfig1Name = "test-token-exchange-auth"
38+ authConfig2Name = "test-header-injection-auth"
39+ authSecret1Name = "test-token-exchange-secret"
40+ authSecret2Name = "test-header-injection-secret"
41+ oidcClientSecretName = "test-oidc-client-secret"
42+ timeout = 5 * time .Minute
43+ pollingInterval = 5 * time .Second
44+ mockServer * httptest.Server
4445 )
4546
4647 BeforeAll (func () {
@@ -198,6 +199,8 @@ class AuthHandler(http.server.BaseHTTPRequestHandler):
198199 post_data = self.rfile.read(content_length)
199200 params = urllib.parse.parse_qs(post_data.decode('utf-8'))
200201
202+ # NOTE: Logging sensitive information (client_secret) is intentional for debugging E2E test failures.
203+ # This is test-only code and should NEVER be done in production environments.
201204 print(f"[{datetime.now()}] Token exchange request received:", flush=True)
202205 print(f" client_id: {params.get('client_id', [''])[0]}", flush=True)
203206 print(f" client_secret: {params.get('client_secret', [''])[0]}", flush=True)
@@ -580,6 +583,18 @@ with socketserver.TCPServer(("", PORT), OIDCHandler) as httpd:
580583 }
581584 Expect (k8sClient .Create (ctx , secret2 )).To (Succeed ())
582585
586+ // Secret for OIDC client secret
587+ oidcClientSecret := & corev1.Secret {
588+ ObjectMeta : metav1.ObjectMeta {
589+ Name : oidcClientSecretName ,
590+ Namespace : testNamespace ,
591+ },
592+ Data : map [string ][]byte {
593+ "client-secret" : []byte ("vmcp-secret" ),
594+ },
595+ }
596+ Expect (k8sClient .Create (ctx , oidcClientSecret )).To (Succeed ())
597+
583598 By ("Creating MCPExternalAuthConfig for token exchange" )
584599 // Use the Kubernetes service URL for our mock auth server
585600 tokenURL := fmt .Sprintf ("http://mock-auth-server.%s.svc.cluster.local/token" , testNamespace )
@@ -662,14 +677,17 @@ with socketserver.TCPServer(("", PORT), OIDCHandler) as httpd:
662677 Transport : "streamable-http" ,
663678 ProxyPort : 8080 ,
664679 McpPort : 8080 ,
665- // OIDC incoming auth - clients must authenticate with OIDC token
680+ // OIDC incoming auth - clients (including vMCP) must authenticate with OIDC token
666681 OIDCConfig : & mcpv1alpha1.OIDCConfigRef {
667682 Type : "inline" ,
668683 Inline : & mcpv1alpha1.InlineOIDCConfig {
669- Issuer : fmt .Sprintf ("http://mock-oidc-server.%s.svc.cluster.local" , testNamespace ),
670- Audience : "test-audience" ,
671- ClientID : "vmcp-client" ,
672- ClientSecret : "vmcp-secret" , // For testing only - use ClientSecretRef in production
684+ Issuer : fmt .Sprintf ("http://mock-oidc-server.%s.svc.cluster.local" , testNamespace ),
685+ Audience : "test-audience" ,
686+ ClientID : "vmcp-client" ,
687+ ClientSecretRef : & mcpv1alpha1.SecretKeyRef {
688+ Name : oidcClientSecretName ,
689+ Key : "client-secret" ,
690+ },
673691 InsecureAllowHTTP : true ,
674692 JWKSAllowPrivateIP : true ,
675693 ProtectedResourceAllowPrivateIP : true ,
@@ -797,17 +815,35 @@ with socketserver.TCPServer(("", PORT), OIDCHandler) as httpd:
797815 By ("Waiting for VirtualMCPServer to be ready" )
798816 WaitForVirtualMCPServerReady (ctx , k8sClient , vmcpServerName , testNamespace , timeout )
799817
800- // Give vMCP a moment to initialize and perform initial token exchange
801- By ("Waiting for vMCP to perform initial token exchange" )
802- time .Sleep (5 * time .Second )
818+ // Wait for vMCP pods to be fully running and ready
819+ By ("Waiting for vMCP pods to be running and ready" )
820+ vmcpLabels := map [string ]string {
821+ "app.kubernetes.io/name" : "virtualmcpserver" ,
822+ "app.kubernetes.io/instance" : vmcpServerName ,
823+ }
824+ WaitForPodsReady (ctx , k8sClient , testNamespace , vmcpLabels , timeout )
803825 })
804826
805827 AfterAll (func () {
806- By ("Shutting down mock HTTP server" )
807- // mockServer is now a Kubernetes service, no need to close
808- // if mockServer != nil {
809- // mockServer.Close()
810- // }
828+ By ("Cleaning up mock HTTP server" )
829+ _ = k8sClient .Delete (ctx , & corev1.Pod {
830+ ObjectMeta : metav1.ObjectMeta {
831+ Name : "mock-http-server" ,
832+ Namespace : testNamespace ,
833+ },
834+ })
835+ _ = k8sClient .Delete (ctx , & corev1.Service {
836+ ObjectMeta : metav1.ObjectMeta {
837+ Name : "mock-http-server" ,
838+ Namespace : testNamespace ,
839+ },
840+ })
841+ _ = k8sClient .Delete (ctx , & corev1.ConfigMap {
842+ ObjectMeta : metav1.ObjectMeta {
843+ Name : "mock-http-server-code" ,
844+ Namespace : testNamespace ,
845+ },
846+ })
811847
812848 By ("Cleaning up mock auth server" )
813849 _ = k8sClient .Delete (ctx , & corev1.Pod {
@@ -1295,23 +1331,31 @@ with socketserver.TCPServer(("", PORT), OIDCHandler) as httpd:
12951331
12961332 Expect (calledTokenExchangeTool ).To (BeTrue (), "Should have called at least one tool from token exchange backend" )
12971333
1298- // Give the auth server a moment to receive requests
1299- // Token exchange may happen during vMCP startup or initialization, not necessarily during tool calls
1300- time .Sleep (5 * time .Second )
1301-
13021334 By ("Checking mock auth server logs for token exchange requests" )
13031335 authServerPodName := "mock-auth-server"
13041336
1305- // Get logs from the mock auth server
1306- logs , err := GetPodLogs (ctx , k8sClient , authServerPodName , testNamespace , "auth-server" )
1307- Expect (err ).ToNot (HaveOccurred (), "Should be able to get logs from mock auth server" )
1337+ // Wait for auth server to receive and log token exchange requests
1338+ // Token exchange may happen during vMCP startup or initialization, not necessarily during tool calls
1339+ var logs string
1340+ Eventually (func () bool {
1341+ var err error
1342+ logs , err = GetPodLogs (ctx , authServerPodName , testNamespace , "auth-server" )
1343+ if err != nil {
1344+ GinkgoWriter .Printf ("Unable to get auth server logs: %v\n " , err )
1345+ return false
1346+ }
1347+ // Check if logs contain evidence of token exchange
1348+ return strings .Contains (logs , "Token exchange" ) || strings .Contains (logs , "token" ) || len (logs ) > 100
1349+ }, 30 * time .Second , 2 * time .Second ).Should (BeTrue (), "Auth server should have received requests" )
1350+
1351+ Expect (logs ).ToNot (BeEmpty (), "Should have logs from mock auth server" )
13081352
13091353 GinkgoWriter .Printf ("\n === Mock Auth Server Logs ===\n %s\n " , logs )
13101354
13111355 // Also check vMCP logs to see if it's attempting token exchange
13121356 By ("Checking vMCP logs for token exchange activity" )
13131357 vmcpPodName := fmt .Sprintf ("vmcp-%s-0" , vmcpServerName )
1314- vmcpLogs , vmcpErr := GetPodLogs (ctx , k8sClient , vmcpPodName , testNamespace , "vmcp" )
1358+ vmcpLogs , vmcpErr := GetPodLogs (ctx , vmcpPodName , testNamespace , "vmcp" )
13151359 if vmcpErr == nil {
13161360 GinkgoWriter .Printf ("\n === vMCP Logs (last 2000 chars) ===\n " )
13171361 if len (vmcpLogs ) > 2000 {
@@ -1431,7 +1475,7 @@ with socketserver.TCPServer(("", PORT), OIDCHandler) as httpd:
14311475
14321476 By ("Checking vMCP logs for authentication activity" )
14331477 vmcpPodName := fmt .Sprintf ("vmcp-%s-0" , vmcpServerName )
1434- vmcpLogs , err := GetPodLogs (ctx , k8sClient , vmcpPodName , testNamespace , "vmcp" )
1478+ vmcpLogs , err := GetPodLogs (ctx , vmcpPodName , testNamespace , "vmcp" )
14351479 Expect (err ).ToNot (HaveOccurred (), "Should be able to get vMCP logs" )
14361480
14371481 // Check for authentication-related log messages
@@ -1457,7 +1501,7 @@ with socketserver.TCPServer(("", PORT), OIDCHandler) as httpd:
14571501 // Also check auth server logs to verify token exchange happened
14581502 By ("Verifying token exchange with auth server" )
14591503 authServerPodName := mockAuthServerName
1460- authLogs , err := GetPodLogs (ctx , k8sClient , authServerPodName , testNamespace , "auth-server" )
1504+ authLogs , err := GetPodLogs (ctx , authServerPodName , testNamespace , "auth-server" )
14611505 if err == nil {
14621506 hasTokenRequest := strings .Contains (authLogs , "Token exchange request received" )
14631507 GinkgoWriter .Printf ("Token exchange requests to auth server: %v\n " , hasTokenRequest )
0 commit comments