package handler import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/repository" "baron-sso-backend/internal/service" "errors" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) // FederationHandler handles API requests for IdP federation. type FederationHandler struct { fedSvc *service.FederationService repo repository.FederationRepository // For IdP Config CRUD db *gorm.DB // For tenant existence checks, etc. in CRUD } // NewFederationHandler creates a new FederationHandler. func NewFederationHandler(fedSvc *service.FederationService, repo repository.FederationRepository, db *gorm.DB) *FederationHandler { return &FederationHandler{ fedSvc: fedSvc, repo: repo, db: db, } } // InitiateOIDCLogin handles the start of the OIDC login flow. // It expects `provider_id` and `login_challenge` as query parameters. func (h *FederationHandler) InitiateOIDCLogin(c *fiber.Ctx) error { providerID := c.Query("provider_id") loginChallenge := c.Query("login_challenge") if providerID == "" || loginChallenge == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "provider_id and login_challenge are required"}) } redirectURL, err := h.fedSvc.InitiateOIDCLogin(c.Context(), providerID, loginChallenge) if err != nil { // Log the error properly in a real application return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to initiate OIDC login"}) } return c.Redirect(redirectURL, fiber.StatusFound) } // HandleOIDCCallback handles the OIDC callback from the IdP. func (h *FederationHandler) HandleOIDCCallback(c *fiber.Ctx) error { code := c.Query("code") state := c.Query("state") if code == "" || state == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "code and state are required"}) } redirectURL, err := h.fedSvc.HandleOIDCCallback(c.Context(), code, state) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "failed to handle OIDC callback"}) } return c.Redirect(redirectURL, fiber.StatusFound) } // --- New Client-based IdP Config Methods --- // ListIdpConfigsForClient handles listing all IdP configurations for a client. func (h *FederationHandler) ListIdpConfigsForClient(c *fiber.Ctx) error { clientID := c.Params("clientId") if clientID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "clientId is required"}) } var configs []domain.IdentityProviderConfig if err := h.db.Where("client_id = ?", clientID).Find(&configs).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(configs) } // CreateIdpConfigForClient handles the creation of a new IdP configuration for a client. func (h *FederationHandler) CreateIdpConfigForClient(c *fiber.Ctx) error { clientID := c.Params("clientId") if clientID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "clientId is required in path"}) } var req domain.IdentityProviderConfig if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"}) } // Assign clientID from path parameter req.ClientID = clientID // Basic validation if req.DisplayName == "" || req.ProviderType == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "display_name and provider_type are required"}) } // TODO: Optionally, validate if the clientID exists in Hydra // Create in DB if err := h.db.Create(&req).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(fiber.StatusCreated).JSON(req) } // --- Deprecated Tenant-based IdP Config Methods --- // ListIdpConfigsForTenant handles listing all IdP configurations for a tenant. func (h *FederationHandler) ListIdpConfigsForTenant(c *fiber.Ctx) error { tenantID := c.Params("tenantId") if tenantID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenantId is required"}) } // This is a temporary solution. We should create a proper method in the repository. var configs []domain.IdentityProviderConfig // Note: This now queries client_id, which is incorrect for tenants. // This method is deprecated. if err := h.db.Where("tenant_id = ?", tenantID).Find(&configs).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(configs) } // CreateIdpConfig handles the creation of a new IdP configuration. func (h *FederationHandler) CreateIdpConfig(c *fiber.Ctx) error { var req domain.IdentityProviderConfig if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"}) } // Basic validation - This is the old validation logic if req.ClientID == "" || req.DisplayName == "" || req.ProviderType == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "client_id, display_name, and provider_type are required"}) } // This check is now incorrect and deprecated. var tenant domain.Tenant if err := h.db.First(&tenant, "id = ?", req.ClientID).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant not found"}) } return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } // Create in DB if err := h.db.Create(&req).Error; err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.Status(fiber.StatusCreated).JSON(req) } // TODO: Re-implement Update, Delete handlers for IdP Configs for Clients