package handler import ( "context" "log" "os" "strings" "github.com/descope/go-sdk/descope" "github.com/descope/go-sdk/descope/client" "github.com/gofiber/fiber/v2" ) type AdminHandler struct { DescopeClient *client.DescopeClient } func NewAdminHandler() *AdminHandler { projectID := os.Getenv("DESCOPE_PROJECT_ID") managementKey := os.Getenv("DESCOPE_MANAGEMENT_KEY") var descopeClient *client.DescopeClient var err error if projectID != "" && managementKey != "" { descopeClient, err = client.NewWithConfig(&client.Config{ ProjectID: projectID, ManagementKey: managementKey, }) if err != nil { log.Printf("Warning: Failed to initialize Descope Client for Admin: %v", err) } } else { log.Println("Warning: DESCOPE_PROJECT_ID or DESCOPE_MANAGEMENT_KEY missing. Admin functions will fail.") } return &AdminHandler{ DescopeClient: descopeClient, } } func boolPtr(b bool) *bool { return &b } type CreateUserRequest struct { LoginID string `json:"loginId"` Email string `json:"email"` Phone string `json:"phone"` DisplayName string `json:"displayName"` Roles []string `json:"roles"` Tenants map[string][]string `json:"tenants"` // tenantId -> roles } func (h *AdminHandler) CreateUser(c *fiber.Ctx) error { // 1. Simple Password Check adminPass := os.Getenv("ADMIN_PASSWORD") if adminPass == "" { adminPass = "admin" // Default fallback } reqPass := c.Get("X-Admin-Password") if reqPass != adminPass { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid Admin Password"}) } if h.DescopeClient == nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Descope Client not configured"}) } var req CreateUserRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"}) } if req.LoginID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "LoginID is required"}) } // Normalize Phone normalizedPhone := req.Phone if normalizedPhone != "" { if strings.HasPrefix(normalizedPhone, "010") { normalizedPhone = "+82" + normalizedPhone[1:] } else if strings.HasPrefix(normalizedPhone, "82") { normalizedPhone = "+" + normalizedPhone } } userObj := &descope.UserRequest{ User: descope.User{ Email: req.Email, Phone: normalizedPhone, Name: req.DisplayName, }, VerifiedEmail: boolPtr(req.Email != ""), VerifiedPhone: boolPtr(normalizedPhone != ""), } // Add Roles if provided if len(req.Roles) > 0 { userObj.Roles = req.Roles } // Add Tenants if provided if len(req.Tenants) > 0 { // Convert map[string][]string to []*descope.AssociatedTenant userTenants := []*descope.AssociatedTenant{} for tenantID, roles := range req.Tenants { userTenants = append(userTenants, &descope.AssociatedTenant{ TenantID: tenantID, Roles: roles, }) } userObj.Tenants = userTenants } log.Printf("[Admin] Creating user: %s (Email: %s, Phone: %s)", req.LoginID, req.Email, normalizedPhone) res, err := h.DescopeClient.Management.User().Create(context.Background(), req.LoginID, userObj) if err != nil { log.Printf("[Admin] Failed to create user: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{ "message": "User created successfully", "user": res, }) } func (h *AdminHandler) CheckAuth(c *fiber.Ctx) error { adminPass := os.Getenv("ADMIN_PASSWORD") if adminPass == "" { adminPass = "admin" } reqPass := c.Get("X-Admin-Password") if reqPass != adminPass { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid Admin Password"}) } return c.Status(fiber.StatusOK).JSON(fiber.Map{"status": "ok"}) }