[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/media/plg_system_webauthn/js/ -> login-es5.js (source)

   1  (function () {
   2    'use strict';
   3  
   4    /**
   5     * @package     Joomla.Plugin
   6     * @subpackage  System.webauthn
   7     *
   8     * @copyright   (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
   9     * @license     GNU General Public License version 2 or later; see LICENSE.txt
  10     */
  11    window.Joomla = window.Joomla || {};
  12  
  13    (function (Joomla, document) {
  14      /**
  15       * Converts a simple object containing query string parameters to a single, escaped query string.
  16       * This method is a necessary evil since Joomla.request can only accept data as a string.
  17       *
  18       * @param    object   {object}  A plain object containing the query parameters to pass
  19       * @param    prefix   {string}  Prefix for array-type parameters
  20       *
  21       * @returns  {string}
  22       */
  23      var interpolateParameters = function interpolateParameters(object, prefix) {
  24        if (prefix === void 0) {
  25          prefix = '';
  26        }
  27  
  28        var encodedString = '';
  29        Object.keys(object).forEach(function (prop) {
  30          if (typeof object[prop] !== 'object') {
  31            if (encodedString.length > 0) {
  32              encodedString += '&';
  33            }
  34  
  35            if (prefix === '') {
  36              encodedString += encodeURIComponent(prop) + "=" + encodeURIComponent(object[prop]);
  37            } else {
  38              encodedString += encodeURIComponent(prefix) + "[" + encodeURIComponent(prop) + "]=" + encodeURIComponent(object[prop]);
  39            }
  40  
  41            return;
  42          } // Objects need special handling
  43  
  44  
  45          encodedString += "" + interpolateParameters(object[prop], prop);
  46        });
  47        return encodedString;
  48      };
  49      /**
  50       * Finds the first field matching a selector inside a form
  51       *
  52       * @param   {HTMLFormElement}  form           The FORM element
  53       * @param   {String}           fieldSelector  The CSS selector to locate the field
  54       *
  55       * @returns {Element|null}  NULL when no element is found
  56       */
  57  
  58  
  59      var findField = function findField(form, fieldSelector) {
  60        var elInputs = form.querySelectorAll(fieldSelector);
  61  
  62        if (!elInputs.length) {
  63          return null;
  64        }
  65  
  66        return elInputs[0];
  67      };
  68      /**
  69       * Find a form field described by the CSS selector fieldSelector.
  70       * The field must be inside a <form> element which is either the
  71       * outerElement itself or enclosed by outerElement.
  72       *
  73       * @param   {Element}  outerElement   The element which is either our form or contains our form.
  74       * @param   {String}   fieldSelector  The CSS selector to locate the field
  75       *
  76       * @returns {null|Element}  NULL when no element is found
  77       */
  78  
  79  
  80      var lookForField = function lookForField(outerElement, fieldSelector) {
  81        var elInput = null;
  82  
  83        if (!outerElement) {
  84          return elInput;
  85        }
  86  
  87        var elElement = outerElement.parentElement;
  88  
  89        if (elElement.nodeName === 'FORM') {
  90          elInput = findField(elElement, fieldSelector);
  91          return elInput;
  92        }
  93  
  94        var elForms = elElement.querySelectorAll('form');
  95  
  96        if (elForms.length) {
  97          for (var i = 0; i < elForms.length; i += 1) {
  98            elInput = findField(elForms[i], fieldSelector);
  99  
 100            if (elInput !== null) {
 101              return elInput;
 102            }
 103          }
 104        }
 105  
 106        return null;
 107      };
 108      /**
 109       * A simple error handler.
 110       *
 111       * @param   {String}  message
 112       */
 113  
 114  
 115      var handleLoginError = function handleLoginError(message) {
 116        Joomla.renderMessages({
 117          error: [message]
 118        });
 119      };
 120      /**
 121       * Handles the browser response for the user interaction with the authenticator. Redirects to an
 122       * internal page which handles the login server-side.
 123       *
 124       * @param {  Object}  publicKey     Public key request options, returned from the server
 125       */
 126  
 127  
 128      var handleLoginChallenge = function handleLoginChallenge(publicKey) {
 129        var arrayToBase64String = function arrayToBase64String(a) {
 130          return btoa(String.fromCharCode.apply(String, a));
 131        };
 132  
 133        var base64url2base64 = function base64url2base64(input) {
 134          var output = input.replace(/-/g, '+').replace(/_/g, '/');
 135          var pad = output.length % 4;
 136  
 137          if (pad) {
 138            if (pad === 1) {
 139              throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
 140            }
 141  
 142            output += new Array(5 - pad).join('=');
 143          }
 144  
 145          return output;
 146        };
 147  
 148        if (!publicKey.challenge) {
 149          handleLoginError(Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_INVALID_USERNAME'));
 150          return;
 151        }
 152  
 153        publicKey.challenge = Uint8Array.from(window.atob(base64url2base64(publicKey.challenge)), function (c) {
 154          return c.charCodeAt(0);
 155        });
 156  
 157        if (publicKey.allowCredentials) {
 158          publicKey.allowCredentials = publicKey.allowCredentials.map(function (data) {
 159            data.id = Uint8Array.from(window.atob(base64url2base64(data.id)), function (c) {
 160              return c.charCodeAt(0);
 161            });
 162            return data;
 163          });
 164        }
 165  
 166        navigator.credentials.get({
 167          publicKey: publicKey
 168        }).then(function (data) {
 169          var publicKeyCredential = {
 170            id: data.id,
 171            type: data.type,
 172            rawId: arrayToBase64String(new Uint8Array(data.rawId)),
 173            response: {
 174              authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
 175              clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
 176              signature: arrayToBase64String(new Uint8Array(data.response.signature)),
 177              userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
 178            }
 179          }; // Send the response to your server
 180  
 181          var paths = Joomla.getOptions('system.paths');
 182          window.location = (paths ? paths.base + "/index.php" : window.location.pathname) + "?" + Joomla.getOptions('csrf.token') + "=1&option=com_ajax&group=system&plugin=webauthn&" + ("format=raw&akaction=login&encoding=redirect&data=" + btoa(JSON.stringify(publicKeyCredential)));
 183        }).catch(function (error) {
 184          // Example: timeout, interaction refused...
 185          handleLoginError(error);
 186        });
 187      };
 188      /**
 189       * Initialize the passwordless login, going through the server to get the registered certificates
 190       * for the user.
 191       *
 192       * @param   {string}   formId       The login form's or login module's HTML ID
 193       *
 194       * @returns {boolean}  Always FALSE to prevent BUTTON elements from reloading the page.
 195       */
 196      // eslint-disable-next-line no-unused-vars
 197  
 198  
 199      Joomla.plgSystemWebauthnLogin = function (formId) {
 200        // Get the username
 201        var elFormContainer = document.getElementById(formId);
 202        var elUsername = lookForField(elFormContainer, 'input[name=username]');
 203        var elReturn = lookForField(elFormContainer, 'input[name=return]');
 204  
 205        if (elUsername === null) {
 206          Joomla.renderMessages({
 207            error: [Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_CANNOT_FIND_USERNAME')]
 208          });
 209          return false;
 210        }
 211  
 212        var username = elUsername.value;
 213        var returnUrl = elReturn ? elReturn.value : null; // No username? We cannot proceed. We need a username to find the acceptable public keys :(
 214  
 215        if (username === '') {
 216          Joomla.renderMessages({
 217            error: [Joomla.Text._('PLG_SYSTEM_WEBAUTHN_ERR_EMPTY_USERNAME')]
 218          });
 219          return false;
 220        } // Get the Public Key Credential Request Options (challenge and acceptable public keys)
 221  
 222  
 223        var postBackData = {
 224          option: 'com_ajax',
 225          group: 'system',
 226          plugin: 'webauthn',
 227          format: 'raw',
 228          akaction: 'challenge',
 229          encoding: 'raw',
 230          username: username,
 231          returnUrl: returnUrl
 232        };
 233        postBackData[Joomla.getOptions('csrf.token')] = 1;
 234        var paths = Joomla.getOptions('system.paths');
 235        Joomla.request({
 236          url: (paths ? paths.base + "/index.php" : window.location.pathname) + "?" + Joomla.getOptions('csrf.token') + "=1",
 237          method: 'POST',
 238          data: interpolateParameters(postBackData),
 239          onSuccess: function onSuccess(rawResponse) {
 240            var jsonData = {};
 241  
 242            try {
 243              jsonData = JSON.parse(rawResponse);
 244            } catch (e) {
 245              /**
 246               * In case of JSON decoding failure fall through; the error will be handled in the login
 247               * challenge handler called below.
 248               */
 249            }
 250  
 251            handleLoginChallenge(jsonData);
 252          },
 253          onError: function onError(xhr) {
 254            handleLoginError(xhr.status + " " + xhr.statusText);
 255          }
 256        });
 257        return false;
 258      }; // Initialization. Runs on DOM content loaded since this script is always loaded deferred.
 259  
 260  
 261      var loginButtons = [].slice.call(document.querySelectorAll('.plg_system_webauthn_login_button'));
 262  
 263      if (loginButtons.length) {
 264        loginButtons.forEach(function (button) {
 265          button.addEventListener('click', function (_ref) {
 266            var currentTarget = _ref.currentTarget;
 267            Joomla.plgSystemWebauthnLogin(currentTarget.getAttribute('data-webauthn-form'));
 268          });
 269        });
 270      }
 271    })(Joomla, document);
 272  
 273  })();


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer