diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 09e8ec193..ec64e60b8 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -1389,6 +1389,18 @@ function set_tfa($_data) { case "webauthn": $key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"]; + // Refuse incomplete WebAuthn registrations. Without this guard a request + // that does not contain the parsed credential data (e.g. a plain form + // POST) would silently insert an empty keyHandle while reporting success. + if (empty($_data['registration']) || empty($_data['registration']->credentialId)) { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_data_log), + 'msg' => 'webauthn_registration_failed' + ); + return false; + } + $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`) VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')"); $stmt->execute(array( diff --git a/data/web/lang/lang.en-gb.json b/data/web/lang/lang.en-gb.json index c958e68eb..6ce823841 100644 --- a/data/web/lang/lang.en-gb.json +++ b/data/web/lang/lang.en-gb.json @@ -1249,6 +1249,7 @@ "u2f_deprecated": "It seems that your Key was registered using the deprecated U2F method. We will deactivate Two-Factor-Authenticaiton for you and delete your Key.", "u2f_deprecated_important": "Please register your Key in the admin panel with the new WebAuthn method.", "webauthn": "WebAuthn authentication", + "webauthn_registration_failed": "WebAuthn registration failed. The credential could not be verified, please try again.", "waiting_usb_auth": "Waiting for USB device...

Please tap the button on your USB device now.", "waiting_usb_register": "Waiting for USB device...

Please enter your password above and confirm your registration by tapping the button on your USB device.", "yubi_otp": "Yubico OTP authentication" diff --git a/data/web/templates/base.twig b/data/web/templates/base.twig index e0f3b5b35..3c01461f3 100644 --- a/data/web/templates/base.twig +++ b/data/web/templates/base.twig @@ -415,18 +415,24 @@ function recursiveBase64StrToArrayBuffer(obj) { return navigator.credentials.create(json); }).then(function(cred) { return { - id: cred.id, - rawId: arrayBufferToBase64(cred.rawId), - response: { - attestationObject: arrayBufferToBase64(cred.response.attestationObject), - clientDataJSON: arrayBufferToBase64(cred.response.clientDataJSON) - }, - type: cred.type + clientDataJSON: arrayBufferToBase64(cred.response.clientDataJSON), + attestationObject: arrayBufferToBase64(cred.response.attestationObject), + key_id: keyId, + tfa_method: 'webauthn', + confirm_password: '' }; - }).then(function(credData) { - $('#setup_webauthn_register_data').val(JSON.stringify(credData)); - $('#setup_webauthn_reg_form input[name=set_tfa]').val('1'); - $('#setup_webauthn_reg_form').submit(); + }).then(JSON.stringify).then(function(credentialData) { + // send the attestation to the JSON API, like the regular WebAuthn modal does, + // so that the credential is parsed and stored correctly + return window.fetch('/api/v1/add/webauthn-tfa-registration', {method: 'POST', body: credentialData, cache: 'no-cache'}); + }).then(function(response) { + return response.json(); + }).then(function(json) { + if (json.success) { + window.location = window.location.href.split('#')[0]; + } else { + throw new Error(json.msg); + } }).catch(function(err) { $('#setup_webauthn_return_code').show().text(err.message || 'Registration failed'); });