package bootstrap import ( "baron-sso-backend/internal/domain" "log/slog" "os" "strings" "time" ) // SeedAdminIdentity creates the initial admin identity in the configured IDP. // Returns the Kratos Identity ID and error. func SeedAdminIdentity(idp domain.IdentityProvider) (string, error) { if idp == nil { return "", nil } adminEmail := strings.TrimSpace(os.Getenv("ADMIN_EMAIL")) adminPassword := os.Getenv("ADMIN_PASSWORD") if adminEmail == "" || adminPassword == "" { slog.Warn("[Bootstrap] ADMIN_EMAIL or ADMIN_PASSWORD not set. Skipping admin identity seed.") return "", nil } adminName := strings.TrimSpace(os.Getenv("ADMIN_NAME")) if adminName == "" { adminName = "System Admin" } user := &domain.BrokerUser{ Email: adminEmail, Name: adminName, PhoneNumber: "", Attributes: map[string]any{ "department": "Admin", "affiliationType": "internal", "grade": "", "role": "super_admin", // Explicitly set role for Kratos traits }, } // Retry logic for Kratos connection maxRetries := 5 var err error var identityID string for i := range maxRetries { identityID, err = idp.CreateUser(user, adminPassword) if err == nil { slog.Info("[Bootstrap] Admin identity created in IDP", "email", adminEmail, "idp", idp.Name(), "id", identityID) return identityID, nil } if strings.Contains(err.Error(), "already exists") { slog.Info("[Bootstrap] Admin identity already exists in IDP. Attempting to retrieve ID...", "email", adminEmail) // Try to sign in to get the identity ID authInfo, err := idp.SignIn(adminEmail, adminPassword) if err == nil && authInfo != nil { slog.Info("[Bootstrap] Retrieved existing admin identity ID", "id", authInfo.Subject) return authInfo.Subject, nil } slog.Warn("[Bootstrap] Failed to retrieve existing admin identity ID via SignIn", "error", err) return "", nil // Return nil error to avoid stopping bootstrap, but ID is missing } slog.Warn("[Bootstrap] Failed to seed admin identity (retrying...)", "attempt", i+1, "max_retries", maxRetries, "error", err, ) time.Sleep(2 * time.Second) } return "", err }