PageSourceSearch

https://www.iidh.ed.cr/media/plg_system_webauthn/js/login.js?bd2cb3

js iidh.ed.cr collected 2026-10-01 11:49:39 UTC 8,119 bytes, 227 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.