diff --git a/src/client/cmdVM/attachHelper.go b/src/client/cmdVM/attachHelper.go
index f607e0dbbe05b93b5fd322298242dfcbb97bc1e4..d4ad3d44f26e79512ad99d9384d290d48bc3e47b 100644
--- a/src/client/cmdVM/attachHelper.go
+++ b/src/client/cmdVM/attachHelper.go
@@ -1,71 +1,37 @@
 package cmdVM
 
 import (
-	"encoding/json"
-	"errors"
 	"fmt"
 	"nexus-client/exec"
 	u "nexus-client/utils"
 	"nexus-common/params"
 	"nexus-common/vm"
 	g "nexus-libclient/globals"
-	"sync"
-
-	"github.com/go-playground/validator/v10"
+	libclient "nexus-libclient/vm"
 )
 
-// The boolean indicates whether attaching is blocking (synchronous) or non-blocking (asynchronous).
-func AttachToVMs(vms []vm.VMAttachCredentialsSerialized, synchronous bool) (int, error) {
+func AttachToVMs(vms []vm.VMAttachCredentialsSerialized) (int, error) {
 	statusCode := 0
 
 	hostname := g.GetInstance().Hostname
 	cert := g.GetInstance().PubCert
 
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
-	var wg sync.WaitGroup
-
-	if synchronous {
-		// Use wait groups to wait until all viewers threads have completed.
-		wg.Add(len(vms))
-	}
-
 	for _, v := range vms {
-		uuid := v.ID.String()
-		p := &params.VMAttachCreds{Pwd: v.Pwd}
-		resp, err := client.R().SetBody(p).Post(host + "/vms/" + uuid + "/spicecreds")
+		p := params.VMAttachCreds{Pwd: v.Pwd}
+
+		creds, err := libclient.VMGetSpiceCreds(v.ID.String(), p)
 		if err != nil {
+			u.PrintlnErr(err)
 			return 1, err
 		}
 
-		if resp.IsSuccess() {
-			var creds vm.VMSpiceCredentialsSerialized
-			if err := json.Unmarshal(resp.Body(), &creds); err != nil {
-				return 1, err
+		go func(v vm.VMAttachCredentialsSerialized, creds vm.VMSpiceCredentialsSerialized) {
+			stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, false)
+			if err != nil {
+				u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr))
+				statusCode |= 1
 			}
-			if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil {
-				return 1, err
-			}
-
-			go func(v vm.VMAttachCredentialsSerialized, creds vm.VMSpiceCredentialsSerialized) {
-				stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, false)
-				if err != nil {
-					u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr))
-					statusCode |= 1
-				}
-				if synchronous {
-					wg.Done()
-				}
-			}(v, creds)
-
-		} else {
-			return 1, errors.New(resp.Status() + ": " + resp.String())
-		}
-	}
-
-	if synchronous {
-		wg.Wait()
+		}(v, *creds)
 	}
 
 	return statusCode, nil
