From 974f2a67f0717adc9ceba56eefcb210d5b5a7f50 Mon Sep 17 00:00:00 2001 From: Pushpinder Singh <53684951+pushpinderbal@users.noreply.github.com> Date: Wed, 31 Dec 2025 04:34:25 -0500 Subject: [PATCH] fix: allow any HTTP method for /api/auth/envoy (#551) * feat: allow any HTTP method for /api/auth/envoy and restrict methods for non-envoy proxies * feat: add Allow header for invalid methods in proxyHandler --- internal/controller/proxy_controller.go | 17 +++++++++++++-- internal/controller/proxy_controller_test.go | 22 +++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/internal/controller/proxy_controller.go b/internal/controller/proxy_controller.go index 40f8374..d6c8beb 100644 --- a/internal/controller/proxy_controller.go +++ b/internal/controller/proxy_controller.go @@ -43,8 +43,8 @@ func NewProxyController(config ProxyControllerConfig, router *gin.RouterGroup, a func (controller *ProxyController) SetupRoutes() { proxyGroup := controller.router.Group("/auth") - proxyGroup.GET("/:proxy", controller.proxyHandler) - proxyGroup.POST("/:proxy", controller.proxyHandler) + // There is a later check to control allowed methods per proxy + proxyGroup.Any("/:proxy", controller.proxyHandler) } func (controller *ProxyController) proxyHandler(c *gin.Context) { @@ -69,6 +69,19 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { return } + // Only allow GET for non-envoy proxies. + // Envoy uses the original client method for the external auth request + // so we allow Any standard HTTP method for /api/auth/envoy + if req.Proxy != "envoy" && c.Request.Method != http.MethodGet { + log.Warn().Str("method", c.Request.Method).Msg("Invalid method for proxy") + c.Header("Allow", "GET") + c.JSON(405, gin.H{ + "status": 405, + "message": "Method Not Allowed", + }) + return + } + isBrowser := strings.Contains(c.Request.Header.Get("Accept"), "text/html") if isBrowser { diff --git a/internal/controller/proxy_controller_test.go b/internal/controller/proxy_controller_test.go index 54bb888..4d409a8 100644 --- a/internal/controller/proxy_controller_test.go +++ b/internal/controller/proxy_controller_test.go @@ -81,6 +81,14 @@ func TestProxyHandler(t *testing.T) { assert.Equal(t, 400, recorder.Code) + // Test invalid method for non-envoy proxy + recorder = httptest.NewRecorder() + req = httptest.NewRequest("POST", "/api/auth/traefik", nil) + router.ServeHTTP(recorder, req) + + assert.Equal(t, 405, recorder.Code) + assert.Equal(t, "GET", recorder.Header().Get("Allow")) + // Test logged out user (traefik/caddy) recorder = httptest.NewRecorder() req = httptest.NewRequest("GET", "/api/auth/traefik", nil) @@ -93,7 +101,7 @@ func TestProxyHandler(t *testing.T) { assert.Equal(t, 307, recorder.Code) assert.Equal(t, "http://localhost:8080/login?redirect_uri=https%3A%2F%2Fexample.com%2Fsomepath", recorder.Header().Get("Location")) - // Test logged out user (envoy) + // Test logged out user (envoy - POST method) recorder = httptest.NewRecorder() req = httptest.NewRequest("POST", "/api/auth/envoy", nil) req.Header.Set("X-Forwarded-Proto", "https") @@ -105,6 +113,18 @@ func TestProxyHandler(t *testing.T) { assert.Equal(t, 307, recorder.Code) assert.Equal(t, "http://localhost:8080/login?redirect_uri=https%3A%2F%2Fexample.com%2Fsomepath", recorder.Header().Get("Location")) + // Test logged out user (envoy - DELETE method) + recorder = httptest.NewRecorder() + req = httptest.NewRequest("DELETE", "/api/auth/envoy", nil) + req.Header.Set("X-Forwarded-Proto", "https") + req.Header.Set("X-Forwarded-Host", "example.com") + req.Header.Set("X-Forwarded-Uri", "/somepath") + req.Header.Set("Accept", "text/html") + router.ServeHTTP(recorder, req) + + assert.Equal(t, 307, recorder.Code) + assert.Equal(t, "http://localhost:8080/login?redirect_uri=https%3A%2F%2Fexample.com%2Fsomepath", recorder.Header().Get("Location")) + // Test logged out user (nginx) recorder = httptest.NewRecorder() req = httptest.NewRequest("GET", "/api/auth/nginx", nil)