From db4ba407dab3838b1a5d8ed922311ab40e678421 Mon Sep 17 00:00:00 2001 From: George Stephanis Date: Wed, 20 Apr 2022 15:20:37 -0400 Subject: [PATCH 1/9] Remove U2F --- class-two-factor-core.php | 15 +- includes/Google/u2f-api.js | 748 ------------------ includes/Yubico/U2F.php | 507 ------------ ...s-two-factor-fido-u2f-admin-list-table.php | 160 ---- providers/class-two-factor-fido-u2f-admin.php | 363 --------- providers/class-two-factor-fido-u2f.php | 407 ---------- providers/css/fido-u2f-admin.css | 12 - providers/js/fido-u2f-admin-inline-edit.js | 150 ---- providers/js/fido-u2f-admin.js | 48 -- providers/js/fido-u2f-login.js | 16 - readme.txt | 14 +- tests/providers/class-two-factor-fido-u2f.php | 222 ------ 12 files changed, 4 insertions(+), 2658 deletions(-) delete mode 100644 includes/Google/u2f-api.js delete mode 100644 includes/Yubico/U2F.php delete mode 100644 providers/class-two-factor-fido-u2f-admin-list-table.php delete mode 100644 providers/class-two-factor-fido-u2f-admin.php delete mode 100644 providers/class-two-factor-fido-u2f.php delete mode 100644 providers/css/fido-u2f-admin.css delete mode 100644 providers/js/fido-u2f-admin-inline-edit.js delete mode 100644 providers/js/fido-u2f-admin.js delete mode 100644 providers/js/fido-u2f-login.js delete mode 100644 tests/providers/class-two-factor-fido-u2f.php diff --git a/class-two-factor-core.php b/class-two-factor-core.php index d7e67869..f5f18a48 100644 --- a/class-two-factor-core.php +++ b/class-two-factor-core.php @@ -214,7 +214,6 @@ private static function get_default_providers() { return array( 'Two_Factor_Email' => TWO_FACTOR_DIR . 'providers/class-two-factor-email.php', 'Two_Factor_Totp' => TWO_FACTOR_DIR . 'providers/class-two-factor-totp.php', - 'Two_Factor_FIDO_U2F' => TWO_FACTOR_DIR . 'providers/class-two-factor-fido-u2f.php', 'Two_Factor_Backup_Codes' => TWO_FACTOR_DIR . 'providers/class-two-factor-backup-codes.php', 'Two_Factor_Dummy' => TWO_FACTOR_DIR . 'providers/class-two-factor-dummy.php', ); @@ -283,18 +282,6 @@ public static function get_providers() { */ $providers = apply_filters( 'two_factor_providers', $providers ); - // FIDO U2F is PHP 5.3+ only. - if ( isset( $providers['Two_Factor_FIDO_U2F'] ) && version_compare( PHP_VERSION, '5.3.0', '<' ) ) { - unset( $providers['Two_Factor_FIDO_U2F'] ); - trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error - sprintf( - /* translators: %s: version number */ - __( 'FIDO U2F is not available because you are using PHP %s. (Requires 5.3 or greater)', 'two-factor' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - PHP_VERSION - ) - ); - } - // Map provider keys to classes so that we can instantiate them. $providers = self::get_providers_classes( $providers ); @@ -620,7 +607,7 @@ public static function get_available_providers_for_user( $user = null ) { * if emailed codes is available force it to be on, so that deprecated * or removed providers don't result in the two-factor requirement being * removed and 'failing open'. - * + * * Possible enhancement: add a filter to change the fallback method? */ if ( empty( $enabled_providers ) && $user_providers_raw ) { diff --git a/includes/Google/u2f-api.js b/includes/Google/u2f-api.js deleted file mode 100644 index 1036b920..00000000 --- a/includes/Google/u2f-api.js +++ /dev/null @@ -1,748 +0,0 @@ -//Copyright 2014-2015 Google Inc. All rights reserved. - -//Use of this source code is governed by a BSD-style -//license that can be found in the LICENSE file or at -//https://developers.google.com/open-source/licenses/bsd - -/** - * @fileoverview The U2F api. - */ -'use strict'; - - -/** - * Namespace for the U2F api. - * @type {Object} - */ -var u2f = u2f || {}; - -/** - * FIDO U2F Javascript API Version - * @number - */ -var js_api_version; - -/** - * The U2F extension id - * @const {string} - */ -// The Chrome packaged app extension ID. -// Uncomment this if you want to deploy a server instance that uses -// the package Chrome app and does not require installing the U2F Chrome extension. - u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd'; -// The U2F Chrome extension ID. -// Uncomment this if you want to deploy a server instance that uses -// the U2F Chrome extension to authenticate. -// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne'; - - -/** - * Message types for messages to/from the extension - * @const - * @enum {string} - */ -u2f.MessageTypes = { - 'U2F_REGISTER_REQUEST': 'u2f_register_request', - 'U2F_REGISTER_RESPONSE': 'u2f_register_response', - 'U2F_SIGN_REQUEST': 'u2f_sign_request', - 'U2F_SIGN_RESPONSE': 'u2f_sign_response', - 'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request', - 'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response' -}; - - -/** - * Response status codes - * @const - * @enum {number} - */ -u2f.ErrorCodes = { - 'OK': 0, - 'OTHER_ERROR': 1, - 'BAD_REQUEST': 2, - 'CONFIGURATION_UNSUPPORTED': 3, - 'DEVICE_INELIGIBLE': 4, - 'TIMEOUT': 5 -}; - - -/** - * A message for registration requests - * @typedef {{ - * type: u2f.MessageTypes, - * appId: ?string, - * timeoutSeconds: ?number, - * requestId: ?number - * }} - */ -u2f.U2fRequest; - - -/** - * A message for registration responses - * @typedef {{ - * type: u2f.MessageTypes, - * responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse), - * requestId: ?number - * }} - */ -u2f.U2fResponse; - - -/** - * An error object for responses - * @typedef {{ - * errorCode: u2f.ErrorCodes, - * errorMessage: ?string - * }} - */ -u2f.Error; - -/** - * Data object for a single sign request. - * @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}} - */ -u2f.Transport; - - -/** - * Data object for a single sign request. - * @typedef {Array} - */ -u2f.Transports; - -/** - * Data object for a single sign request. - * @typedef {{ - * version: string, - * challenge: string, - * keyHandle: string, - * appId: string - * }} - */ -u2f.SignRequest; - - -/** - * Data object for a sign response. - * @typedef {{ - * keyHandle: string, - * signatureData: string, - * clientData: string - * }} - */ -u2f.SignResponse; - - -/** - * Data object for a registration request. - * @typedef {{ - * version: string, - * challenge: string - * }} - */ -u2f.RegisterRequest; - - -/** - * Data object for a registration response. - * @typedef {{ - * version: string, - * keyHandle: string, - * transports: Transports, - * appId: string - * }} - */ -u2f.RegisterResponse; - - -/** - * Data object for a registered key. - * @typedef {{ - * version: string, - * keyHandle: string, - * transports: ?Transports, - * appId: ?string - * }} - */ -u2f.RegisteredKey; - - -/** - * Data object for a get API register response. - * @typedef {{ - * js_api_version: number - * }} - */ -u2f.GetJsApiVersionResponse; - - -//Low level MessagePort API support - -/** - * Sets up a MessagePort to the U2F extension using the - * available mechanisms. - * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback - */ -u2f.getMessagePort = function(callback) { - if (typeof chrome != 'undefined' && chrome.runtime) { - // The actual message here does not matter, but we need to get a reply - // for the callback to run. Thus, send an empty signature request - // in order to get a failure response. - var msg = { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - signRequests: [] - }; - chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() { - if (!chrome.runtime.lastError) { - // We are on a whitelisted origin and can talk directly - // with the extension. - u2f.getChromeRuntimePort_(callback); - } else { - // chrome.runtime was available, but we couldn't message - // the extension directly, use iframe - u2f.getIframePort_(callback); - } - }); - } else if (u2f.isAndroidChrome_()) { - u2f.getAuthenticatorPort_(callback); - } else if (u2f.isIosChrome_()) { - u2f.getIosPort_(callback); - } else { - // chrome.runtime was not available at all, which is normal - // when this origin doesn't have access to any extensions. - u2f.getIframePort_(callback); - } -}; - -/** - * Detect chrome running on android based on the browser's useragent. - * @private - */ -u2f.isAndroidChrome_ = function() { - var userAgent = navigator.userAgent; - return userAgent.indexOf('Chrome') != -1 && - userAgent.indexOf('Android') != -1; -}; - -/** - * Detect chrome running on iOS based on the browser's platform. - * @private - */ -u2f.isIosChrome_ = function() { - return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1; -}; - -/** - * Connects directly to the extension via chrome.runtime.connect. - * @param {function(u2f.WrappedChromeRuntimePort_)} callback - * @private - */ -u2f.getChromeRuntimePort_ = function(callback) { - var port = chrome.runtime.connect(u2f.EXTENSION_ID, - {'includeTlsChannelId': true}); - setTimeout(function() { - callback(new u2f.WrappedChromeRuntimePort_(port)); - }, 0); -}; - -/** - * Return a 'port' abstraction to the Authenticator app. - * @param {function(u2f.WrappedAuthenticatorPort_)} callback - * @private - */ -u2f.getAuthenticatorPort_ = function(callback) { - setTimeout(function() { - callback(new u2f.WrappedAuthenticatorPort_()); - }, 0); -}; - -/** - * Return a 'port' abstraction to the iOS client app. - * @param {function(u2f.WrappedIosPort_)} callback - * @private - */ -u2f.getIosPort_ = function(callback) { - setTimeout(function() { - callback(new u2f.WrappedIosPort_()); - }, 0); -}; - -/** - * A wrapper for chrome.runtime.Port that is compatible with MessagePort. - * @param {Port} port - * @constructor - * @private - */ -u2f.WrappedChromeRuntimePort_ = function(port) { - this.port_ = port; -}; - -/** - * Format and return a sign request compliant with the JS API version supported by the extension. - * @param {Array} signRequests - * @param {number} timeoutSeconds - * @param {number} reqId - * @return {Object} - */ -u2f.formatSignRequest_ = - function(appId, challenge, registeredKeys, timeoutSeconds, reqId) { - if (js_api_version === undefined || js_api_version < 1.1) { - // Adapt request to the 1.0 JS API. - var signRequests = []; - for (var i = 0; i < registeredKeys.length; i++) { - signRequests[i] = { - version: registeredKeys[i].version, - challenge: challenge, - keyHandle: registeredKeys[i].keyHandle, - appId: appId - }; - } - return { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - signRequests: signRequests, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - } - // JS 1.1 API. - return { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - appId: appId, - challenge: challenge, - registeredKeys: registeredKeys, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; -}; - -/** - * Format and return a register request compliant with the JS API version supported by the extension.. - * @param {Array} signRequests - * @param {Array} signRequests - * @param {number} timeoutSeconds - * @param {number} reqId - * @return {Object} - */ -u2f.formatRegisterRequest_ = - function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) { - if (js_api_version === undefined || js_api_version < 1.1) { - // Adapt request to the 1.0 JS API. - for (var i = 0; i < registerRequests.length; i++) { - registerRequests[i].appId = appId; - } - var signRequests = []; - for (var i = 0; i < registeredKeys.length; i++) { - signRequests[i] = { - version: registeredKeys[i].version, - challenge: registerRequests[0], - keyHandle: registeredKeys[i].keyHandle, - appId: appId - }; - } - return { - type: u2f.MessageTypes.U2F_REGISTER_REQUEST, - signRequests: signRequests, - registerRequests: registerRequests, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - } - // JS 1.1 API. - return { - type: u2f.MessageTypes.U2F_REGISTER_REQUEST, - appId: appId, - registerRequests: registerRequests, - registeredKeys: registeredKeys, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; -}; - - -/** - * Posts a message on the underlying channel. - * @param {Object} message - */ -u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) { - this.port_.postMessage(message); -}; - - -/** - * Emulates the HTML 5 addEventListener interface. Works only for the - * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage. - * @param {string} eventName - * @param {function({data: Object})} handler - */ -u2f.WrappedChromeRuntimePort_.prototype.addEventListener = - function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name == 'message' || name == 'onmessage') { - this.port_.onMessage.addListener(function(message) { - // Emulate a minimal MessageEvent object. - handler({'data': message}); - }); - } else { - console.error('WrappedChromeRuntimePort only supports onMessage'); - } -}; - -/** - * Wrap the Authenticator app with a MessagePort interface. - * @constructor - * @private - */ -u2f.WrappedAuthenticatorPort_ = function() { - this.requestId_ = -1; - this.requestObject_ = null; -} - -/** - * Launch the Authenticator intent. - * @param {Object} message - */ -u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) { - var intentUrl = - u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ + - ';S.request=' + encodeURIComponent(JSON.stringify(message)) + - ';end'; - document.location = intentUrl; -}; - -/** - * Tells what type of port this is. - * @return {String} port type - */ -u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() { - return "WrappedAuthenticatorPort_"; -}; - - -/** - * Emulates the HTML 5 addEventListener interface. - * @param {string} eventName - * @param {function({data: Object})} handler - */ -u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name == 'message') { - var self = this; - /* Register a callback to that executes when - * chrome injects the response. */ - window.addEventListener( - 'message', self.onRequestUpdate_.bind(self, handler), false); - } else { - console.error('WrappedAuthenticatorPort only supports message'); - } -}; - -/** - * Callback invoked when a response is received from the Authenticator. - * @param function({data: Object}) callback - * @param {Object} message message Object - */ -u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ = - function(callback, message) { - var messageObject = JSON.parse(message.data); - var intentUrl = messageObject['intentURL']; - - var errorCode = messageObject['errorCode']; - var responseObject = null; - if (messageObject.hasOwnProperty('data')) { - responseObject = /** @type {Object} */ ( - JSON.parse(messageObject['data'])); - } - - callback({'data': responseObject}); -}; - -/** - * Base URL for intents to Authenticator. - * @const - * @private - */ -u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ = - 'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE'; - -/** - * Wrap the iOS client app with a MessagePort interface. - * @constructor - * @private - */ -u2f.WrappedIosPort_ = function() {}; - -/** - * Launch the iOS client app request - * @param {Object} message - */ -u2f.WrappedIosPort_.prototype.postMessage = function(message) { - var str = JSON.stringify(message); - var url = "u2f://auth?" + encodeURI(str); - location.replace(url); -}; - -/** - * Tells what type of port this is. - * @return {String} port type - */ -u2f.WrappedIosPort_.prototype.getPortType = function() { - return "WrappedIosPort_"; -}; - -/** - * Emulates the HTML 5 addEventListener interface. - * @param {string} eventName - * @param {function({data: Object})} handler - */ -u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name !== 'message') { - console.error('WrappedIosPort only supports message'); - } -}; - -/** - * Sets up an embedded trampoline iframe, sourced from the extension. - * @param {function(MessagePort)} callback - * @private - */ -u2f.getIframePort_ = function(callback) { - // Create the iframe - var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID; - var iframe = document.createElement('iframe'); - iframe.src = iframeOrigin + '/u2f-comms.html'; - iframe.setAttribute('style', 'display:none'); - document.body.appendChild(iframe); - - var channel = new MessageChannel(); - var ready = function(message) { - if (message.data == 'ready') { - channel.port1.removeEventListener('message', ready); - callback(channel.port1); - } else { - console.error('First event on iframe port was not "ready"'); - } - }; - channel.port1.addEventListener('message', ready); - channel.port1.start(); - - iframe.addEventListener('load', function() { - // Deliver the port to the iframe and initialize - iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]); - }); -}; - - -//High-level JS API - -/** - * Default extension response timeout in seconds. - * @const - */ -u2f.EXTENSION_TIMEOUT_SEC = 30; - -/** - * A singleton instance for a MessagePort to the extension. - * @type {MessagePort|u2f.WrappedChromeRuntimePort_} - * @private - */ -u2f.port_ = null; - -/** - * Callbacks waiting for a port - * @type {Array} - * @private - */ -u2f.waitingForPort_ = []; - -/** - * A counter for requestIds. - * @type {number} - * @private - */ -u2f.reqCounter_ = 0; - -/** - * A map from requestIds to client callbacks - * @type {Object.} - * @private - */ -u2f.callbackMap_ = {}; - -/** - * Creates or retrieves the MessagePort singleton to use. - * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback - * @private - */ -u2f.getPortSingleton_ = function(callback) { - if (u2f.port_) { - callback(u2f.port_); - } else { - if (u2f.waitingForPort_.length == 0) { - u2f.getMessagePort(function(port) { - u2f.port_ = port; - u2f.port_.addEventListener('message', - /** @type {function(Event)} */ (u2f.responseHandler_)); - - // Careful, here be async callbacks. Maybe. - while (u2f.waitingForPort_.length) - u2f.waitingForPort_.shift()(u2f.port_); - }); - } - u2f.waitingForPort_.push(callback); - } -}; - -/** - * Handles response messages from the extension. - * @param {MessageEvent.} message - * @private - */ -u2f.responseHandler_ = function(message) { - var response = message.data; - var reqId = response['requestId']; - if (!reqId || !u2f.callbackMap_[reqId]) { - console.error('Unknown or missing requestId in response.'); - return; - } - var cb = u2f.callbackMap_[reqId]; - delete u2f.callbackMap_[reqId]; - cb(response['responseData']); -}; - -/** - * Dispatches an array of sign requests to available U2F tokens. - * If the JS API version supported by the extension is unknown, it first sends a - * message to the extension to find out the supported API version and then it sends - * the sign request. - * @param {string=} appId - * @param {string=} challenge - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.SignResponse))} callback - * @param {number=} opt_timeoutSeconds - */ -u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) { - if (js_api_version === undefined) { - // Send a message to get the extension to JS API version, then send the actual sign request. - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); - u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds); - }); - } else { - // We know the JS API version. Send the actual sign request in the supported API version. - u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds); - } -}; - -/** - * Dispatches an array of sign requests to available U2F tokens. - * @param {string=} appId - * @param {string=} challenge - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.SignResponse))} callback - * @param {number=} opt_timeoutSeconds - */ -u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); - var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId); - port.postMessage(req); - }); -}; - -/** - * Dispatches register requests to available U2F tokens. An array of sign - * requests identifies already registered tokens. - * If the JS API version supported by the extension is unknown, it first sends a - * message to the extension to find out the supported API version and then it sends - * the register request. - * @param {string=} appId - * @param {Array} registerRequests - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.RegisterResponse))} callback - * @param {number=} opt_timeoutSeconds - */ -u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { - if (js_api_version === undefined) { - // Send a message to get the extension to JS API version, then send the actual register request. - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); - u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, - callback, opt_timeoutSeconds); - }); - } else { - // We know the JS API version. Send the actual register request in the supported API version. - u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, - callback, opt_timeoutSeconds); - } -}; - -/** - * Dispatches register requests to available U2F tokens. An array of sign - * requests identifies already registered tokens. - * @param {string=} appId - * @param {Array} registerRequests - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.RegisterResponse))} callback - * @param {number=} opt_timeoutSeconds - */ -u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); - var req = u2f.formatRegisterRequest_( - appId, registeredKeys, registerRequests, timeoutSeconds, reqId); - port.postMessage(req); - }); -}; - - -/** - * Dispatches a message to the extension to find out the supported - * JS API version. - * If the user is on a mobile phone and is thus using Google Authenticator instead - * of the Chrome extension, don't send the request and simply return 0. - * @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback - * @param {number=} opt_timeoutSeconds - */ -u2f.getApiVersion = function(callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - // If we are using Android Google Authenticator or iOS client app, - // do not fire an intent to ask which JS API version to use. - if (port.getPortType) { - var apiVersion; - switch (port.getPortType()) { - case 'WrappedIosPort_': - case 'WrappedAuthenticatorPort_': - apiVersion = 1.1; - break; - - default: - apiVersion = 0; - break; - } - callback({ 'js_api_version': apiVersion }); - return; - } - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var req = { - type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST, - timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC), - requestId: reqId - }; - port.postMessage(req); - }); -}; diff --git a/includes/Yubico/U2F.php b/includes/Yubico/U2F.php deleted file mode 100644 index bbb6e9a0..00000000 --- a/includes/Yubico/U2F.php +++ /dev/null @@ -1,507 +0,0 @@ -appId = $appId; - $this->attestDir = $attestDir; - } - - /** - * Called to get a registration request to send to a user. - * Returns an array of one registration request and a array of sign requests. - * - * @param array $registrations List of current registrations for this - * user, to prevent the user from registering the same authenticator several - * times. - * @return array An array of two elements, the first containing a - * RegisterRequest the second being an array of SignRequest - * @throws Error - */ - public function getRegisterData(array $registrations = array()) - { - $challenge = $this->createChallenge(); - $request = new RegisterRequest($challenge, $this->appId); - $signs = $this->getAuthenticateData($registrations); - return array($request, $signs); - } - - /** - * Called to verify and unpack a registration message. - * - * @param RegisterRequest $request this is a reply to - * @param object $response response from a user - * @param bool $includeCert set to true if the attestation certificate should be - * included in the returned Registration object - * @return Registration - * @throws Error - */ - public function doRegister($request, $response, $includeCert = true) - { - if( !is_object( $request ) ) { - throw new \InvalidArgumentException('$request of doRegister() method only accepts object.'); - } - - if( !is_object( $response ) ) { - throw new \InvalidArgumentException('$response of doRegister() method only accepts object.'); - } - - if( property_exists( $response, 'errorCode') && $response->errorCode !== 0 ) { - throw new Error('User-agent returned error. Error code: ' . $response->errorCode, ERR_BAD_UA_RETURNING ); - } - - if( !is_bool( $includeCert ) ) { - throw new \InvalidArgumentException('$include_cert of doRegister() method only accepts boolean.'); - } - - $rawReg = $this->base64u_decode($response->registrationData); - $regData = array_values(unpack('C*', $rawReg)); - $clientData = $this->base64u_decode($response->clientData); - $cli = json_decode($clientData); - - if($cli->challenge !== $request->challenge) { - throw new Error('Registration challenge does not match', ERR_UNMATCHED_CHALLENGE ); - } - - $registration = new Registration(); - $offs = 1; - $pubKey = substr($rawReg, $offs, PUBKEY_LEN); - $offs += PUBKEY_LEN; - // Decode the pubKey to make sure it's good. - $tmpKey = $this->pubkey_to_pem($pubKey); - if($tmpKey === null) { - throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE ); - } - $registration->publicKey = base64_encode($pubKey); - $khLen = $regData[$offs++]; - $kh = substr($rawReg, $offs, $khLen); - $offs += $khLen; - $registration->keyHandle = $this->base64u_encode($kh); - - // length of certificate is stored in byte 3 and 4 (excluding the first 4 bytes). - $certLen = 4; - $certLen += ($regData[$offs + 2] << 8); - $certLen += $regData[$offs + 3]; - - $rawCert = $this->fixSignatureUnusedBits(substr($rawReg, $offs, $certLen)); - $offs += $certLen; - $pemCert = "-----BEGIN CERTIFICATE-----\r\n"; - $pemCert .= chunk_split(base64_encode($rawCert), 64); - $pemCert .= "-----END CERTIFICATE-----"; - if($includeCert) { - $registration->certificate = base64_encode($rawCert); - } - if($this->attestDir) { - if(openssl_x509_checkpurpose($pemCert, -1, $this->get_certs()) !== true) { - throw new Error('Attestation certificate can not be validated', ERR_ATTESTATION_VERIFICATION ); - } - } - - if(!openssl_pkey_get_public($pemCert)) { - throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE ); - } - $signature = substr($rawReg, $offs); - - $dataToVerify = chr(0); - $dataToVerify .= hash('sha256', $request->appId, true); - $dataToVerify .= hash('sha256', $clientData, true); - $dataToVerify .= $kh; - $dataToVerify .= $pubKey; - - if(openssl_verify($dataToVerify, $signature, $pemCert, 'sha256') === 1) { - return $registration; - } else { - throw new Error('Attestation signature does not match', ERR_ATTESTATION_SIGNATURE ); - } - } - - /** - * Called to get an authentication request. - * - * @param array $registrations An array of the registrations to create authentication requests for. - * @return array An array of SignRequest - * @throws Error - */ - public function getAuthenticateData(array $registrations) - { - $sigs = array(); - $challenge = $this->createChallenge(); - foreach ($registrations as $reg) { - if( !is_object( $reg ) ) { - throw new \InvalidArgumentException('$registrations of getAuthenticateData() method only accepts array of object.'); - } - - $sig = new SignRequest(); - $sig->appId = $this->appId; - $sig->keyHandle = $reg->keyHandle; - $sig->challenge = $challenge; - $sigs[] = $sig; - } - return $sigs; - } - - /** - * Called to verify an authentication response - * - * @param array $requests An array of outstanding authentication requests - * @param array $registrations An array of current registrations - * @param object $response A response from the authenticator - * @return Registration - * @throws Error - * - * The Registration object returned on success contains an updated counter - * that should be saved for future authentications. - * If the Error returned is ERR_COUNTER_TOO_LOW this is an indication of - * token cloning or similar and appropriate action should be taken. - */ - public function doAuthenticate(array $requests, array $registrations, $response) - { - if( !is_object( $response ) ) { - throw new \InvalidArgumentException('$response of doAuthenticate() method only accepts object.'); - } - - if( property_exists( $response, 'errorCode') && $response->errorCode !== 0 ) { - throw new Error('User-agent returned error. Error code: ' . $response->errorCode, ERR_BAD_UA_RETURNING ); - } - - /** @var object|null $req */ - $req = null; - - /** @var object|null $reg */ - $reg = null; - - $clientData = $this->base64u_decode($response->clientData); - $decodedClient = json_decode($clientData); - foreach ($requests as $req) { - if( !is_object( $req ) ) { - throw new \InvalidArgumentException('$requests of doAuthenticate() method only accepts array of object.'); - } - - if($req->keyHandle === $response->keyHandle && $req->challenge === $decodedClient->challenge) { - break; - } - - $req = null; - } - if($req === null) { - throw new Error('No matching request found', ERR_NO_MATCHING_REQUEST ); - } - foreach ($registrations as $reg) { - if( !is_object( $reg ) ) { - throw new \InvalidArgumentException('$registrations of doAuthenticate() method only accepts array of object.'); - } - - if($reg->keyHandle === $response->keyHandle) { - break; - } - $reg = null; - } - if($reg === null) { - throw new Error('No matching registration found', ERR_NO_MATCHING_REGISTRATION ); - } - $pemKey = $this->pubkey_to_pem($this->base64u_decode($reg->publicKey)); - if($pemKey === null) { - throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE ); - } - - $signData = $this->base64u_decode($response->signatureData); - $dataToVerify = hash('sha256', $req->appId, true); - $dataToVerify .= substr($signData, 0, 5); - $dataToVerify .= hash('sha256', $clientData, true); - $signature = substr($signData, 5); - - if(openssl_verify($dataToVerify, $signature, $pemKey, 'sha256') === 1) { - $ctr = unpack("Nctr", substr($signData, 1, 4)); - $counter = $ctr['ctr']; - /* TODO: wrap-around should be handled somehow.. */ - if($counter > $reg->counter) { - $reg->counter = $counter; - return $reg; - } else { - throw new Error('Counter too low.', ERR_COUNTER_TOO_LOW ); - } - } else { - throw new Error('Authentication failed', ERR_AUTHENTICATION_FAILURE ); - } - } - - /** - * @return array - */ - private function get_certs() - { - $files = array(); - $dir = $this->attestDir; - if($dir && $handle = opendir($dir)) { - while(false !== ($entry = readdir($handle))) { - if(is_file("$dir/$entry")) { - $files[] = "$dir/$entry"; - } - } - closedir($handle); - } - return $files; - } - - /** - * @param string $data - * @return string - */ - private function base64u_encode($data) - { - return trim(strtr(base64_encode($data), '+/', '-_'), '='); - } - - /** - * @param string $data - * @return string - */ - private function base64u_decode($data) - { - return base64_decode(strtr($data, '-_', '+/')); - } - - /** - * @param string $key - * @return null|string - */ - private function pubkey_to_pem($key) - { - if(strlen($key) !== PUBKEY_LEN || $key[0] !== "\x04") { - return null; - } - - /* - * Convert the public key to binary DER format first - * Using the ECC SubjectPublicKeyInfo OIDs from RFC 5480 - * - * SEQUENCE(2 elem) 30 59 - * SEQUENCE(2 elem) 30 13 - * OID1.2.840.10045.2.1 (id-ecPublicKey) 06 07 2a 86 48 ce 3d 02 01 - * OID1.2.840.10045.3.1.7 (secp256r1) 06 08 2a 86 48 ce 3d 03 01 07 - * BIT STRING(520 bit) 03 42 ..key.. - */ - $der = "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01"; - $der .= "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42"; - $der .= "\0".$key; - - $pem = "-----BEGIN PUBLIC KEY-----\r\n"; - $pem .= chunk_split(base64_encode($der), 64); - $pem .= "-----END PUBLIC KEY-----"; - - return $pem; - } - - /** - * @return string - * @throws Error - */ - private function createChallenge() - { - $challenge = openssl_random_pseudo_bytes(32, $crypto_strong ); - if( $crypto_strong !== true ) { - throw new Error('Unable to obtain a good source of randomness', ERR_BAD_RANDOM); - } - - $challenge = $this->base64u_encode( $challenge ); - - return $challenge; - } - - /** - * Fixes a certificate where the signature contains unused bits. - * - * @param string $cert - * @return mixed - */ - private function fixSignatureUnusedBits($cert) - { - if(in_array(hash('sha256', $cert), $this->FIXCERTS)) { - $cert[strlen($cert) - 257] = "\0"; - } - return $cert; - } -} - -/** - * Class for building a registration request - * - * @package u2flib_server - */ -class RegisterRequest -{ - /** Protocol version */ - public $version = U2F_VERSION; - - /** Registration challenge */ - public $challenge; - - /** Application id */ - public $appId; - - /** - * @param string $challenge - * @param string $appId - * @internal - */ - public function __construct($challenge, $appId) - { - $this->challenge = $challenge; - $this->appId = $appId; - } -} - -/** - * Class for building up an authentication request - * - * @package u2flib_server - */ -class SignRequest -{ - /** Protocol version */ - public $version = U2F_VERSION; - - /** Authentication challenge */ - public $challenge; - - /** Key handle of a registered authenticator */ - public $keyHandle; - - /** Application id */ - public $appId; -} - -/** - * Class returned for successful registrations - * - * @package u2flib_server - */ -class Registration -{ - /** The key handle of the registered authenticator */ - public $keyHandle; - - /** The public key of the registered authenticator */ - public $publicKey; - - /** The attestation certificate of the registered authenticator */ - public $certificate; - - /** The counter associated with this registration */ - public $counter = -1; -} - -/** - * Error class, returned on errors - * - * @package u2flib_server - */ -class Error extends \Exception -{ - /** - * Override constructor and make message and code mandatory - * @param string $message - * @param int $code - * @param \Exception|null $previous - */ - public function __construct($message, $code, ?\Exception $previous = null) { - parent::__construct($message, $code, $previous); - } -} diff --git a/providers/class-two-factor-fido-u2f-admin-list-table.php b/providers/class-two-factor-fido-u2f-admin-list-table.php deleted file mode 100644 index b63d3cf8..00000000 --- a/providers/class-two-factor-fido-u2f-admin-list-table.php +++ /dev/null @@ -1,160 +0,0 @@ - wp_strip_all_tags( __( 'Name', 'two-factor' ) ), - 'added' => wp_strip_all_tags( __( 'Added', 'two-factor' ) ), - 'last_used' => wp_strip_all_tags( __( 'Last Used', 'two-factor' ) ), - ); - } - - /** - * Prepares the list of items for displaying. - * - * @since 0.1-dev - */ - public function prepare_items() { - $columns = $this->get_columns(); - $hidden = array(); - $sortable = array(); - $primary = 'name'; - $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); - } - - /** - * Generates content for a single row of the table - * - * @since 0.1-dev - * @access protected - * - * @param object $item The current item. - * @param string $column_name The current column name. - * @return string - */ - protected function column_default( $item, $column_name ) { - switch ( $column_name ) { - case 'name': - $out = ''; - - $actions = array( - 'rename hide-if-no-js' => Two_Factor_FIDO_U2F_Admin::rename_link( $item ), - 'delete' => Two_Factor_FIDO_U2F_Admin::delete_link( $item ), - ); - - return esc_html( $item->name ) . $out . self::row_actions( $actions ); - case 'added': - return gmdate( get_option( 'date_format', 'r' ), $item->added ); - case 'last_used': - return gmdate( get_option( 'date_format', 'r' ), $item->last_used ); - default: - return 'WTF^^?'; - } - } - - /** - * Generates custom table navigation to prevent conflicting nonces. - * - * @since 0.1-dev - * @access protected - * - * @param string $which The location of the bulk actions: 'top' or 'bottom'. - */ - protected function display_tablenav( $which ) { - // Not used for the Security key list. - } - - /** - * Generates content for a single row of the table - * - * @since 0.1-dev - * @access public - * - * @param object $item The current item. - */ - public function single_row( $item ) { - ?> - - single_row_columns( $item ); ?> - - - - - - - - -
- getRegisterData( $security_keys ); - list( $req, $sigs ) = $data; - - update_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, $req ); - } catch ( Exception $e ) { - return false; - } - - wp_enqueue_style( - 'fido-u2f-admin', - plugins_url( 'css/fido-u2f-admin.css', __FILE__ ), - null, - self::asset_version() - ); - - wp_enqueue_script( - 'fido-u2f-admin', - plugins_url( 'js/fido-u2f-admin.js', __FILE__ ), - array( 'jquery', 'fido-u2f-api' ), - self::asset_version(), - true - ); - - /** - * Pass a U2F challenge and user data to our scripts - */ - - $translation_array = array( - 'user_id' => $user_id, - 'register' => array( - 'request' => $req, - 'sigs' => $sigs, - ), - 'text' => array( - 'insert' => esc_html__( 'Now insert (and tap) your Security Key.', 'two-factor' ), - 'error' => esc_html__( 'U2F request failed.', 'two-factor' ), - 'error_codes' => array( - // Map u2f.ErrorCodes to error messages. - 0 => esc_html__( 'Request OK.', 'two-factor' ), - 1 => esc_html__( 'Other U2F error.', 'two-factor' ), - 2 => esc_html__( 'Bad U2F request.', 'two-factor' ), - 3 => esc_html__( 'Unsupported U2F configuration.', 'two-factor' ), - 4 => esc_html__( 'U2F device ineligible.', 'two-factor' ), - 5 => esc_html__( 'U2F request timeout reached.', 'two-factor' ), - ), - 'u2f_not_supported' => esc_html__( 'FIDO U2F appears to be not supported by your web browser. Try using Google Chrome or Firefox.', 'two-factor' ), - ), - ); - - wp_localize_script( - 'fido-u2f-admin', - 'u2fL10n', - $translation_array - ); - - /** - * Script for admin UI - */ - - wp_enqueue_script( - 'inline-edit-key', - plugins_url( 'js/fido-u2f-admin-inline-edit.js', __FILE__ ), - array( 'jquery' ), - self::asset_version(), - true - ); - - wp_localize_script( - 'inline-edit-key', - 'inlineEditL10n', - array( - 'error' => esc_html__( 'Error while saving the changes.', 'two-factor' ), - ) - ); - } - - /** - * Return the current asset version number. - * - * Added as own helper to allow swapping the implementation once we inject - * it as a dependency. - * - * @return string - */ - protected static function asset_version() { - return Two_Factor_FIDO_U2F::asset_version(); - } - - /** - * Display the security key section in a users profile. - * - * This executes during the `show_user_security_settings` action. - * - * @since 0.1-dev - * - * @access public - * @static - * - * @param WP_User $user WP_User object of the logged-in user. - */ - public static function show_user_profile( $user ) { - if ( ! Two_Factor_FIDO_U2F::is_supported_for_user( $user ) ) { - return; - } - - wp_nonce_field( "user_security_keys-{$user->ID}", '_nonce_user_security_keys' ); - $new_key = false; - - $security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user->ID ); - if ( $security_keys ) { - foreach ( $security_keys as &$security_key ) { - if ( property_exists( $security_key, 'new' ) ) { - $new_key = true; - unset( $security_key->new ); - - // If we've got a new one, update the db record to not save it there any longer. - Two_Factor_FIDO_U2F::update_security_key( $user->ID, $security_key ); - } - } - unset( $security_key ); - } - - ?> -
-

- - -

- -

- - -
- - - - - -
- - -
-

-
- - -

- - items = $security_keys; - $u2f_list_table->prepare_items(); - $u2f_list_table->display(); - $u2f_list_table->inline_edit(); - ?> -
- doRegister( get_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, true ), $response ); - $reg->new = true; - - Two_Factor_FIDO_U2F::add_security_key( $user_id, $reg ); - } catch ( Exception $e ) { - return; - } - - delete_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY ); - - wp_safe_redirect( - add_query_arg( - array( - 'new_app_pass' => 1, - ), - wp_get_referer() - ) . '#security-keys-section' - ); - exit; - } - } - - /** - * Catch the delete security key request. - * - * This executes during the `load-profile.php` & `load-user-edit.php` actions. - * - * @since 0.1-dev - * - * @access public - * @static - */ - public static function catch_delete_security_key() { - $user_id = Two_Factor_Core::current_user_being_edited(); - - if ( ! empty( $user_id ) && ! empty( $_REQUEST['delete_security_key'] ) ) { - $slug = $_REQUEST['delete_security_key']; - - check_admin_referer( "delete_security_key-{$slug}", '_nonce_delete_security_key' ); - - Two_Factor_FIDO_U2F::delete_security_key( $user_id, $slug ); - - wp_safe_redirect( remove_query_arg( 'new_app_pass', wp_get_referer() ) . '#security-keys-section' ); - exit; - } - } - - /** - * Generate a link to rename a specified security key. - * - * @since 0.1-dev - * - * @access public - * @static - * - * @param array $item The current item. - * @return string - */ - public static function rename_link( $item ) { - return sprintf( '%s', esc_html__( 'Rename', 'two-factor' ) ); - } - - /** - * Generate a link to delete a specified security key. - * - * @since 0.1-dev - * - * @access public - * @static - * - * @param array $item The current item. - * @return string - */ - public static function delete_link( $item ) { - $delete_link = add_query_arg( 'delete_security_key', $item->keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $delete_link = wp_nonce_url( $delete_link, "delete_security_key-{$item->keyHandle}", '_nonce_delete_security_key' ); - return sprintf( '%2$s', esc_url( $delete_link ), esc_html__( 'Delete', 'two-factor' ) ); - } - - /** - * Ajax handler for quick edit saving for a security key. - * - * @since 0.1-dev - * - * @access public - * @static - */ - public static function wp_ajax_inline_save() { - check_ajax_referer( 'keyinlineeditnonce', '_inline_edit' ); - - require_once TWO_FACTOR_DIR . 'providers/class-two-factor-fido-u2f-admin-list-table.php'; - $wp_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table(); - - if ( ! isset( $_POST['keyHandle'] ) ) { - wp_die(); - } - - $user_id = Two_Factor_Core::current_user_being_edited(); - $security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user_id ); - if ( ! $security_keys ) { - wp_die(); - } - - foreach ( $security_keys as &$key ) { - if ( $key->keyHandle === $_POST['keyHandle'] ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - break; - } - } - - $key->name = $_POST['name']; - - $updated = Two_Factor_FIDO_U2F::update_security_key( $user_id, $key ); - if ( ! $updated ) { - wp_die( esc_html__( 'Item not updated.', 'two-factor' ) ); - } - $wp_list_table->single_row( $key ); - wp_die(); - } -} diff --git a/providers/class-two-factor-fido-u2f.php b/providers/class-two-factor-fido-u2f.php deleted file mode 100644 index c6b6d047..00000000 --- a/providers/class-two-factor-fido-u2f.php +++ /dev/null @@ -1,407 +0,0 @@ - -

