diff --git a/backend/internal/bootstrap/kratos_seed.go b/backend/internal/bootstrap/kratos_seed.go index 7ddb4a4c..c1130c65 100644 --- a/backend/internal/bootstrap/kratos_seed.go +++ b/backend/internal/bootstrap/kratos_seed.go @@ -5,19 +5,21 @@ import ( "log/slog" "os" "strings" + "time" ) // SeedAdminIdentity creates the initial admin identity in the configured IDP. -func SeedAdminIdentity(idp domain.IdentityProvider) error { +// Returns the Kratos Identity ID and error. +func SeedAdminIdentity(idp domain.IdentityProvider) (string, error) { if idp == nil { - return 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 + return "", nil } adminName := strings.TrimSpace(os.Getenv("ADMIN_NAME")) @@ -34,18 +36,41 @@ func SeedAdminIdentity(idp domain.IdentityProvider) error { "affiliationType": "internal", "companyCode": "", "grade": "admin", + "role": "super_admin", // Explicitly set role for Kratos traits }, } - _, err := idp.CreateUser(user, adminPassword) - if err != nil { - if strings.Contains(err.Error(), "already exists") { - slog.Info("[Bootstrap] Admin identity already exists in IDP", "email", adminEmail) - return nil + // Retry logic for Kratos connection + maxRetries := 5 + var err error + var identityID string + + for i := 0; i < maxRetries; i++ { + 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 } - return err + + 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) } - slog.Info("[Bootstrap] Admin identity created in IDP", "email", adminEmail, "idp", idp.Name()) - return nil + return "", err }