From c08da6fc92b9e5ebf38ed321b76632b1c0e2bed3 Mon Sep 17 00:00:00 2001 From: Jean Jacques Avril Date: Tue, 11 Mar 2025 17:20:39 +0000 Subject: [PATCH] feat: Add API key middleware and update configuration to support API key validation --- .env | 3 +- backend/cmd/api/main.go | 2 +- .../api/middleware/api_key_middleware.go | 35 +++++++++++++++++++ backend/internal/api/routes/router.go | 5 ++- backend/internal/config/config.go | 4 +++ 5 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 backend/internal/api/middleware/api_key_middleware.go diff --git a/.env b/.env index 3016f81..94b0bdc 100644 --- a/.env +++ b/.env @@ -3,4 +3,5 @@ DB_PORT=5432 DB_USER=timetracker DB_PASSWORD=password DB_NAME=timetracker -DB_SSLMODE=disable \ No newline at end of file +DB_SSLMODE=disable +API_KEY= \ No newline at end of file diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go index 691c3e5..d7ae432 100644 --- a/backend/cmd/api/main.go +++ b/backend/cmd/api/main.go @@ -67,7 +67,7 @@ func main() { r.GET("/api", helloHandler) // Setup API routes - routes.SetupRouter(r) + routes.SetupRouter(r, cfg) // Swagger documentation r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/backend/internal/api/middleware/api_key_middleware.go b/backend/internal/api/middleware/api_key_middleware.go new file mode 100644 index 0000000..4a5376a --- /dev/null +++ b/backend/internal/api/middleware/api_key_middleware.go @@ -0,0 +1,35 @@ +package middleware + +import ( + "github.com/gin-gonic/gin" + "github.com/timetracker/backend/internal/api/utils" + "github.com/timetracker/backend/internal/config" +) + +// APIKeyMiddleware checks for a valid API key if configured +func APIKeyMiddleware(cfg *config.Config) gin.HandlerFunc { + return func(c *gin.Context) { + // Skip if no API key is configured + if cfg.APIKey == "" { + c.Next() + return + } + + // Get API key from header + apiKey := c.GetHeader("X-API-Key") + if apiKey == "" { + utils.UnauthorizedResponse(c, "API key is required") + c.Abort() + return + } + + // Validate API key + if apiKey != cfg.APIKey { + utils.UnauthorizedResponse(c, "Invalid API key") + c.Abort() + return + } + + c.Next() + } +} diff --git a/backend/internal/api/routes/router.go b/backend/internal/api/routes/router.go index d8f771b..e6d38dc 100644 --- a/backend/internal/api/routes/router.go +++ b/backend/internal/api/routes/router.go @@ -4,11 +4,14 @@ import ( "github.com/gin-gonic/gin" "github.com/timetracker/backend/internal/api/handlers" "github.com/timetracker/backend/internal/api/middleware" + "github.com/timetracker/backend/internal/config" ) // SetupRouter configures all the routes for the API -func SetupRouter(r *gin.Engine) { +func SetupRouter(r *gin.Engine, cfg *config.Config) { // Create handlers + // Apply API key middleware to all API routes + r.Use(middleware.APIKeyMiddleware(cfg)) userHandler := handlers.NewUserHandler() activityHandler := handlers.NewActivityHandler() companyHandler := handlers.NewCompanyHandler() diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index d911363..52992f1 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -15,6 +15,7 @@ import ( // Config represents the application configuration type Config struct { Database models.DatabaseConfig + APIKey string } // LoadConfig loads configuration from environment variables and .env file @@ -31,6 +32,9 @@ func LoadConfig() (*Config, error) { return nil, fmt.Errorf("failed to load database config: %w", err) } + // Load API key + cfg.APIKey = getEnv("API_KEY", "") + return cfg, nil }