diff --git a/src/client/cmdVM/helper.go b/src/client/cmdVM/helper.go
index a20df134f367b89efb5e60984fafe8d05e58f55a..9259f42dccf1ef15243c9b39cb4a5e894d08ef59 100644
--- a/src/client/cmdVM/helper.go
+++ b/src/client/cmdVM/helper.go
@@ -108,7 +108,7 @@ func getFilteredVMs(filteredVMsFunc GetVMs, patterns []string) ([]vm.VMNetworkSe
 //
 //	"."   -> matches everything
 //	"bla" -> matches any VM name containing "bla"
-func getFilteredVMCredentials(route string, patterns []string) ([]vm.VMAttachCredentialsSerialized, error) {
+func getFilteredVMCredentials(patterns []string) ([]vm.VMAttachCredentialsSerialized, error) {
 	if len(patterns) < 1 {
 		return nil, errors.New("At least one ID or regex must be specified")
 	}
diff --git a/src/client/cmdVM/vmAttachAsync.go b/src/client/cmdVM/vmAttachAsync.go
index d147716b78bb3e0a0b835fbdeaed84325f5b99db..748a9417b9847ea36f99c7a3465a399dceae2e81 100644
--- a/src/client/cmdVM/vmAttachAsync.go
+++ b/src/client/cmdVM/vmAttachAsync.go
@@ -36,7 +36,7 @@ func (cmd *AttachAsync) Run(args []string) int {
 		return 1
 	}
 
-	creds, err := getFilteredVMCredentials("/vms/attach", args)
+	creds, err := getFilteredVMCredentials(args)
 	if err != nil {
 		u.PrintlnErr(err)
 		return 1
@@ -47,7 +47,7 @@ func (cmd *AttachAsync) Run(args []string) int {
 		return 1
 	}
 
-	statusCode, err := AttachToVMs(creds, false)
+	statusCode, err := AttachToVMs(creds)
 	if err != nil {
 		u.PrintlnErr(err)
 	}
diff --git a/src/client/cmdVM/vmAttachAsyncSingle.go b/src/client/cmdVM/vmAttachAsyncSingle.go
index 6fce7a941879a75e30f5af7b090c1fb7fb173882..872013bd090493d70ee05ff85c9bcf5498db4946 100644
--- a/src/client/cmdVM/vmAttachAsyncSingle.go
+++ b/src/client/cmdVM/vmAttachAsyncSingle.go
@@ -50,7 +50,7 @@ func (cmd *AttachAsyncSingle) Run(args []string) int {
 		return 1
 	}
 
-	statusCode, err := AttachToVMs([]vm.VMAttachCredentialsSerialized{*creds}, false)
+	statusCode, err := AttachToVMs([]vm.VMAttachCredentialsSerialized{*creds})
 	if err != nil {
 		u.PrintlnErr(err)
 		return 1
diff --git a/src/client/cmdVM/vmAttachFromPwd.go b/src/client/cmdVM/vmAttachFromPwd.go
index 48d75f33eeb91a3520c9acb31fcc21753e01eaf2..1bbf3f4726c6b418fe7181aef39020986c476d49 100644
--- a/src/client/cmdVM/vmAttachFromPwd.go
+++ b/src/client/cmdVM/vmAttachFromPwd.go
@@ -1,15 +1,13 @@
 package cmdVM
 
 import (
-	"encoding/json"
 	"nexus-client/exec"
 	e "nexus-client/exec"
 	u "nexus-client/utils"
 	"nexus-common/params"
 	"nexus-common/vm"
 	g "nexus-libclient/globals"
-
-	"github.com/go-playground/validator/v10"
+	libclient "nexus-libclient/vm"
 )
 
 type AttachFromPwd struct {
@@ -23,7 +21,7 @@ func (cmd *AttachFromPwd) GetName() string {
 func (cmd *AttachFromPwd) GetDesc() []string {
 	return []string{
 		"Attaches to the VM that matches the specified password.",
-		"Requires the VM_ATTACH_ANY user capability."}
+		"Requires either EXAM_ATTACH or VM_ATTACH_ANY user capability."}
 }
 
 func (cmd *AttachFromPwd) PrintUsage() {
@@ -47,47 +45,23 @@ func (cmd *AttachFromPwd) Run(args []string) int {
 		return 1
 	}
 
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
 	hostname := g.GetInstance().Hostname
 	cert := g.GetInstance().PubCert
 
 	pwd := args[0]
-	p := &params.VMAttachCreds{Pwd: pwd}
-	resp, err := client.R().SetBody(p).Post(host + "/vms/spicecreds")
+	p := params.VMAttachCreds{Pwd: pwd}
+
+	creds, err := libclient.VMGetAnySpiceCreds(p)
 	if err != nil {
-		u.PrintlnErr("Failed retrieving VM credentials: " + err.Error())
+		u.PrintlnErr(err)
 		return 1
-	}
-
-	if resp.IsSuccess() {
-		var creds vm.VMSpiceCredentialsSerialized
-		if err := json.Unmarshal(resp.Body(), &creds); err != nil {
-			u.PrintlnErr("Failed deserializing VM spice credentials: " + err.Error())
-			return 1
-		}
-		if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil {
-			u.PrintlnErr("Failed validating VM spice credentials: " + err.Error())
-			return 1
-		}
+	} else {
 		go func(creds vm.VMSpiceCredentialsSerialized) {
 			_, err := e.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, true)
 			if err != nil {
 				u.PrintlnErr("Failed executing remote-viewer: " + err.Error())
 			}
-		}(creds)
-	} else {
-		type msg struct {
-			Message string `json:"message"`
-		}
-		var m msg
-		if err := json.Unmarshal(resp.Body(), &m); err != nil {
-			u.PrintlnErr("Failed retrieving VM credentials: " + err.Error())
-			return 1
-		}
-		u.PrintlnErr("Failed retrieving VM credentials: " + m.Message)
-		return 1
+		}(*creds)
 	}
 
 	return 0
diff --git a/src/client/cmdVM/vmCreds2csv.go b/src/client/cmdVM/vmCreds2csv.go
index 5474cd93ecd6fb582128322905e91698b3c275a0..f11e49601f4af922ab1f4952f8764427713cbfb4 100644
--- a/src/client/cmdVM/vmCreds2csv.go
+++ b/src/client/cmdVM/vmCreds2csv.go
@@ -41,7 +41,7 @@ func (cmd *Creds2csv) Run(args []string) int {
 
 	csvFile := args[argc-1]
 
-	credsList, err := getFilteredVMCredentials("/vms/attach", args[:argc-1])
+	credsList, err := getFilteredVMCredentials(args[:argc-1])
 	if err != nil {
 		u.PrintlnErr("Error: " + err.Error())
 		return 1
diff --git a/src/client/cmdVM/vmCreds2pdf.go b/src/client/cmdVM/vmCreds2pdf.go
index 77d60cda6a70dfaa6d5490b193b6fde3d31e6354..7a2c1b9c501c166342431c87976c66d1762780d5 100644
--- a/src/client/cmdVM/vmCreds2pdf.go
+++ b/src/client/cmdVM/vmCreds2pdf.go
@@ -44,7 +44,7 @@ func (cmd *Creds2pdf) Run(args []string) int {
 
 	pdfFile := args[argc-1]
 
-	credsList, err := getFilteredVMCredentials("/vms/attach", args[:argc-1])
+	credsList, err := getFilteredVMCredentials(args[:argc-1])
 	if err != nil {
 		u.PrintlnErr("Error: " + err.Error())
 		return 1
diff --git a/src/client/cmdVM/vmListSingle.go b/src/client/cmdVM/vmListSingle.go
index be8ef4b579d06466a55f3d4b5db6689a178bd234..8d06c5d71dfafc12cb1fffa8e0b5512949b3168a 100644
--- a/src/client/cmdVM/vmListSingle.go
+++ b/src/client/cmdVM/vmListSingle.go
@@ -36,7 +36,7 @@ func (cmd *ListSingle) Run(args []string) int {
 	}
 
 	vmID := args[0]
-	vm, err := libclient.GetListableVM(vmID)
+	vm, err := libclient.GetListVM(vmID)
 	if err != nil {
 		u.PrintlnErr(err)
 		return 1
diff --git a/src/client/cmdVM/vmStartAttach.go b/src/client/cmdVM/vmStartAttach.go
index 4cca859438ff75e810d36043b3f4a1dd7b1a4351..d3ae95bc6ba6c080dbc97c5e0dd4ad0ba64bb04d 100644
--- a/src/client/cmdVM/vmStartAttach.go
+++ b/src/client/cmdVM/vmStartAttach.go
@@ -73,7 +73,7 @@ func (cmd *StartAttach) Run(args []string) int {
 	}
 
 	// at this point, the returned filtered credentials only works for VMs that started successfully
-	creds, err := getFilteredVMCredentials("/vms/attach", args)
+	creds, err := getFilteredVMCredentials(args)
 	if err != nil {
 		u.PrintlnErr(err)
 		return 1
@@ -84,7 +84,7 @@ func (cmd *StartAttach) Run(args []string) int {
 		return 1
 	}
 
-	attachStatusCode, err := AttachToVMs(creds, false)
+	attachStatusCode, err := AttachToVMs(creds)
 	if err != nil {
 		u.PrintlnErr(err)
 	}
diff --git a/src/client/nexush/nexush.go b/src/client/nexush/nexush.go
index 3a12c03e4d9cf4b1799f1047c3a7727880f06b46..181f453b03fd1631183f8e5fa699a1fe448a4abd 100644
--- a/src/client/nexush/nexush.go
+++ b/src/client/nexush/nexush.go
@@ -60,7 +60,7 @@ var cmdList = []cmd.Command{
 	&cmdVM.AddAccess{"vmaddaccess"},
 	&cmdVM.AttachAsync{"vmattach"},
 	// &cmdVM.AttachAsyncSingle{"vmattachsingle"},   // for testing the route only
-	// &cmdVM.AttachFromPwd{"vmattachfrompwd"},    // for testing the route only
+	&cmdVM.AttachFromPwd{"vmattachfrompwd"}, // for testing the route only
 	&cmdVM.Create{"vmcreate"},
 	&cmdVM.Creds2pdf{"vmcreds2pdf"},
 	&cmdVM.Creds2csv{"vmcreds2csv"},
diff --git a/src/libclient/vm/vmGet.go b/src/libclient/vm/vmGet.go
index 2b2eaf3de2ed665cd83736ff8dda71ba0b529c08..34bac139c1e645cd67601d2268ee0b78e62b319f 100644
--- a/src/libclient/vm/vmGet.go
+++ b/src/libclient/vm/vmGet.go
@@ -69,31 +69,31 @@ func getFilteredVMs(route string) ([]vm.VMNetworkSerialized, error) {
 	}
 
 	if resp.IsSuccess() {
-		templates, err := deserializeVMs(resp)
+		vms, err := deserializeVMs(resp)
 		if err != nil {
 			return nil, err
 		}
-		return templates, nil
+		return vms, nil
 	} else {
 		return nil, response.ErrorToMsg(resp)
 	}
 }
 
-func GetListableVM(uuid string) (*vm.VMNetworkSerialized, error) {
+func GetListVM(vmID string) (*vm.VMNetworkSerialized, error) {
 	client := g.GetInstance().Client
 	host := g.GetInstance().Host
 
-	resp, err := client.R().Get(host + "/vms/" + uuid)
+	resp, err := client.R().Get(host + "/vms/" + vmID)
 	if err != nil {
 		return nil, err
 	}
 
 	if resp.IsSuccess() {
-		template, err := deserializeVM(resp)
+		vm, err := deserializeVM(resp)
 		if err != nil {
 			return nil, err
 		}
-		return template, nil
+		return vm, nil
 	} else {
 		return nil, response.ErrorToMsg(resp)
 	}
diff --git a/src/libclient/vm/vmGetSpiceCreds.go b/src/libclient/vm/vmGetSpiceCreds.go
new file mode 100644
index 0000000000000000000000000000000000000000..d97ac60c447c5e3335c97a89641b552ac8c320f0
--- /dev/null
+++ b/src/libclient/vm/vmGetSpiceCreds.go
@@ -0,0 +1,52 @@
+package vm
+
+import (
+	"encoding/json"
+	"errors"
+	"nexus-common/params"
+	"nexus-common/vm"
+	g "nexus-libclient/globals"
+	"nexus-libclient/response"
+
+	"github.com/go-playground/validator/v10"
+	"github.com/go-resty/resty/v2"
+)
+
+func VMGetSpiceCreds(vmID string, p params.VMAttachCreds) (*vm.VMSpiceCredentialsSerialized, error) {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().SetBody(p).Post(host + "/vms/" + vmID + "/spicecreds")
+	if err != nil {
+		return nil, err
+	}
+
+	return handleResponse(resp)
+}
+
+func VMGetAnySpiceCreds(p params.VMAttachCreds) (*vm.VMSpiceCredentialsSerialized, error) {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().SetBody(p).Post(host + "/vms/spicecreds")
+	if err != nil {
+		return nil, err
+	}
+
+	return handleResponse(resp)
+}
+
+func handleResponse(resp *resty.Response) (*vm.VMSpiceCredentialsSerialized, error) {
+	if resp.IsSuccess() {
+		var creds vm.VMSpiceCredentialsSerialized
+		if err := json.Unmarshal(resp.Body(), &creds); err != nil {
+			return nil, errors.New("Failed deserializing VM spice credentials: " + err.Error())
+		}
+		if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil {
+			return nil, errors.New("Failed validating VM spice credentials: " + err.Error())
+		}
+		return &creds, nil
+	} else {
+		return nil, response.ErrorToMsg(resp)
+	}
+}
diff --git a/src/server/router/routerVMs.go b/src/server/router/routerVMs.go
index e328202f0f4c220b8c0a21c4f3efa12a8c6af474..51d59d42215879574fb97054e0d4b846ecfa5369 100644
--- a/src/server/router/routerVMs.go
+++ b/src/server/router/routerVMs.go
@@ -205,7 +205,8 @@ func (r *RouterVMs) VMSpiceCreds(c echo.Context) error {
 
 // Returns the Spice credentials for the VM matching the specific VM attach password.
 // Output: nexus-common/vm.VMSpiceCredentialsSerialized
-// Requires either CAP_EXAM_ATTACH or CAP_VM_ATTACH_ANY user capability:
+// Requires either to be the VM's owner, or either capability:
+// CAP_EXAM_ATTACH or CAP_VM_ATTACH_ANY user capability:
 // curl --cacert ca.pem -X POST https://localhost:1077/vms/spicecreds -H 'Content-Type: application/json' -d '{"pwd":"46L8drgZ5Dx"}' -H "Authorization: Bearer <AccessToken>"
 func (r *RouterVMs) VMSpiceCredsAny(c echo.Context) error {
 	// Retrieves logged user from context.