- ID ); - $data = self::$u2f->getAuthenticateData( $keys ); - update_user_meta( $user->ID, self::AUTH_DATA_USER_META_KEY, $data ); - } catch ( Exception $e ) { - ?> -

- $data, - ) - ); - - wp_enqueue_script( 'fido-u2f-login' ); - - ?> - -

- - - - ID, self::AUTH_DATA_USER_META_KEY, true ); - - $response = json_decode( stripslashes( $_REQUEST['u2f_response'] ) ); - - $keys = self::get_security_keys( $user->ID ); - - try { - $reg = self::$u2f->doAuthenticate( $requests, $keys, $response ); - - $reg->last_used = time(); - - self::update_security_key( $user->ID, $reg ); - - return true; - } catch ( Exception $e ) { - return false; - } - } - - /** - * Whether this Two Factor provider is configured and available for the user specified. - * - * @since 0.1-dev - * - * @param WP_User $user WP_User object of the logged-in user. - * @return boolean - */ - public function is_available_for_user( $user ) { - return (bool) self::get_security_keys( $user->ID ); - } - - /** - * Inserts markup at the end of the user profile field for this provider. - * - * @since 0.1-dev - * - * @param WP_User $user WP_User object of the logged-in user. - */ - public function user_options( $user ) { - ?> -

- -

- keyHandle ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - || ! property_exists( $register, 'publicKey' ) || empty( $register->publicKey ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - || ! property_exists( $register, 'certificate' ) || empty( $register->certificate ) - || ! property_exists( $register, 'counter' ) || ( -1 > $register->counter ) - ) { - return false; - } - - $register = array( - 'keyHandle' => $register->keyHandle, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - 'publicKey' => $register->publicKey, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - 'certificate' => $register->certificate, - 'counter' => $register->counter, - ); - - $register['name'] = __( 'New Security Key', 'two-factor' ); - $register['added'] = time(); - $register['last_used'] = $register['added']; - - return add_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, $register ); - } - - /** - * Retrieve registered security keys for a user. - * - * @since 0.1-dev - * - * @param int $user_id User ID. - * @return array|bool Array of keys on success, false on failure. - */ - public static function get_security_keys( $user_id ) { - if ( ! is_numeric( $user_id ) ) { - return false; - } - - $keys = get_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY ); - if ( $keys ) { - foreach ( $keys as &$key ) { - $key = (object) $key; - } - unset( $key ); - } - - return $keys; - } - - /** - * Update registered security key. - * - * Use the $prev_value parameter to differentiate between meta fields with the - * same key and user ID. - * - * If the meta field for the user does not exist, it will be added. - * - * @since 0.1-dev - * - * @param int $user_id User ID. - * @param object $data The data of registered security key. - * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure. - */ - public static function update_security_key( $user_id, $data ) { - if ( ! is_numeric( $user_id ) ) { - return false; - } - - if ( - ! is_object( $data ) - || ! property_exists( $data, 'keyHandle' ) || empty( $data->keyHandle ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - || ! property_exists( $data, 'publicKey' ) || empty( $data->publicKey ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - || ! property_exists( $data, 'certificate' ) || empty( $data->certificate ) - || ! property_exists( $data, 'counter' ) || ( -1 > $data->counter ) - ) { - return false; - } - - $keys = self::get_security_keys( $user_id ); - if ( $keys ) { - foreach ( $keys as $key ) { - if ( $key->keyHandle === $data->keyHandle ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - return update_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, (array) $data, (array) $key ); - } - } - } - - return self::add_security_key( $user_id, $data ); - } - - /** - * Remove registered security key matching criteria from a user. - * - * @since 0.1-dev - * - * @param int $user_id User ID. - * @param string $keyHandle Optional. Key handle. - * @return bool True on success, false on failure. - */ - public static function delete_security_key( $user_id, $keyHandle = null ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase - global $wpdb; - - if ( ! is_numeric( $user_id ) ) { - return false; - } - - $user_id = absint( $user_id ); - if ( ! $user_id ) { - return false; - } - - $keyHandle = wp_unslash( $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase - $keyHandle = maybe_serialize( $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase - - $query = $wpdb->prepare( "SELECT umeta_id FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id = %d", self::REGISTERED_KEY_USER_META_KEY, $user_id ); - - if ( $keyHandle ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase - $key_handle_lookup = sprintf( ':"%s";s:', $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase - - $query .= $wpdb->prepare( - ' AND meta_value LIKE %s', - '%' . $wpdb->esc_like( $key_handle_lookup ) . '%' - ); - } - - $meta_ids = $wpdb->get_col( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - if ( ! count( $meta_ids ) ) { - return false; - } - - foreach ( $meta_ids as $meta_id ) { - delete_metadata_by_mid( 'user', $meta_id ); - } - - return true; - } - - /** - * Return user meta keys to delete during plugin uninstall. - * - * @return array - */ - public static function uninstall_user_meta_keys() { - return array( - self::REGISTERED_KEY_USER_META_KEY, - self::AUTH_DATA_USER_META_KEY, - '_two_factor_fido_u2f_register_request', // From Two_Factor_FIDO_U2F_Admin which is not loaded during uninstall. - ); - } -} diff --git a/providers/css/fido-u2f-admin.css b/providers/css/fido-u2f-admin.css deleted file mode 100644 index 96ca78aa..00000000 --- a/providers/css/fido-u2f-admin.css +++ /dev/null @@ -1,12 +0,0 @@ -#security-keys-section .wp-list-table { - margin-bottom: 2em; -} - -#security-keys-section .register-security-key .spinner { - float: none; -} - -#security-keys-section .security-key-status { - vertical-align: middle; - font-style: italic; -} diff --git a/providers/js/fido-u2f-admin-inline-edit.js b/providers/js/fido-u2f-admin-inline-edit.js deleted file mode 100644 index b7b0123e..00000000 --- a/providers/js/fido-u2f-admin-inline-edit.js +++ /dev/null @@ -1,150 +0,0 @@ -/* global window, document, jQuery, inlineEditL10n, ajaxurl */ -var inlineEditKey; - -( function( $ ) { - inlineEditKey = { - - init: function() { - var t = this, - row = $( '#security-keys-section #inline-edit' ); - - t.what = '#key-'; - - $( '#security-keys-section #the-list' ).on( 'click', 'a.editinline', function() { - inlineEditKey.edit( this ); - return false; - } ); - - // Prepare the edit row. - row.keyup( function( event ) { - if ( 27 === event.which ) { - return inlineEditKey.revert(); - } - } ); - - $( 'a.cancel', row ).click( function() { - return inlineEditKey.revert(); - } ); - - $( 'a.save', row ).click( function() { - return inlineEditKey.save( this ); - } ); - - $( 'input, select', row ).keydown( function( event ) { - if ( 13 === event.which ) { - return inlineEditKey.save( this ); - } - } ); - }, - - toggle: function( el ) { - var t = this; - - if ( 'none' === $( t.what + t.getId( el ) ).css( 'display' ) ) { - t.revert(); - } else { - t.edit( el ); - } - }, - - edit: function( id ) { - var editRow, rowData, val, - t = this; - t.revert(); - - if ( 'object' === typeof id ) { - id = t.getId( id ); - } - - editRow = $( '#inline-edit' ).clone( true ); - rowData = $( '#inline_' + id ); - - $( 'td', editRow ).attr( 'colspan', $( 'th:visible, td:visible', '#security-keys-section .widefat thead' ).length ); - - $( t.what + id ).hide().after( editRow ).after( '' ); - - val = $( '.name', rowData ); - val.find( 'img' ).replaceWith( function() { - return this.alt; - } ); - val = val.text(); - $( ':input[name="name"]', editRow ).val( val ); - - $( editRow ).attr( 'id', 'edit-' + id ).addClass( 'inline-editor' ).show(); - $( '.ptitle', editRow ).eq( 0 ).focus(); - - return false; - }, - - save: function( id ) { - var params, fields; - - if ( 'object' === typeof id ) { - id = this.getId( id ); - } - - $( '#security-keys-section table.widefat .spinner' ).addClass( 'is-active' ); - - params = { - action: 'inline-save-key', - keyHandle: id, - user_id: window.u2fL10n.user_id - }; - - fields = $( '#edit-' + id ).find( ':input' ).serialize(); - params = fields + '&' + $.param( params ); - - // Make ajax request. - $.post( ajaxurl, params, - function( r ) { - var row, newID; - $( '#security-keys-section table.widefat .spinner' ).removeClass( 'is-active' ); - - if ( r ) { - if ( -1 !== r.indexOf( '' )[0].submit.call( $( '#your-profile' )[0] ); - } ); - } ); -}( jQuery ) ); diff --git a/providers/js/fido-u2f-login.js b/providers/js/fido-u2f-login.js deleted file mode 100644 index 28295307..00000000 --- a/providers/js/fido-u2f-login.js +++ /dev/null @@ -1,16 +0,0 @@ -/* global window, u2f, u2fL10n, jQuery */ -( function( $ ) { - if ( ! window.u2fL10n ) { - window.console.error( 'u2fL10n is not defined' ); - return; - } - - u2f.sign( u2fL10n.request[0].appId, u2fL10n.request[0].challenge, u2fL10n.request, function( data ) { - if ( data.errorCode ) { - window.console.error( 'Registration Failed', data.errorCode ); - } else { - $( '#u2f_response' ).val( JSON.stringify( data ) ); - $( '#loginform' ).submit(); - } - } ); -}( jQuery ) ); diff --git a/readme.txt b/readme.txt index f166a0c4..ec245dca 100644 --- a/readme.txt +++ b/readme.txt @@ -6,7 +6,7 @@ Stable tag: 0.15.0 License: GPL-2.0-or-later License URI: https://spdx.org/licenses/GPL-2.0-or-later.html -Enable Two-Factor Authentication (2FA) using time-based one-time passwords (TOTP), Universal 2nd Factor (U2F), email, and backup verification codes. +Enable Two-Factor Authentication (2FA) using time-based one-time passwords (TOTP), email, and backup verification codes. == Description == @@ -23,7 +23,6 @@ The Two-Factor plugin adds an extra layer of security to your WordPress login by 3. **Choose your methods**: Enable one or more authentication providers (noting a site admin may have hidden one or more so what is available could vary): - **Authenticator App (TOTP)** - Use apps like Google Authenticator, Authy, or 1Password - **Email Codes** - Receive one-time codes via email - - **FIDO U2F Security Keys** - Use physical security keys (requires HTTPS) - **Backup Codes** - Generate one-time backup codes for emergencies - **Dummy Method** - For testing purposes only (requires WP_DEBUG) 4. **Configure each method**: Follow the setup instructions for each enabled provider @@ -57,11 +56,7 @@ The Two-Factor plugin adds an extra layer of security to your WordPress login by - **Best for**: Users who prefer email-based authentication ### FIDO U2F Security Keys -- **Security**: High - Hardware-based authentication -- **Setup**: Register physical security keys (USB, NFC, or Bluetooth) -- **Requirements**: HTTPS connection required, compatible browser needed -- **Browser Support**: Chrome, Firefox, Edge (varies by key type) -- **Best for**: Users with security keys who want maximum security +- Deprecated and removed due to loss of browser support. ### Dummy Method - **Security**: None - Always succeeds @@ -72,11 +67,9 @@ The Two-Factor plugin adds an extra layer of security to your WordPress login by ## Important Notes ### HTTPS Requirement -- FIDO U2F Security Keys require an HTTPS connection to function - Other methods work on both HTTP and HTTPS sites ### Browser Compatibility -- FIDO U2F requires a compatible browser and may not work on all devices - TOTP and email methods work on all devices and browsers ### Account Recovery @@ -135,7 +128,7 @@ If you have backup codes enabled, you can use one of those to regain access. If = Can I use this plugin with WebAuthn? = -The plugin currently supports FIDO U2F, which is the predecessor to WebAuthn. For full WebAuthn support, you may want to look into additional plugins that extend this functionality. The current U2F implementation requires HTTPS and has browser compatibility limitations. +The plugin previously supported FIDO U2F, which was a predecessor to WebAuthn. There is an open issue to add WebAuthn support here: https://github.com/WordPress/two-factor/pull/427 = Is there a recommended way to use passkeys or hardware security keys with Two-Factor? = @@ -145,7 +138,6 @@ Yes. For passkeys and hardware security keys, you can install the Two-Factor Pro == Screenshots == 1. Two-factor options under User Profile - Shows the main configuration area where users can enable different authentication methods. -2. U2F Security Keys section under User Profile - Displays the security key management interface for registering and managing FIDO U2F devices. 3. Email Code Authentication during WordPress Login - Shows the email verification screen that appears during login. 4. Authenticator App (TOTP) setup with QR code - Demonstrates the QR code generation and manual key entry for TOTP setup. 5. Backup codes generation and management - Shows the backup codes interface for generating and managing emergency access codes. diff --git a/tests/providers/class-two-factor-fido-u2f.php b/tests/providers/class-two-factor-fido-u2f.php deleted file mode 100644 index 1c529c15..00000000 --- a/tests/providers/class-two-factor-fido-u2f.php +++ /dev/null @@ -1,222 +0,0 @@ -u2f = new u2flib_server\U2F( 'http://demo.example.com' ); - - $this->provider = Two_Factor_FIDO_U2F::get_instance(); - } catch ( Exception $e ) { - $this->markTestSkipped( 'Could not create U2F provider!' ); - } - } - - /** - * Verify the label value. - */ - public function test_get_label() { - $this->assertStringContainsString( 'FIDO U2F Security Keys', $this->provider->get_label() ); - } - - /** - * Verify adding a security key. - */ - public function test_add_security_key() { - $req = json_decode( '{"version":"U2F_V2","challenge":"yKA0x075tjJ-GE7fKTfnzTOSaNUOWQxRd9TWz5aFOg8","appId":"http://demo.example.com"}' ); - $resp = json_decode( '{ "registrationData": "BQQtEmhWVgvbh-8GpjsHbj_d5FB9iNoRL8mNEq34-ANufKWUpVdIj6BSB_m3eMoZ3GqnaDy3RA5eWP8mhTkT1Ht3QAk1GsmaPIQgXgvrBkCQoQtMFvmwYPfW5jpRgoMPFxquHS7MTt8lofZkWAK2caHD-YQQdaRBgd22yWIjPuWnHOcwggLiMIHLAgEBMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMTEll1YmljbyBVMkYgVGVzdCBDQTAeFw0xNDA1MTUxMjU4NTRaFw0xNDA2MTQxMjU4NTRaMB0xGzAZBgNVBAMTEll1YmljbyBVMkYgVGVzdCBFRTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNsK2_Uhx1zOY9ym4eglBg2U5idUGU-dJK8mGr6tmUQflaNxkQo6IOc-kV4T6L44BXrVeqN-dpCPr-KKlLYw650wDQYJKoZIhvcNAQELBQADggIBAJVAa1Bhfa2Eo7TriA_jMA8togoA2SUE7nL6Z99YUQ8LRwKcPkEpSpOsKYWJLaR6gTIoV3EB76hCiBaWN5HV3-CPyTyNsM2JcILsedPGeHMpMuWrbL1Wn9VFkc7B3Y1k3OmcH1480q9RpYIYr-A35zKedgV3AnvmJKAxVhv9GcVx0_CewHMFTryFuFOe78W8nFajutknarupekDXR4tVcmvj_ihJcST0j_Qggeo4_3wKT98CgjmBgjvKCd3Kqg8n9aSDVWyaOZsVOhZj3Fv5rFu895--D4qiPDETozJIyliH-HugoQpqYJaTX10mnmMdCa6aQeW9CEf-5QmbIP0S4uZAf7pKYTNmDQ5z27DVopqaFw00MIVqQkae_zSPX4dsNeeoTTXrwUGqitLaGap5ol81LKD9JdP3nSUYLfq0vLsHNDyNgb306TfbOenRRVsgQS8tJyLcknSKktWD_Qn7E5vjOXprXPrmdp7g5OPvrbz9QkWa1JTRfo2n2AXV02LPFc-UfR9bWCBEIJBxvmbpmqt0MnBTHWnth2b0CU_KJTDCY3kAPLGbOT8A4KiI73pRW-e9SWTaQXskw3Ei_dHRILM_l9OXsqoYHJ4Dd3tbfvmjoNYggSw4j50l3unI9d1qR5xlBFpW5sLr8gKX4bnY4SR2nyNiOQNLyPc0B0nW502aMEUCIQDTGOX-i_QrffJDY8XvKbPwMuBVrOSO-ayvTnWs_WSuDQIgZ7fMAvD_Ezyy5jg6fQeuOkoJi8V2naCtzV-HTly8Nww=", "clientData": "eyAiY2hhbGxlbmdlIjogInlLQTB4MDc1dGpKLUdFN2ZLVGZuelRPU2FOVU9XUXhSZDlUV3o1YUZPZzgiLCAib3JpZ2luIjogImh0dHA6XC9cL2RlbW8uZXhhbXBsZS5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5maW5pc2hFbnJvbGxtZW50IiB9" }' ); - $reg = $this->u2f->doRegister( $req, $resp ); - - $add_method = new ReflectionMethod( $this->provider, 'add_security_key' ); - $add_method->setAccessible( true ); - $delete_method = new ReflectionMethod( $this->provider, 'delete_security_key' ); - $delete_method->setAccessible( true ); - - $user_id = self::factory()->user->create(); - $this->assertIsInt( $add_method->invoke( $this->provider, $user_id, $reg ) ); - - $delete_method->invoke( $this->provider, $user_id ); - } - - /** - * Verify adding a second security key. - */ - public function test_add_security_key2() { - $reg = json_decode( '{"keyHandle":"j_k-SThUkpALxdWPS8PjBzxaNUMj3WQIGz1rHIDMRnZCMr26C7xWItTRgiRqeqPVMPgSC6WvBJcKgqNNcReDAw","publicKey":"BBLL5S5hWjGRZzpw4kZnDMbAVTdWtlBFaCt5Fsn7sPWAXdInZpVdk\/bGNVfm43+uUmB2w6u90lf57PXQghhbF4I=","certificate":"MIICLjCCARigAwIBAgIECmML\/zALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMCkxJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE3NDI2MzI5NTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKQjZF26iyPtbNnl5IuTKs\/fRWTHVzHxz1IHRRBrSbqWD60PCqUJPe4zkIRFqBa4NnzdhVcS80nlZuY3ANQm0J+jJjAkMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4yMAsGCSqGSIb3DQEBCwOCAQEAZTmwMqHPxEjSB64Umwq2tGDKplAcEzrwmg6kgS8KPkJKXKSu9T1H6XBM9+LAE9cN48oUirFFmDIlTbZRXU2Vm2qO9OdrSVFY+qdbF9oti8CKAmPHuJZSW6ii7qNE59dHKUaP4lDYpnhRDqttWSUalh2LPDJQUpO9bsJPkgNZAhBUQMYZXL\/MQZLRYkX+ld7llTNOX5u7n\/4Y5EMr+lqOyVVC9lQ6JP6xoa9q6Zp9+Y9ZmLCecrrcuH6+pLDgAzPcc8qxhC2OR1B0ZSpI9RBgcT0KqnVE0tq1KEDeokPqF3MgmDRkJ++\/a2pV0wAYfPC3tC57BtBdH\/UXEB8xZVFhtA==","counter":-1}' ); - - $add_method = new ReflectionMethod( $this->provider, 'add_security_key' ); - $add_method->setAccessible( true ); - $delete_method = new ReflectionMethod( $this->provider, 'delete_security_key' ); - $delete_method->setAccessible( true ); - - $user_id = self::factory()->user->create(); - $this->assertIsInt( $add_method->invoke( $this->provider, $user_id, $reg ) ); - - $delete_method->invoke( $this->provider, $user_id ); - } - - /** - * Verify retrieving security keys. - */ - public function test_get_security_keys() { - $reg = json_decode( '{"keyHandle":"j_k-SThUkpALxdWPS8PjBzxaNUMj3WQIGz1rHIDMRnZCMr26C7xWItTRgiRqeqPVMPgSC6WvBJcKgqNNcReDAw","publicKey":"BBLL5S5hWjGRZzpw4kZnDMbAVTdWtlBFaCt5Fsn7sPWAXdInZpVdk\/bGNVfm43+uUmB2w6u90lf57PXQghhbF4I=","certificate":"MIICLjCCARigAwIBAgIECmML\/zALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMCkxJzAlBgNVBAMMHll1YmljbyBVMkYgRUUgU2VyaWFsIDE3NDI2MzI5NTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKQjZF26iyPtbNnl5IuTKs\/fRWTHVzHxz1IHRRBrSbqWD60PCqUJPe4zkIRFqBa4NnzdhVcS80nlZuY3ANQm0J+jJjAkMCIGCSsGAQQBgsQKAgQVMS4zLjYuMS40LjEuNDE0ODIuMS4yMAsGCSqGSIb3DQEBCwOCAQEAZTmwMqHPxEjSB64Umwq2tGDKplAcEzrwmg6kgS8KPkJKXKSu9T1H6XBM9+LAE9cN48oUirFFmDIlTbZRXU2Vm2qO9OdrSVFY+qdbF9oti8CKAmPHuJZSW6ii7qNE59dHKUaP4lDYpnhRDqttWSUalh2LPDJQUpO9bsJPkgNZAhBUQMYZXL\/MQZLRYkX+ld7llTNOX5u7n\/4Y5EMr+lqOyVVC9lQ6JP6xoa9q6Zp9+Y9ZmLCecrrcuH6+pLDgAzPcc8qxhC2OR1B0ZSpI9RBgcT0KqnVE0tq1KEDeokPqF3MgmDRkJ++\/a2pV0wAYfPC3tC57BtBdH\/UXEB8xZVFhtA==","counter":-1}' ); - - $add_method = new ReflectionMethod( $this->provider, 'add_security_key' ); - $add_method->setAccessible( true ); - $get_method = new ReflectionMethod( $this->provider, 'get_security_keys' ); - $get_method->setAccessible( true ); - $delete_method = new ReflectionMethod( $this->provider, 'delete_security_key' ); - $delete_method->setAccessible( true ); - - $user_id = self::factory()->user->create(); - - $add_method->invoke( $this->provider, $user_id, $reg ); - $actual = $get_method->invoke( $this->provider, $user_id ); - - $this->assertEquals( $reg->keyHandle, $actual[0]->keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $this->assertEquals( $reg->publicKey, $actual[0]->publicKey ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $this->assertEquals( $reg->certificate, $actual[0]->certificate ); - $this->assertEquals( $reg->counter, $actual[0]->counter ); - - $delete_method->invoke( $this->provider, $user_id ); - } - - /** - * Verify updating a security key. - */ - public function test_update_security_key() { - $reqs = array( json_decode( '{"version":"U2F_V2","challenge":"fEnc9oV79EaBgK5BoNERU5gPKM2XGYWrz4fUjgc0Q7g","keyHandle":"CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w","appId":"http://demo.example.com"}' ) ); - $regs = array( json_decode( '{"keyHandle":"CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w","publicKey":"BC0SaFZWC9uH7wamOwduP93kUH2I2hEvyY0Srfj4A258pZSlV0iPoFIH+bd4yhncaqdoPLdEDl5Y\/yaFORPUe3c=","certificate":"MIIC4jCBywIBATANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDExJZdWJpY28gVTJGIFRlc3QgQ0EwHhcNMTQwNTE1MTI1ODU0WhcNMTQwNjE0MTI1ODU0WjAdMRswGQYDVQQDExJZdWJpY28gVTJGIFRlc3QgRUUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATbCtv1IcdczmPcpuHoJQYNlOYnVBlPnSSvJhq+rZlEH5WjcZEKOiDnPpFeE+i+OAV61XqjfnaQj6\/iipS2MOudMA0GCSqGSIb3DQEBCwUAA4ICAQCVQGtQYX2thKO064gP4zAPLaIKANklBO5y+mffWFEPC0cCnD5BKUqTrCmFiS2keoEyKFdxAe+oQogWljeR1d\/gj8k8jbDNiXCC7HnTxnhzKTLlq2y9Vp\/VRZHOwd2NZNzpnB9ePNKvUaWCGK\/gN+cynnYFdwJ75iSgMVYb\/RnFcdPwnsBzBU68hbhTnu\/FvJxWo7rZJ2q7qXpA10eLVXJr4\/4oSXEk9I\/0IIHqOP98Ck\/fAoI5gYI7ygndyqoPJ\/Wkg1VsmjmbFToWY9xb+axbvPefvg+KojwxE6MySMpYh\/h7oKEKamCWk19dJp5jHQmumkHlvQhH\/uUJmyD9EuLmQH+6SmEzZg0Oc9uw1aKamhcNNDCFakJGnv80j1+HbDXnqE0168FBqorS2hmqeaJfNSyg\/SXT950lGC36tLy7BzQ8jYG99Ok32znp0UVbIEEvLSci3JJ0ipLVg\/0J+xOb4zl6a1z65nae4OTj7628\/UJFmtSU0X6Np9gF1dNizxXPlH0fW1ggRCCQcb5m6ZqrdDJwUx1p7Ydm9AlPyiUwwmN5ADyxmzk\/AOCoiO96UVvnvUlk2kF7JMNxIv3R0SCzP5fTl7KqGByeA3d7W375o6DWIIEsOI+dJd7pyPXdakecZQRaVubC6\/ICl+G52OEkdp8jYjkDS8j3NAdJ1udNmg==", "counter":3}' ) ); - $resp = json_decode( '{ "signatureData": "AQAAAAQwRQIhAI6FSrMD3KUUtkpiP0jpIEakql-HNhwWFngyw553pS1CAiAKLjACPOhxzZXuZsVO8im-HStEcYGC50PKhsGp_SUAng==", "clientData": "eyAiY2hhbGxlbmdlIjogImZFbmM5b1Y3OUVhQmdLNUJvTkVSVTVnUEtNMlhHWVdyejRmVWpnYzBRN2ciLCAib3JpZ2luIjogImh0dHA6XC9cL2RlbW8uZXhhbXBsZS5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5nZXRBc3NlcnRpb24iIH0=", "keyHandle": "CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w" }' ); - - $add_method = new ReflectionMethod( $this->provider, 'add_security_key' ); - $add_method->setAccessible( true ); - $get_method = new ReflectionMethod( $this->provider, 'get_security_keys' ); - $get_method->setAccessible( true ); - $update_method = new ReflectionMethod( $this->provider, 'update_security_key' ); - $update_method->setAccessible( true ); - $delete_method = new ReflectionMethod( $this->provider, 'delete_security_key' ); - $delete_method->setAccessible( true ); - - $user_id = self::factory()->user->create(); - $add_method->invoke( $this->provider, $user_id, $regs[0] ); - - $data = $this->u2f->doAuthenticate( $reqs, $regs, $resp ); - - $this->assertEquals( true, $update_method->invoke( $this->provider, $user_id, $data ) ); - - $meta = $get_method->invoke( $this->provider, $user_id ); - $this->assertEquals( $data->keyHandle, $meta[0]->keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $this->assertEquals( $data->publicKey, $meta[0]->publicKey ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $this->assertEquals( $data->certificate, $meta[0]->certificate ); - $this->assertEquals( $data->counter, $meta[0]->counter ); - $this->assertEquals( 4, $meta[0]->counter ); - - $delete_method->invoke( $this->provider, $user_id ); - } - - /** - * Verify updating a second security key. - */ - public function test_update_security_key2() { - $reqs = array( json_decode( '{"version":"U2F_V2","challenge":"fEnc9oV79EaBgK5BoNERU5gPKM2XGYWrz4fUjgc0Q7g","keyHandle":"CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w","appId":"http://demo.example.com"}' ) ); - $regs = array( json_decode( '{"keyHandle":"CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w","publicKey":"BC0SaFZWC9uH7wamOwduP93kUH2I2hEvyY0Srfj4A258pZSlV0iPoFIH+bd4yhncaqdoPLdEDl5Y\/yaFORPUe3c=","certificate":"MIIC4jCBywIBATANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDExJZdWJpY28gVTJGIFRlc3QgQ0EwHhcNMTQwNTE1MTI1ODU0WhcNMTQwNjE0MTI1ODU0WjAdMRswGQYDVQQDExJZdWJpY28gVTJGIFRlc3QgRUUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATbCtv1IcdczmPcpuHoJQYNlOYnVBlPnSSvJhq+rZlEH5WjcZEKOiDnPpFeE+i+OAV61XqjfnaQj6\/iipS2MOudMA0GCSqGSIb3DQEBCwUAA4ICAQCVQGtQYX2thKO064gP4zAPLaIKANklBO5y+mffWFEPC0cCnD5BKUqTrCmFiS2keoEyKFdxAe+oQogWljeR1d\/gj8k8jbDNiXCC7HnTxnhzKTLlq2y9Vp\/VRZHOwd2NZNzpnB9ePNKvUaWCGK\/gN+cynnYFdwJ75iSgMVYb\/RnFcdPwnsBzBU68hbhTnu\/FvJxWo7rZJ2q7qXpA10eLVXJr4\/4oSXEk9I\/0IIHqOP98Ck\/fAoI5gYI7ygndyqoPJ\/Wkg1VsmjmbFToWY9xb+axbvPefvg+KojwxE6MySMpYh\/h7oKEKamCWk19dJp5jHQmumkHlvQhH\/uUJmyD9EuLmQH+6SmEzZg0Oc9uw1aKamhcNNDCFakJGnv80j1+HbDXnqE0168FBqorS2hmqeaJfNSyg\/SXT950lGC36tLy7BzQ8jYG99Ok32znp0UVbIEEvLSci3JJ0ipLVg\/0J+xOb4zl6a1z65nae4OTj7628\/UJFmtSU0X6Np9gF1dNizxXPlH0fW1ggRCCQcb5m6ZqrdDJwUx1p7Ydm9AlPyiUwwmN5ADyxmzk\/AOCoiO96UVvnvUlk2kF7JMNxIv3R0SCzP5fTl7KqGByeA3d7W375o6DWIIEsOI+dJd7pyPXdakecZQRaVubC6\/ICl+G52OEkdp8jYjkDS8j3NAdJ1udNmg==", "counter":3}' ) ); - $resp = json_decode( '{ "signatureData": "AQAAAAQwRQIhAI6FSrMD3KUUtkpiP0jpIEakql-HNhwWFngyw553pS1CAiAKLjACPOhxzZXuZsVO8im-HStEcYGC50PKhsGp_SUAng==", "clientData": "eyAiY2hhbGxlbmdlIjogImZFbmM5b1Y3OUVhQmdLNUJvTkVSVTVnUEtNMlhHWVdyejRmVWpnYzBRN2ciLCAib3JpZ2luIjogImh0dHA6XC9cL2RlbW8uZXhhbXBsZS5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5nZXRBc3NlcnRpb24iIH0=", "keyHandle": "CTUayZo8hCBeC-sGQJChC0wW-bBg99bmOlGCgw8XGq4dLsxO3yWh9mRYArZxocP5hBB1pEGB3bbJYiM-5acc5w" }' ); - - $add_method = new ReflectionMethod( $this->provider, 'add_security_key' ); - $add_method->setAccessible( true ); - $get_method = new ReflectionMethod( $this->provider, 'get_security_keys' ); - $get_method->setAccessible( true ); - $update_method = new ReflectionMethod( $this->provider, 'update_security_key' ); - $update_method->setAccessible( true ); - $delete_method = new ReflectionMethod( $this->provider, 'delete_security_key' ); - $delete_method->setAccessible( true ); - - $user_id = self::factory()->user->create(); - - $data = $this->u2f->doAuthenticate( $reqs, $regs, $resp ); - - $this->assertIsInt( $update_method->invoke( $this->provider, $user_id, $data ) ); - - $meta = $get_method->invoke( $this->provider, $user_id ); - $this->assertEquals( $data->keyHandle, $meta[0]->keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $this->assertEquals( $data->publicKey, $meta[0]->publicKey ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase - $this->assertEquals( $data->certificate, $meta[0]->certificate ); - $this->assertEquals( $data->counter, $meta[0]->counter ); - $this->assertEquals( 4, $meta[0]->counter ); - - $delete_method->invoke( $this->provider, $user_id ); - } - - /** - * Verify removing a security key. - */ - public function test_delete_security_key() { - $get_method = new ReflectionMethod( $this->provider, 'get_security_keys' ); - $get_method->setAccessible( true ); - $delete_method = new ReflectionMethod( $this->provider, 'delete_security_key' ); - $delete_method->setAccessible( true ); - - $user_id = self::factory()->user->create(); - $delete_method->invoke( $this->provider, $user_id ); - - $this->assertEquals( array(), $get_method->invoke( $this->provider, $user_id ) ); - } - - /** - * Verify removing a second security key. - */ - public function test_delete_security_key2() { - $req = json_decode( '{"version":"U2F_V2","challenge":"yKA0x075tjJ-GE7fKTfnzTOSaNUOWQxRd9TWz5aFOg8","appId":"http://demo.example.com"}' ); - $resp = json_decode( '{ "registrationData": "BQQtEmhWVgvbh-8GpjsHbj_d5FB9iNoRL8mNEq34-ANufKWUpVdIj6BSB_m3eMoZ3GqnaDy3RA5eWP8mhTkT1Ht3QAk1GsmaPIQgXgvrBkCQoQtMFvmwYPfW5jpRgoMPFxquHS7MTt8lofZkWAK2caHD-YQQdaRBgd22yWIjPuWnHOcwggLiMIHLAgEBMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNVBAMTEll1YmljbyBVMkYgVGVzdCBDQTAeFw0xNDA1MTUxMjU4NTRaFw0xNDA2MTQxMjU4NTRaMB0xGzAZBgNVBAMTEll1YmljbyBVMkYgVGVzdCBFRTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNsK2_Uhx1zOY9ym4eglBg2U5idUGU-dJK8mGr6tmUQflaNxkQo6IOc-kV4T6L44BXrVeqN-dpCPr-KKlLYw650wDQYJKoZIhvcNAQELBQADggIBAJVAa1Bhfa2Eo7TriA_jMA8togoA2SUE7nL6Z99YUQ8LRwKcPkEpSpOsKYWJLaR6gTIoV3EB76hCiBaWN5HV3-CPyTyNsM2JcILsedPGeHMpMuWrbL1Wn9VFkc7B3Y1k3OmcH1480q9RpYIYr-A35zKedgV3AnvmJKAxVhv9GcVx0_CewHMFTryFuFOe78W8nFajutknarupekDXR4tVcmvj_ihJcST0j_Qggeo4_3wKT98CgjmBgjvKCd3Kqg8n9aSDVWyaOZsVOhZj3Fv5rFu895--D4qiPDETozJIyliH-HugoQpqYJaTX10mnmMdCa6aQeW9CEf-5QmbIP0S4uZAf7pKYTNmDQ5z27DVopqaFw00MIVqQkae_zSPX4dsNeeoTTXrwUGqitLaGap5ol81LKD9JdP3nSUYLfq0vLsHNDyNgb306TfbOenRRVsgQS8tJyLcknSKktWD_Qn7E5vjOXprXPrmdp7g5OPvrbz9QkWa1JTRfo2n2AXV02LPFc-UfR9bWCBEIJBxvmbpmqt0MnBTHWnth2b0CU_KJTDCY3kAPLGbOT8A4KiI73pRW-e9SWTaQXskw3Ei_dHRILM_l9OXsqoYHJ4Dd3tbfvmjoNYggSw4j50l3unI9d1qR5xlBFpW5sLr8gKX4bnY4SR2nyNiOQNLyPc0B0nW502aMEUCIQDTGOX-i_QrffJDY8XvKbPwMuBVrOSO-ayvTnWs_WSuDQIgZ7fMAvD_Ezyy5jg6fQeuOkoJi8V2naCtzV-HTly8Nww=", "clientData": "eyAiY2hhbGxlbmdlIjogInlLQTB4MDc1dGpKLUdFN2ZLVGZuelRPU2FOVU9XUXhSZDlUV3o1YUZPZzgiLCAib3JpZ2luIjogImh0dHA6XC9cL2RlbW8uZXhhbXBsZS5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5maW5pc2hFbnJvbGxtZW50IiB9" }' ); - $reg = $this->u2f->doRegister( $req, $resp ); - - $add_method = new ReflectionMethod( $this->provider, 'add_security_key' ); - $add_method->setAccessible( true ); - $get_method = new ReflectionMethod( $this->provider, 'get_security_keys' ); - $get_method->setAccessible( true ); - $delete_method = new ReflectionMethod( $this->provider, 'delete_security_key' ); - $delete_method->setAccessible( true ); - - $user_id = self::factory()->user->create(); - $add_method->invoke( $this->provider, $user_id, $reg ); - $delete_method->invoke( $this->provider, $user_id ); - - $this->assertEquals( array(), $get_method->invoke( $this->provider, $user_id ) ); - } -} From 7401e4f2ea73fe7f5c770f45448b711aeb94400f Mon Sep 17 00:00:00 2001 From: George Stephanis Date: Wed, 18 Feb 2026 14:58:30 -0500 Subject: [PATCH 2/9] Drop U2F from plugin description as well. --- two-factor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/two-factor.php b/two-factor.php index 4095b465..c0f38b17 100644 --- a/two-factor.php +++ b/two-factor.php @@ -10,7 +10,7 @@ * @wordpress-plugin * Plugin Name: Two Factor * Plugin URI: https://wordpress.org/plugins/two-factor/ - * Description: Enable Two-Factor Authentication using time-based one-time passwords, Universal 2nd Factor (FIDO U2F, YubiKey), email, and backup verification codes. + * Description: Enable Two-Factor Authentication using time-based one-time passwords, email, and backup verification codes. * Requires at least: 6.8 * Version: 0.15.0 * Requires PHP: 7.2 From f667c980bbdabf59cd18a0c32fc101d20576223a Mon Sep 17 00:00:00 2001 From: George Stephanis Date: Wed, 18 Feb 2026 15:00:15 -0500 Subject: [PATCH 3/9] Clear a no longer relevant screenshot. --- .wordpress-org/screenshot-2.png | Bin 111272 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .wordpress-org/screenshot-2.png diff --git a/.wordpress-org/screenshot-2.png b/.wordpress-org/screenshot-2.png deleted file mode 100644 index 1c4e8590f518b1fd15b34f9374317a6eb0dd8716..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111272 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

=NA0g!*)Dt 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 From f6959306a448d262689914636a8a5dc2e5ec8a59 Mon Sep 17 00:00:00 2001 From: George Stephanis Date: Wed, 18 Feb 2026 15:04:55 -0500 Subject: [PATCH 4/9] These directories no longer exist. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index eefe2421..01a9d34f 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "lint": "npm-run-all lint:*", "lint:php": "composer lint", "lint:phpstan": "composer lint-phpstan", - "lint:css": "wp-scripts lint-style ./user-edit.css ./providers/css/", - "lint:js": "wp-scripts lint-js ./Gruntfile.js ./providers/js/", + "lint:css": "wp-scripts lint-style ./user-edit.css", + "lint:js": "wp-scripts lint-js ./Gruntfile.js", "format": "npm-run-all format:*", "format:php": "composer format", "format:js": "npm run lint:js -- --fix", From cf2be4dcfc735d4044eb06768a8416d1a2edaf2b Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 26 Feb 2026 15:25:57 +0100 Subject: [PATCH 5/9] Rename screenshot-3.png to screenshot-2.png --- .../{screenshot-3.png => screenshot-2.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename .wordpress-org/{screenshot-3.png => screenshot-2.png} (100%) diff --git a/.wordpress-org/screenshot-3.png b/.wordpress-org/screenshot-2.png similarity index 100% rename from .wordpress-org/screenshot-3.png rename to .wordpress-org/screenshot-2.png From 5e8702be8131aefa70b7bef52e897be23fc1081b Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 26 Feb 2026 15:26:14 +0100 Subject: [PATCH 6/9] Rename screenshot-4.png to screenshot-3.png --- .../{screenshot-4.png => screenshot-3.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename .wordpress-org/{screenshot-4.png => screenshot-3.png} (100%) diff --git a/.wordpress-org/screenshot-4.png b/.wordpress-org/screenshot-3.png similarity index 100% rename from .wordpress-org/screenshot-4.png rename to .wordpress-org/screenshot-3.png From 57c721ef0d0a9a294d79ee8856b85baf9d9abc91 Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 26 Feb 2026 15:26:21 +0100 Subject: [PATCH 7/9] Rename screenshot-5.png to screenshot-4.png --- .../{screenshot-5.png => screenshot-4.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename .wordpress-org/{screenshot-5.png => screenshot-4.png} (100%) diff --git a/.wordpress-org/screenshot-5.png b/.wordpress-org/screenshot-4.png similarity index 100% rename from .wordpress-org/screenshot-5.png rename to .wordpress-org/screenshot-4.png From ac58bc7f567a3a59aaaa2b8140e884c248f44af0 Mon Sep 17 00:00:00 2001 From: George Stephanis Date: Thu, 26 Feb 2026 09:50:05 -0500 Subject: [PATCH 8/9] Update readme.txt Co-authored-by: Brian --- readme.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.txt b/readme.txt index ec245dca..bab464c3 100644 --- a/readme.txt +++ b/readme.txt @@ -138,9 +138,9 @@ Yes. For passkeys and hardware security keys, you can install the Two-Factor Pro == Screenshots == 1. Two-factor options under User Profile - Shows the main configuration area where users can enable different authentication methods. -3. Email Code Authentication during WordPress Login - Shows the email verification screen that appears during login. -4. Authenticator App (TOTP) setup with QR code - Demonstrates the QR code generation and manual key entry for TOTP setup. -5. Backup codes generation and management - Shows the backup codes interface for generating and managing emergency access codes. +2. Email Code Authentication during WordPress Login - Shows the email verification screen that appears during login. +3. Authenticator App (TOTP) setup with QR code - Demonstrates the QR code generation and manual key entry for TOTP setup. +4. Backup codes generation and management - Shows the backup codes interface for generating and managing emergency access codes. == Changelog == From 1b38c51e6f222960bda1ba2f54c4d77330abfa3b Mon Sep 17 00:00:00 2001 From: George Stephanis Date: Thu, 26 Feb 2026 09:50:18 -0500 Subject: [PATCH 9/9] Update readme.txt Co-authored-by: Brian --- readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.txt b/readme.txt index bab464c3..9a70ee8d 100644 --- a/readme.txt +++ b/readme.txt @@ -67,7 +67,7 @@ The Two-Factor plugin adds an extra layer of security to your WordPress login by ## Important Notes ### HTTPS Requirement -- Other methods work on both HTTP and HTTPS sites +- All methods work on both HTTP and HTTPS sites ### Browser Compatibility - TOTP and email methods work on all devices and browsers