1'use strict'; 2 3// Send an AJAX request and get the response 4const wwa_ajax = function () { 5 let xmlHttpReq = new XMLHttpRequest(); 6 return { 7 /** Send an AJAX GET request and get the response 8 * 9 * @param {string} url URL 10 * @param {string} data Attached data 11 * @param {object} callback Callback function 12 */ 13 get: (url, data = '', callback = () => { }) => { 14 xmlHttpReq.open('GET', url + data, true); 15 xmlHttpReq.send(); 16 xmlHttpReq.onreadystatechange = () => { 17 if (xmlHttpReq.readyState === 4 && xmlHttpReq.status === 200) { 18 callback(xmlHttpReq.responseText, true); 19 } else if (xmlHttpReq.readyState === 4) { 20 callback('Network Error.', false); 21 } 22 } 23 }, 24 /** Send an AJAX POST request and get the response 25 * 26 * @param {string} url URL 27 * @param {string} data Attached data 28 * @param {object} callback Callback function 29 */ 30 post: (url, data = '', callback = () => { }) => { 31 xmlHttpReq.open('POST', url, true); 32 xmlHttpReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 33 xmlHttpReq.send(data); 34 xmlHttpReq.onreadystatechange = () => { 35 if (xmlHttpReq.readyState === 4 && xmlHttpReq.status === 200) { 36 callback(xmlHttpReq.responseText, true); 37 } else if (xmlHttpReq.readyState === 4) { 38 callback('Network Error.', false); 39 } 40 } 41 } 42 } 43}; 44 45/** Operate selected DOMs 46 * 47 * @param {string} selector DOM selector 48 * @param {object} callback Callbck function 49 * @param {string} method Selecte method 50 */ 51const wwa_dom = (selector, callback = () => { }, method = 'query') => { 52 let dom_list = []; 53 if (method === 'id') { 54 let dom = document.getElementById(selector); 55 if (dom) { 56 callback(dom); 57 } 58 return; 59 } else if (method === 'class') { 60 dom_list = document.getElementsByClassName(selector); 61 } else if (method === 'tag') { 62 dom_list = document.getElementsByTagName(selector); 63 } else { 64 dom_list = document.querySelectorAll(selector); 65 } 66 for (let dom of dom_list) { 67 callback(dom); 68 } 69 return; 70} 71 72/** Code Base64URL into Base64 73 * 74 * @param {string} input Base64URL coded string 75 */ 76function base64url2base64(input) { 77 input = input.replace(/=/g, '').replace(/-/g, '+').replace(/_/g, '/'); 78 const pad = input.length % 4; 79 if (pad) { 80 if (pad === 1) { 81 throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding'); 82 } 83 input += new Array(5 - pad).join('='); 84 } 85 return input; 86} 87 88/** Code Uint8Array into Base64 string 89 * 90 * @param {Uint8Array} a The Uint8Array needed to be coded into Base64 string 91 */ 92function arrayToBase64String(a) { 93 return btoa(String.fromCharCode(...a)); 94} 95 96// Disable all WP-Webauthn buttons 97function wwa_disable_buttons() { 98 wwa_dom('wwa-test-submit', (dom) => { dom.disabled = true }, 'class'); 99 wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.disabled = true }, 'class'); 100 wwa_dom('wwa-bind-submit', (dom) => { dom.disabled = true }, 'class'); 101 wwa_dom('wwa-login-submit', (dom) => { dom.disabled = true }, 'class'); 102 wwa_dom('wp-submit', (dom) => { dom.disabled = true }, 'id'); 103} 104 105// Enable all WP-Webauthn buttons 106function wwa_enable_buttons() { 107 wwa_dom('wwa-test-submit', (dom) => { dom.disabled = false }, 'class'); 108 wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.disabled = false }, 'class'); 109 wwa_dom('wwa-bind-submit', (dom) => { dom.disabled = false }, 'class'); 110 wwa_dom('wwa-login-submit', (dom) => { dom.disabled = false }, 'class'); 111 wwa_dom('wp-submit', (dom) => { dom.disabled = false }, 'id'); 112} 113 114document.addEventListener('DOMContentLoaded', () => { 115 wwa_dom('wwa-login-submit', (dom) => { dom.addEventListener('click', wwa_auth, false) }, 'class'); 116 // If traditional form exists 117 if (document.getElementsByClassName('wwa-login-form-traditional').length > 0) { 118 wwa_dom('.wwa-login-form-traditional .login-password', (dom) => { 119 let height = dom.clientHeight; 120 wwa_dom('.wwa-login-form-webauthn .wp-webauthn-notice', (ele) => { 121 ele.style.height = height - 40.4 + 'px'; 122 }); 123 }); 124 wwa_dom('wwa-w2t', (dom) => { dom.addEventListener('click', wwa_toggle, false) }, 'class'); 125 wwa_dom('wwa-t2w', (dom) => { dom.addEventListener('click', wwa_toggle, false) }, 'class'); 126 } else { 127 wwa_dom('.wwa-login-form-webauthn .wp-webauthn-notice', (ele) => { 128 ele.style.height = '40.6px'; 129 }); 130 wwa_dom('wwa-w2t', (dom) => { dom.parentNode.removeChild(document.getElementsByClassName('wwa-w2t')[0]) }, 'class'); 131 } 132 // If not support 133 if (window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function') { 134 wwa_dom('wwa-test-submit', (dom) => { dom.disabled = true }, 'class'); 135 wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.disabled = true }, 'class'); 136 wwa_dom('wwa-bind-submit', (dom) => { dom.disabled = true }, 'class'); 137 wwa_dom('wwa-show-test', (dom) => { dom.innerText = wwa_php_vars.i18n_31 }, 'class');
138 wwa_dom('wwa-show-progress', (dom) => { dom.innerText = wwa_php_vars.i18n_31 }, 'class'); 139 if (document.getElementsByClassName('wwa-login-form-traditional').length > 0) { 140 wwa_dom('wwa-login-form-webauthn', (dom) => { dom.classList.add('wwa-hide-form') }, 'class'); 141 } 142 return; 143 } 144 wwa_dom('wwa-login-form-traditional', (dom) => { dom.classList.add('wwa-hide-form') }, 'class'); 145 wwa_dom('wwa-bind-submit', (dom) => { dom.addEventListener('click', wwa_bind, false) }, 'class'); 146 wwa_dom('wwa-test-submit', (dom) => { dom.addEventListener('click', wwa_verify, false) }, 'class'); 147 wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.addEventListener('click', wwa_verify, false) }, 'class'); 148 updateList(); 149}); 150 151// Toggle form 152function wwa_toggle(e) { 153 e.preventDefault(); 154 if (document.getElementsByClassName('wwa-login-form-traditional').length > 0) { 155 // Disable buttons if it is not shown 156 if (document.getElementsByClassName('wwa-login-form-traditional')[0].className.indexOf('wwa-hide-form') !== -1) { 157 wwa_dom('wp-submit', (dom) => { dom.disabled = false }, 'id'); 158 wwa_dom('wwa-login-submit', (dom) => { dom.disabled = true }, 'class'); 159 setTimeout(() => { 160 wwa_dom('user_login', (dom) => { dom.focus() }, 'id'); 161 }, 0); 162 } else { 163 wwa_dom('wp-submit', (dom) => { dom.disabled = true }, 'id'); 164 wwa_dom('wwa-login-submit', (dom) => { dom.disabled = false }, 'class'); 165 setTimeout(() => { 166 wwa_dom('wwa-user-name', (dom) => { dom.focus() }, 'id'); 167 }, 0); 168 } 169 document.getElementsByClassName('wwa-login-form-traditional')[0].classList.toggle('wwa-hide-form'); 170 document.getElementsByClassName('wwa-login-form-webauthn')[0].classList.toggle('wwa-hide-form'); 171 } 172} 173 174// Auth 175function wwa_auth() { 176 if (window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function') { 177 alert(wwa_php_vars.i18n_31); 178 return; 179 } 180 181 let wwa_username = document.getElementById('username').value; 182 if (wwa_username === '' && wwa_php_vars.usernameless !== 'true') { 183 alert(wwa_php_vars.i18n_11); 184 return; 185 } 186 wwa_dom('wwa-user-name', (dom) => { dom.readOnly = true }, 'class'); 187 wwa_disable_buttons(); 188 let button_dom = this; 189 190 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_3; 191 let request = wwa_ajax(); 192 request.get(wwa_php_vars.ajax_url, `?action=wwa_auth_start&user=${encodeURIComponent(wwa_username)}&type=auth`, (rawData, status) => { 193 if (status) { 194 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_4; 195 let data = rawData; 196 try { 197 data = JSON.parse(rawData); 198 } catch (e) { 199 console.warn(rawData); 200 wwa_enable_buttons(); 201 if (wwa_php_vars.usernameless === 'true' && wwa_username === '') { 202 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33; 203 } else { 204 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7; 205 } 206 wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class'); 207 return; 208 } 209 data.challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) => c.charCodeAt(0)); 210 211 if (data.allowCredentials) { 212 data.allowCredentials = data.allowCredentials.map((item) => { 213 item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0)); 214 return item; 215 }); 216 } 217 218 // Save client ID 219 const clientID = data.clientID; 220 delete data.clientID; 221 222 navigator.credentials.get({ 'publicKey': data }).then((credentialInfo) => { 223 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_5; 224 return credentialInfo; 225 }).then((data) => { 226 const publicKeyCredential = { 227 id: data.id, 228 type: data.type, 229 rawId: arrayToBase64String(new Uint8Array(data.rawId)), 230 response: { 231 authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)), 232 clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)), 233 signature: arrayToBase64String(new Uint8Array(data.response.signature)), 234 userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null 235 } 236 }; 237 return publicKeyCredential; 238 }).then(JSON.stringify).then((AuthenticatorResponse) => { 239 let response = wwa_ajax(); 240 response.post(`${wwa_php_vars.ajax_url}?action=wwa_auth`, `data=${encodeURIComponent(window.btoa(AuthenticatorResponse))}&type=auth&clientid=${clientID}&user=${encodeURIComponent(wwa_username)}&remember=${wwa_php_vars.remember_me === 'false' ? 'false' : (document.getElementById('wwa-rememberme') ? (document.getElementById('wwa-rememberme').checked ? 'true' : 'false') : 'fal
240se')}`, (data, status) => { 241 if (status) { 242 if (data === 'true') { 243 wwa_enable_buttons(); 244 wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class'); 245 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_6; 246 if (document.querySelectorAll('input[name="redirect"]').length > 0) { 247 setTimeout(() => { 248 window.location.href = document.querySelectorAll('input[name="redirect"]')[0].value; 249 }, 200); 250 } else { 251 if (document.getElementsByClassName('wwa-redirect-to').length > 0) { 252 setTimeout(() => { 253 window.location.href = document.getElementsByClassName('wwa-redirect-to')[0].value; 254 }, 200); 255 } else { 256 setTimeout(() => { 257 window.location.reload(); 258 }, 200); 259 } 260 } 261 } else { 262 wwa_enable_buttons(); 263 if (wwa_php_vars.usernameless === 'true' && wwa_username === '') { 264 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33; 265 } else { 266 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7; 267 } 268 wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class'); 269 } 270 } else { 271 wwa_enable_buttons(); 272 if (wwa_php_vars.usernameless === 'true' && wwa_username === '') { 273 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33; 274 } else { 275 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7; 276 } 277 wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class'); 278 } 279 }) 280 }).catch((error) => { 281 console.warn(error); 282 wwa_enable_buttons(); 283 if (wwa_php_vars.usernameless === 'true' && wwa_username === '') { 284 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33; 285 } else { 286 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7; 287 } 288 wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class'); 289 }) 290 } else { 291 wwa_enable_buttons(); 292 if (wwa_php_vars.usernameless === 'true' && wwa_username === '') { 293 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33; 294 } else { 295 document.getElementById('wwa-login-form-webauthn-text').innerHTML = wwa_php_vars.i18n_7; 296 } 297 wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class'); 298 } 299 }) 300} 301 302// Bind 303function wwa_bind() { 304 let button_dom = this; 305 let wwa_name = this.parentNode.parentNode.getElementsByClassName('wwa-authenticator-name')[0].value; 306 if (wwa_name === '') { 307 alert(wwa_php_vars.i18n_12); 308 return; 309 } 310 let wwa_type = this.parentNode.parentNode.getElementsByClassName('wwa-authenticator-type')[0].value; 311 let wwa_usernameless = this.parentNode.parentNode.querySelectorAll('.wwa-authenticator-usernameless:checked')[0] ? this.parentNode.parentNode.querySelectorAll('.wwa-authenticator-usernameless:checked')[0].value : 'false'; 312 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_3; 313 wwa_disable_buttons(); 314 // Lock options 315 wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = true }, 'class'); 316 wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = true }, 'class'); 317 wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = true }, 'class'); 318 let request = wwa_ajax(); 319 request.get(wwa_php_vars.ajax_url, `?action=wwa_create&name=${encodeURIComponent(wwa_name)}&type=${encodeURIComponent(wwa_type)}&usernameless=${wwa_usernameless}`, (rawData, status) => { 320 if (status) { 321 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_28; 322 let data = rawData; 323 try { 324 data = JSON.parse(rawData); 325 } catch (e) { 326 console.warn(rawData); 327 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30; 328 wwa_enable_buttons(); 329 wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class'); 330 wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class'); 331 wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class'); 332 updateList(); 333 return; 334 } 335 let challenge = new Uint8Array(32); 336 let user_id = new Uint8Array(32); 337 challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) =>
337 c.charCodeAt(0)); 338 user_id = Uint8Array.from(window.atob(base64url2base64(data.user.id)), (c) => c.charCodeAt(0)); 339 340 let public_key = { 341 challenge: challenge, 342 rp: { 343 id: data.rp.id, 344 name: data.rp.name 345 }, 346 user: { 347 id: user_id, 348 name: data.user.name, 349 displayName: data.user.displayName 350 }, 351 pubKeyCredParams: data.pubKeyCredParams, 352 authenticatorSelection: data.authenticatorSelection, 353 timeout: data.timeout 354 } 355 356 // If some authenticators are already registered, exclude 357 if (data.excludeCredentials) { 358 public_key.excludeCredentials = data.excludeCredentials.map((item) => { 359 item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0)); 360 return item; 361 }) 362 } 363 364 // Save client ID 365 const clientID = data.clientID; 366 delete data.clientID; 367 368 // Create, a pop-up window should appear 369 navigator.credentials.create({ 'publicKey': public_key }).then((newCredentialInfo) => { 370 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_32; 371 return newCredentialInfo; 372 }).then((data) => { 373 // Code Uint8Array into string for transmission 374 const publicKeyCredential = { 375 id: data.id, 376 type: data.type, 377 rawId: arrayToBase64String(new Uint8Array(data.rawId)), 378 response: { 379 clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)), 380 attestationObject: arrayToBase64String(new Uint8Array(data.response.attestationObject)) 381 } 382 }; 383 return publicKeyCredential; 384 }).then(JSON.stringify).then((AuthenticatorAttestationResponse) => { 385 let response = wwa_ajax(); 386 response.post(`${wwa_php_vars.ajax_url}?action=wwa_create_response`, `data=${encodeURIComponent(window.btoa(AuthenticatorAttestationResponse))}&name=${encodeURIComponent(wwa_name)}&type=${encodeURIComponent(wwa_type)}&usernameless=${wwa_usernameless}&clientid=${clientID}`, (rawData, status) => { 387 if (status) { 388 if (rawData === 'true') { 389 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_29; 390 wwa_enable_buttons(); 391 wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false; dom.value = '' }, 'class'); 392 wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class'); 393 wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class'); 394 updateList(); 395 } else { 396 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30; 397 wwa_enable_buttons(); 398 wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class'); 399 wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class'); 400 wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class'); 401 updateList(); 402 } 403 } else { 404 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30; 405 wwa_enable_buttons(); 406 wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class'); 407 wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class'); 408 wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class'); 409 updateList(); 410 } 411 }) 412 }).catch((error) => { 413 console.warn(error); 414 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30; 415 wwa_enable_buttons(); 416 wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class'); 417 wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class'); 418 wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class'); 419 updateList(); 420 }) 421 } else { 422 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30; 423 wwa_enable_buttons(); 424 wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class'); 425 wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class'); 426 wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class'); 427 updateList(); 428 } 429 }) 430} 431 432// Verify 433function wwa_verify() { 434 let button_dom = this; 435 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_3; 436 let usernameless = this.className.indexOf('wwa-test-usernameless-submit') === -1 ? false : true; 437 wwa_disable_buttons(); 438 let request = wwa_ajax(); 439 request.get(wwa_php_vars.ajax_url, `?action=wwa_auth_start&type=test&usernameless=${usernameless ? 'true' : 'false'}`, (rawData, status) => { 440 if (status) { 441 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_4; 442 if (rawData === 'User not inited.') { 443 wwa_enable_buttons(); 444 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15; 445 return; 446 } 447 let data = rawData; 448 try { 449 data = JSON.parse(rawData); 450 } catch (e) { 451 console.warn(rawData); 452 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15; 453 wwa_enable_buttons(); 454 return; 455 } 456 data.challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) =>
456 c.charCodeAt(0)); 457 458 if (data.allowCredentials) { 459 data.allowCredentials = data.allowCredentials.map((item) => { 460 item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0)); 461 return item; 462 }); 463 } 464 465 if (data.allowCredentials && wwa_php_vars.allow_authenticator_type && wwa_php_vars.allow_authenticator_type !== 'none') { 466 for (let credential of data.allowCredentials) { 467 if (wwa_php_vars.allow_authenticator_type === 'cross-platform') { 468 credential.transports = ['usb', 'nfc', 'ble']; 469 } else if (wwa_php_vars.allow_authenticator_type === 'platform') { 470 credential.transports = ['internal']; 471 } 472 } 473 } 474 475 // Save client ID 476 const clientID = data.clientID; 477 delete data.clientID; 478 479 navigator.credentials.get({ 'publicKey': data }).then((credentialInfo) => { 480 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_13; 481 return credentialInfo; 482 }).then((data) => { 483 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_14; 484 const publicKeyCredential = { 485 id: data.id, 486 type: data.type, 487 rawId: arrayToBase64String(new Uint8Array(data.rawId)), 488 response: { 489 authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)), 490 clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)), 491 signature: arrayToBase64String(new Uint8Array(data.response.signature)), 492 userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null 493 } 494 }; 495 return publicKeyCredential; 496 }).then(JSON.stringify).then((AuthenticatorResponse) => { 497 let response = wwa_ajax(); 498 response.post(`${wwa_php_vars.ajax_url}?action=wwa_auth`, `data=${encodeURIComponent(window.btoa(AuthenticatorResponse))}&type=test&remember=false&clientid=${clientID}`, (rawData, status) => { 499 if (status) { 500 if (rawData === 'true') { 501 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_16; 502 wwa_enable_buttons(); 503 updateList(); 504 } else { 505 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15; 506 wwa_enable_buttons(); 507 } 508 } else { 509 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15; 510 wwa_enable_buttons(); 511 } 512 }) 513 }).catch((error) => { 514 console.warn(error); 515 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15; 516 wwa_enable_buttons(); 517 }) 518 } else { 519 button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15; 520 wwa_enable_buttons(); 521 } 522 }) 523} 524 525// Update authenticator list 526function updateList() { 527 if (document.getElementsByClassName('wwa-authenticator-list').length === 0) { 528 return; 529 } 530 let request = wwa_ajax(); 531 request.get(wwa_php_vars.ajax_url, '?action=wwa_authenticator_list', (rawData, status) => { 532 if (status) { 533 let data = rawData; 534 try { 535 data = JSON.parse(rawData); 536 } catch (e) { 537 console.warn(rawData); 538 wwa_dom('wwa-authenticator-list', (dom) => { dom.innerHTML = `<tr><td colspan="${document.getElementsByClassName('wwa-usernameless-th')[0].style.display === 'none' ? '5' : '6'}">${wwa_php_vars.i18n_17}</td></tr>` }, 'class'); 539 return; 540 } 541 if (data.length === 0) { 542 if (wwa_php_vars.usernameless === 'true') { 543 wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'table-cell' }); 544 } else { 545 wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'none' }); 546 } 547 wwa_dom('wwa-authenticator-list', (dom) => { dom.innerHTML = `<tr><td colspan="${document.getElementsByClassName('wwa-usernameless-th')[0].style.display === 'none' ? '5' : '6'}">${wwa_php_vars.i18n_23}</td></tr>` }, 'class'); 548 wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.innerText = '' }, 'class'); 549 wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = '' }, 'class'); 550 return; 551 } 552 let htmlStr = ''; 553 let has_usernameless = false;
554 let has_disabled_type = false; 555 for (let item of data) { 556 let item_type_disabled = false; 557 if (item.usernameless) { 558 has_usernameless = true; 559 } 560 if (wwa_php_vars.allow_authenticator_type !== 'none') { 561 if (wwa_php_vars.allow_authenticator_type !== item.type) { 562 has_disabled_type = true; 563 item_type_disabled = true; 564 } 565 } 566 htmlStr += `<tr><td>${item.name}</td><td>${item.type === 'none' ? wwa_php_vars.i18n_24 : (item.type === 'platform' ? wwa_php_vars.i18n_25 : wwa_php_vars.i18n_26)}${item_type_disabled ? wwa_php_vars.i18n_35 : ''}</td><td>${item.added}</td><td>${item.last_used}</td><td class="wwa-usernameless-td">${item.usernameless ? wwa_php_vars.i18n_1 + (wwa_php_vars.usernameless === 'true' ? '' : wwa_php_vars.i18n_9) : wwa_php_vars.i18n_8}</td><td class="wwa-key-${item.key}"><a href="javascript:renameAuthenticator('${item.key}', '${item.name}')">${wwa_php_vars.i18n_20}</a> | <a href="javascript:removeAuthenticator('${item.key}', '${item.name}')">${wwa_php_vars.i18n_27}</a></td></tr>`; 567 } 568 wwa_dom('wwa-authenticator-list', (dom) => { dom.innerHTML = htmlStr }, 'class'); 569 if (has_usernameless || wwa_php_vars.usernameless === 'true') { 570 wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'table-cell' }); 571 } else { 572 wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'none' }); 573 } 574 if (has_usernameless && wwa_php_vars.usernameless !== 'true') { 575 wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.innerText = wwa_php_vars.i18n_10 }, 'class'); 576 wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.style.display = 'block'; }, 'class'); 577 } else { 578 wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.innerText = ''; dom.style.display = 'none' }, 'class'); 579 } 580 if (has_disabled_type && wwa_php_vars.allow_authenticator_type !== 'none') { 581 if (wwa_php_vars.allow_authenticator_type === 'platform') { 582 wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = wwa_php_vars.i18n_36 }, 'class'); 583 } else { 584 wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = wwa_php_vars.i18n_37 }, 'class'); 585 } 586 wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.style.display = 'block'; }, 'class'); 587 } else { 588 wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = ''; dom.style.display = 'none' }, 'class'); 589 } 590 } else { 591 wwa_dom('wwa-authenticator-list', (dom) => { dom.innerHTML = `<tr><td colspan="${document.getElementsByClassName('wwa-usernameless-th')[0].style.display === 'none' ? '5' : '6'}">${wwa_php_vars.i18n_17}</td></tr>` }, 'class'); 592 } 593 }) 594} 595 596/** Rename an authenticator 597 * 598 * @param {string} id Authenticator ID 599 * @param {string} name Current authenticator name 600 */ 601function renameAuthenticator(id, name) { 602 let new_name = prompt(wwa_php_vars.i18n_21, name); 603 if (new_name === '') { 604 alert(wwa_php_vars.i18n_12); 605 } else if (new_name !== null && new_name !== name) { 606 let request = wwa_ajax(); 607 wwa_dom(`wwa-key-${id}`, (dom) => { dom.innerText = wwa_php_vars.i18n_22 }, 'class'); 608 request.get(wwa_php_vars.ajax_url, `?action=wwa_modify_authenticator&id=${encodeURIComponent(id)}&name=${encodeURIComponent(new_name)}&target=rename`, (data, status) => { 609 if (status) { 610 updateList(); 611 } else { 612 alert(`Error: ${data}`); 613 updateList(); 614 } 615 }) 616 } 617} 618 619/** Remove an authenticator 620 * 621 * @param {string} id Authenticator ID 622 * @param {string} name Authenticator name 623 */ 624function removeAuthenticator(id, name) { 625 if (confirm(wwa_php_vars.i18n_18 + name + (document.getElementsByClassName('wwa-authenticator-list')[0].children.length === 1 ? '\n' + wwa_php_vars.i18n_34 : ''))) { 626 wwa_dom(`wwa-key-${id}`, (dom) => { dom.innerText = wwa_php_vars.i18n_19 }, 'class'); 627 let request = wwa_ajax(); 628 request.get(wwa_php_vars.ajax_url, `?action=wwa_modify_authenticator&id=${encodeURIComponent(id)}&target=remove`, (data, status) => { 629 if (status) { 630 updateList(); 631 } else { 632 alert(`Error: ${data}`); 633 updateList(); 634 } 635 }) 636 } 637}
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.