diff --git a/app/login/login_form.php b/app/login/login_form.php
index 2616cabcdf31b639c345f1646739776d1e38ff88..5450c93dcde1e2903c3dd58f4448627df9d0887f 100755
--- a/app/login/login_form.php
+++ b/app/login/login_form.php
@@ -88,9 +88,14 @@
 	</div>
 
 
-	<?php if($User->settings->{'passkeys'}=="1") { ?>
+	<?php if($User->settings->{'passkeys'}=="1"){ ?>
 	<div class="col-xs-12" style="padding-top:20px;">
 
+		<?php
+		// set disabled class if composer has errors
+		$disabled = $User->composer_has_errors(["firehed/webauthn", "firehed/cbor"]) ? "disabled" : "";
+		?>
+
 		<div style="width: 45%;" class='text-center pull-left'>
 			<hr style="padding-top: 3px">
 		</div>
@@ -101,7 +106,7 @@
 			<hr style="padding-top: 3px">
 		</div>
 
-		<button class="btn btn-sm btn-default passkey_login" style="width:100%;margin-top:20px;">
+		<button class="btn btn-sm btn-default passkey_login <?php print $disabled; ?>" style="width:100%;margin-top:20px;">
 			<svg height="14" aria-hidden="true" viewBox="0 -3 32 24" version="1.1" width="20" data-view-component="true" class="octicon octicon-passkey-fill">
     			<path d="M9.496 2a5.25 5.25 0 0 0-2.519 9.857A9.006 9.006 0 0 0 .5 20.228a.751.751 0 0 0 .728.772h5.257c3.338.001 6.677.002 10.015 0a.5.5 0 0 0 .5-.5v-4.669a.95.95 0 0 0-.171-.551 9.02 9.02 0 0 0-4.814-3.423A5.25 5.25 0 0 0 9.496 2Z"></path>
     			<path d="M23.625 10.313c0 1.31-.672 2.464-1.691 3.134a.398.398 0 0 0-.184.33v.886a.372.372 0 0 1-.11.265l-.534.534a.188.188 0 0 0 0 .265l.534.534c.071.07.11.166.11.265v.347a.374.374 0 0 1-.11.265l-.534.534a.188.188 0 0 0 0 .265l.534.534a.37.37 0 0 1 .11.265v.431a.379.379 0 0 1-.097.253l-1.2 1.319a.781.781 0 0 1-1.156 0l-1.2-1.319a.379.379 0 0 1-.097-.253v-5.39a.398.398 0 0 0-.184-.33 3.75 3.75 0 1 1 5.809-3.134ZM21 9.75a1.125 1.125 0 1 0-2.25 0 1.125 1.125 0 0 0 2.25 0Z"></path>
diff --git a/app/tools/user-menu/passkeys.php b/app/tools/user-menu/passkeys.php
index ed3c91b5c6342dca9e10d0b2a8cfe70eb265ea7d..b68beb4bf0313a8bb4ce647c1b51453fb82f9b08 100644
--- a/app/tools/user-menu/passkeys.php
+++ b/app/tools/user-menu/passkeys.php
@@ -18,12 +18,17 @@ $User->check_user_session();
 
 # tls check
 if (!$Tools->isHttps()) {
-	$Result->show("danger", _("TLS is required for passcode authentication"), false);
+	$Result->show("danger alert-absolute", _("TLS is required for passcode authentication"), false);
 }
 # are passkeys enabled ?
 elseif (!$User->settings->{'passkeys'}=="1") {
-	$Result->show("danger", _("Passkey authentication is disabled"), false);
+	$Result->show("danger alert-absolute", _("Passkey authentication is disabled"), false);
 }
+# is composer present ?
+elseif($User->composer_has_errors(["firehed/webauthn", "firehed/cbor"])) {
+	$Result->show("danger alert-absolute", $User->composer_err, false);
+}
+# is
 else {
 	// get user passkeys
 	$user_passkeys = $User->get_user_passkeys(false);
diff --git a/functions/classes/class.Common.php b/functions/classes/class.Common.php
index b49cc99d39d5c89135948d535b6428b28b5f3779..b3471be2aa299c0c35d3fdfdf35ee555c0bf1787 100644
--- a/functions/classes/class.Common.php
+++ b/functions/classes/class.Common.php
@@ -2289,4 +2289,51 @@ class Common_functions  {
 	    // result
 	    return implode("\n", $html);
 	}
+
+
+	/**
+	 * Composer check
+	 *
+	 * Checks if composer is installed and if requested checks for composer modules required
+	 *
+	 * @method composer_has_error
+	 * @param  array $composer_packages
+	 * @return bool
+	 */
+	public function composer_has_errors ($composer_packages = []) {
+		// check for autoload file
+        if(!file_exists(__DIR__ . '/../vendor/autoload.php')) {
+        	$this->composer_err = _("Composer autoload not present")." !<hr>"._("Please install composer modules ( cd functions && composer install ).");
+        	return true;
+        }
+
+        // autoload composer files
+        require __DIR__ . '/../vendor/autoload.php';
+        // check if composer is installed
+        if (!class_exists('\Composer\InstalledVersions')) {
+        	$this->composer_err = _("Composer is not installed")."!<hr>"._("Please install composer and composer modules ( cd functions && composer install ).");
+        	return true;
+        }
+
+        // validate all packages if required
+        if(is_array($composer_packages)) {
+        	if(sizeof($composer_packages)>0) {
+        		foreach ($composer_packages as $package) {
+					if(\Composer\InstalledVersions::isInstalled($package)===false) {
+						$this->composer_err .= _("Composer package")." ".$package." "._("is not installed")." !<hr>"._("Please install required modules ( cd functions && composer install ).");
+					}
+        		}
+        		// check
+        		if (isset($this->composer_err)) {
+        			return true;
+        		}
+        	}
+        }
+        // all good
+        return false;
+	}
+
+
 }
+
+
diff --git a/functions/classes/class.User.php b/functions/classes/class.User.php
index abc658d91c21eaabad60317b3df9bd0610b70c25..6e858b422d03367dad0255419670ca9024ea1826 100644
--- a/functions/classes/class.User.php
+++ b/functions/classes/class.User.php
@@ -1331,8 +1331,7 @@ class User extends Common_functions {
 
 
 
-    /* @passkey -------------------- */
-
+    /* @passkeys -------------------- */
 
     /**
      * Fetch user details based on passkey ID
diff --git a/js/login.js b/js/login.js
index 2b18c75fc2a9a663a9b8aa7c7cda5a1d179c6dce..a41610edc2c6598dca1cbafa74eb6bd2b2a0fdb0 100755
--- a/js/login.js
+++ b/js/login.js
@@ -147,8 +147,10 @@ $(".clearIPrequest").click(function() {
 
 // passkey login
 $('.passkey_login').click(function() {
-    // execute passkey login
-    startLogin();
+    if(!$(this).hasClass('disabled')) {
+        // execute passkey login
+        startLogin();
+    }
     // prevent submit
     return false;
 })
diff --git a/misc/CHANGELOG b/misc/CHANGELOG
index 48936dbc36370b960731a049aa0fb90bd19391b8..1d5feb2b7b4e5b692c2071ac33141b068a0f3cd4 100755
--- a/misc/CHANGELOG
+++ b/misc/CHANGELOG
@@ -2,10 +2,12 @@
 
     New features:
     ------------
-    + support for passkeys / passwordless logins;
+    + Added support for passkeys / passwordless logins;
 
     Bugfixes:
     ----------------------------
+    + Use UTF-16LE encoding for XLS sheet names, and UTF-8 as input encoding (#3977);
+    + Update login_form.php for installation inside subdir (#3954);
 
     Security Fixes:
     ----------------------------