package middleware import ( "Noooste/garage-ui/internal/auth" "Noooste/garage-ui/internal/config" "Noooste/garage-ui/internal/models" "github.com/gofiber/fiber/v3" ) // AuthMiddleware returns a Fiber middleware for authentication // It handles different auth modes: none, basic, and OIDC func AuthMiddleware(cfg *config.AuthConfig, authService *auth.AuthService) fiber.Handler { return func(c fiber.Ctx) error { // If auth mode is "none", allow all requests if cfg.Mode == "none" { return c.Next() } // Handle basic authentication if cfg.Mode == "basic" { return handleBasicAuth(c, authService) } // Handle OIDC authentication if cfg.Mode == "oidc" { return handleOIDCAuth(c, authService, &cfg.OIDC) } // Unknown auth mode - deny access return c.Status(fiber.StatusUnauthorized).JSON( models.ErrorResponse(models.ErrCodeUnauthorized, "Invalid authentication mode"), ) } } // handleBasicAuth validates basic authentication credentials func handleBasicAuth(c fiber.Ctx, authService *auth.AuthService) error { // Get Authorization header authHeader := c.Get("Authorization") if authHeader == "" { c.Set("WWW-Authenticate", `Basic realm="Restricted"`) return c.Status(fiber.StatusUnauthorized).JSON( models.ErrorResponse(models.ErrCodeUnauthorized, "Authorization header required"), ) } // Parse basic auth credentials username, password, ok := auth.ParseBasicAuth(authHeader) if !ok { c.Set("WWW-Authenticate", `Basic realm="Restricted"`) return c.Status(fiber.StatusUnauthorized).JSON( models.ErrorResponse(models.ErrCodeUnauthorized, "Invalid Authorization header format"), ) } // Validate credentials if !authService.ValidateBasicAuth(username, password) { c.Set("WWW-Authenticate", `Basic realm="Restricted"`) return c.Status(fiber.StatusUnauthorized).JSON( models.ErrorResponse(models.ErrCodeUnauthorized, "Invalid credentials"), ) } // Store username in context for later use c.Locals("username", username) return c.Next() } // handleOIDCAuth validates OIDC session/token func handleOIDCAuth(c fiber.Ctx, authService *auth.AuthService, oidcCfg *config.OIDCConfig) error { // Get session cookie sessionCookie := c.Cookies(oidcCfg.CookieName) if sessionCookie == "" { return c.Status(fiber.StatusUnauthorized).JSON( models.ErrorResponse(models.ErrCodeUnauthorized, "Authentication required"), ) } // Validate JWT session token userInfo, err := authService.ValidateSessionToken(sessionCookie) if err != nil { return c.Status(fiber.StatusUnauthorized).JSON( models.ErrorResponse(models.ErrCodeUnauthorized, "Invalid or expired session"), ) } // Store user info in context for handlers to use c.Locals("userInfo", userInfo) c.Locals("username", userInfo.Username) c.Locals("email", userInfo.Email) return c.Next() } func RequireAuth(cfg *config.AuthConfig) fiber.Handler { return func(c fiber.Ctx) error { if cfg.Mode == "none" { return c.Status(fiber.StatusForbidden).JSON( models.ErrorResponse(models.ErrCodeForbidden, "Authentication is required but not configured"), ) } return c.Next() } } func RequireAdmin(authService *auth.AuthService) fiber.Handler { return func(c fiber.Ctx) error { userInfoInterface := c.Locals("userInfo") if userInfoInterface == nil { return c.Status(fiber.StatusForbidden).JSON( models.ErrorResponse(models.ErrCodeForbidden, "Admin access required"), ) } userInfo, ok := userInfoInterface.(*auth.UserInfo) if !ok { return c.Status(fiber.StatusForbidden).JSON( models.ErrorResponse(models.ErrCodeForbidden, "Admin access required"), ) } // Check if user has admin role if !authService.IsAdmin(userInfo) { return c.Status(fiber.StatusForbidden).JSON( models.ErrorResponse(models.ErrCodeForbidden, "Admin role required"), ) } return c.Next() } }