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 }