modified file bootstrap-buttons.css
This commit is contained in:
@ -1,85 +0,0 @@
|
||||
jQuery(() => {
|
||||
let div = document.getElementById('wwa_log');
|
||||
if (div !== null) {
|
||||
div.scrollTop = div.scrollHeight;
|
||||
if (jQuery('#wwa-remove-log').length === 0) {
|
||||
setInterval(() => {
|
||||
updateLog();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
jQuery('input[name=user_verification]').on('change', () => {
|
||||
if (jQuery('input[name=user_verification]:checked').val() === 'false') {
|
||||
jQuery('#wwa-uv-field').after(`<div class="notice notice-warning" role="alert" id="wp-webauthn-uv-warning"><p>${php_vars.i18n_1}</p></div>`);
|
||||
} else {
|
||||
jQuery('#wp-webauthn-uv-warning').remove();
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (jQuery('input[name=user_verification]:checked').val() === 'false') {
|
||||
jQuery('#wwa-uv-field').after(`<div class="notice notice-warning" role="alert" id="wp-webauthn-uv-warning"><p>${php_vars.i18n_1}</p></div>`);
|
||||
}
|
||||
}, 0);
|
||||
})
|
||||
|
||||
// Update log
|
||||
function updateLog() {
|
||||
if (jQuery('#wwa_log').length === 0) {
|
||||
return;
|
||||
}
|
||||
jQuery.ajax({
|
||||
url: php_vars.ajax_url,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'wwa_get_log'
|
||||
},
|
||||
success: function (data) {
|
||||
if (typeof data === 'string') {
|
||||
console.warn(data);
|
||||
jQuery('#wwa_log').text(php_vars.i18n_3);
|
||||
return;
|
||||
}
|
||||
if (data.length === 0) {
|
||||
document.getElementById('clear_log').disabled = true;
|
||||
jQuery('#wwa_log').text('');
|
||||
jQuery('#wwa-remove-log').remove();
|
||||
jQuery('#log-count').text(php_vars.i18n_2 + '0');
|
||||
return;
|
||||
}
|
||||
document.getElementById('clear_log').disabled = false;
|
||||
let data_str = data.join('\n');
|
||||
if (data_str !== jQuery('#wwa_log').text()) {
|
||||
jQuery('#wwa_log').text(data_str);
|
||||
jQuery('#log-count').text(php_vars.i18n_2 + data.length);
|
||||
let div = document.getElementById('wwa_log');
|
||||
div.scrollTop = div.scrollHeight;
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
jQuery('#wwa_log').text(php_vars.i18n_3);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Clear log
|
||||
jQuery('#clear_log').click((e) => {
|
||||
e.preventDefault();
|
||||
document.getElementById('clear_log').disabled = true;
|
||||
jQuery.ajax({
|
||||
url: php_vars.ajax_url,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'wwa_clear_log'
|
||||
},
|
||||
success: function () {
|
||||
updateLog();
|
||||
},
|
||||
error: function (data) {
|
||||
document.getElementById('clear_log').disabled = false;
|
||||
alert(`Error: ${data}`);
|
||||
updateLog();
|
||||
}
|
||||
})
|
||||
})
|
@ -1,70 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (document.querySelectorAll('#lostpasswordform, #registerform, .admin-email-confirm-form').length > 0) {
|
||||
return;
|
||||
}
|
||||
window.onload = () => {
|
||||
if (php_vars.webauthn_only === 'true') {
|
||||
if ((window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function')) {
|
||||
// Not support, show a message
|
||||
if (document.querySelectorAll('#login > h1').length > 0) {
|
||||
let dom = document.createElement('p');
|
||||
dom.className = 'message';
|
||||
dom.innerHTML = php_vars.i18n_8;
|
||||
document.querySelectorAll('#login > h1')[0].parentNode.insertBefore(dom, document.querySelectorAll('#login > h1')[0].nextElementSibling)
|
||||
}
|
||||
}
|
||||
wwa_dom('#loginform', (dom) => { dom.classList.add('wwa-webauthn-only') });
|
||||
if (document.getElementsByClassName('user-pass-wrap').length > 0) {
|
||||
wwa_dom('.user-pass-wrap, #wp-submit', (dom) => { dom.parentNode.removeChild(dom) });
|
||||
if (php_vars.remember_me === 'false' ) {
|
||||
wwa_dom('.forgetmenot', (dom) => { dom.parentNode.removeChild(dom) });
|
||||
}
|
||||
} else {
|
||||
// WordPress 5.2-
|
||||
wwa_dom('#wp-submit', (dom) => { dom.parentNode.removeChild(dom) });
|
||||
if (php_vars.remember_me === 'false' ) {
|
||||
wwa_dom('.forgetmenot', (dom) => { dom.parentNode.removeChild(dom) });
|
||||
}
|
||||
const targetDOM = document.getElementById('loginform').getElementsByTagName('p')[1];
|
||||
targetDOM.parentNode.removeChild(targetDOM);
|
||||
}
|
||||
}
|
||||
if (!(window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function') || php_vars.webauthn_only === 'true') {
|
||||
// If supported, toggle
|
||||
if (php_vars.webauthn_only !== 'true') {
|
||||
if (document.getElementsByClassName('user-pass-wrap').length > 0) {
|
||||
wwa_dom('.user-pass-wrap, #wp-submit', (dom) => { dom.style.display = 'none' });
|
||||
if (php_vars.remember_me === 'false' ) {
|
||||
wwa_dom('.forgetmenot', (dom) => { dom.style.display = 'none' });
|
||||
}
|
||||
} else {
|
||||
// WordPress 5.2-
|
||||
wwa_dom('#wp-submit', (dom) => { dom.style.display = 'none' });
|
||||
if (php_vars.remember_me === 'false' ) {
|
||||
wwa_dom('.forgetmenot', (dom) => { dom.style.display = 'none' });
|
||||
}
|
||||
document.getElementById('loginform').getElementsByTagName('p')[1].style.display = 'none';
|
||||
}
|
||||
}
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.style.display = 'flex' }, 'class');
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.style.cssText = `${dom.style.cssText}display: block !important` }, 'id');
|
||||
wwa_dom('user_login', (dom) => { dom.focus() }, 'id');
|
||||
wwa_dom('wp-submit', (dom) => { dom.disabled = true }, 'id');
|
||||
}
|
||||
if (document.querySelectorAll('#lostpasswordform, #registerform').length > 0) {
|
||||
return;
|
||||
}
|
||||
wwa_dom('user_pass', (dom) => { dom.disabled = false }, 'id');
|
||||
let dom = document.querySelectorAll('#loginform label');
|
||||
if (dom.length > 0) {
|
||||
if (dom[0].getElementsByTagName('input').length > 0) {
|
||||
// WordPress 5.2-
|
||||
dom[0].innerHTML = `<span id="wwa-username-label">${php_vars.i18n_9}</span>${dom[0].innerHTML.split('<br>')[1]}`;
|
||||
} else {
|
||||
dom[0].innerText = php_vars.i18n_9;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -1,635 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Send an AJAX request and get the response
|
||||
const wwa_ajax = function () {
|
||||
let xmlHttpReq = new XMLHttpRequest();
|
||||
return {
|
||||
/** Send an AJAX GET request and get the response
|
||||
*
|
||||
* @param {string} url URL
|
||||
* @param {string} data Attached data
|
||||
* @param {object} callback Callback function
|
||||
*/
|
||||
get: (url, data = '', callback = () => { }) => {
|
||||
xmlHttpReq.open('GET', url + data, true);
|
||||
xmlHttpReq.send();
|
||||
xmlHttpReq.onreadystatechange = () => {
|
||||
if (xmlHttpReq.readyState === 4 && xmlHttpReq.status === 200) {
|
||||
callback(xmlHttpReq.responseText, true);
|
||||
} else if (xmlHttpReq.readyState === 4) {
|
||||
callback('Network Error.', false);
|
||||
}
|
||||
}
|
||||
},
|
||||
/** Send an AJAX POST request and get the response
|
||||
*
|
||||
* @param {string} url URL
|
||||
* @param {string} data Attached data
|
||||
* @param {object} callback Callback function
|
||||
*/
|
||||
post: (url, data = '', callback = () => { }) => {
|
||||
xmlHttpReq.open('POST', url, true);
|
||||
xmlHttpReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xmlHttpReq.send(data);
|
||||
xmlHttpReq.onreadystatechange = () => {
|
||||
if (xmlHttpReq.readyState === 4 && xmlHttpReq.status === 200) {
|
||||
callback(xmlHttpReq.responseText, true);
|
||||
} else if (xmlHttpReq.readyState === 4) {
|
||||
callback('Network Error.', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Operate selected DOMs
|
||||
*
|
||||
* @param {string} selector DOM selector
|
||||
* @param {object} callback Callbck function
|
||||
* @param {string} method Selecte method
|
||||
*/
|
||||
const wwa_dom = (selector, callback = () => { }, method = 'query') => {
|
||||
let dom_list = [];
|
||||
if (method === 'id') {
|
||||
let dom = document.getElementById(selector);
|
||||
if (dom) {
|
||||
callback(dom);
|
||||
}
|
||||
return;
|
||||
} else if (method === 'class') {
|
||||
dom_list = document.getElementsByClassName(selector);
|
||||
} else if (method === 'tag') {
|
||||
dom_list = document.getElementsByTagName(selector);
|
||||
} else {
|
||||
dom_list = document.querySelectorAll(selector);
|
||||
}
|
||||
for (let dom of dom_list) {
|
||||
callback(dom);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/** Code Base64URL into Base64
|
||||
*
|
||||
* @param {string} input Base64URL coded string
|
||||
*/
|
||||
function base64url2base64(input) {
|
||||
input = input.replace(/=/g, '').replace(/-/g, '+').replace(/_/g, '/');
|
||||
const pad = input.length % 4;
|
||||
if (pad) {
|
||||
if (pad === 1) {
|
||||
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
|
||||
}
|
||||
input += new Array(5 - pad).join('=');
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/** Code Uint8Array into Base64 string
|
||||
*
|
||||
* @param {Uint8Array} a The Uint8Array needed to be coded into Base64 string
|
||||
*/
|
||||
function arrayToBase64String(a) {
|
||||
return btoa(String.fromCharCode(...a));
|
||||
}
|
||||
|
||||
// Disable all WP-Webauthn buttons
|
||||
function wwa_disable_buttons() {
|
||||
wwa_dom('wwa-test-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wwa-bind-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wwa-login-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wp-submit', (dom) => { dom.disabled = true }, 'id');
|
||||
}
|
||||
|
||||
// Enable all WP-Webauthn buttons
|
||||
function wwa_enable_buttons() {
|
||||
wwa_dom('wwa-test-submit', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-bind-submit', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-login-submit', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wp-submit', (dom) => { dom.disabled = false }, 'id');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
wwa_dom('wwa-login-submit', (dom) => { dom.addEventListener('click', wwa_auth, false) }, 'class');
|
||||
// If traditional form exists
|
||||
if (document.getElementsByClassName('wwa-login-form-traditional').length > 0) {
|
||||
wwa_dom('.wwa-login-form-traditional .login-password', (dom) => {
|
||||
let height = dom.clientHeight;
|
||||
wwa_dom('.wwa-login-form-webauthn .wp-webauthn-notice', (ele) => {
|
||||
ele.style.height = height - 40.4 + 'px';
|
||||
});
|
||||
});
|
||||
wwa_dom('wwa-w2t', (dom) => { dom.addEventListener('click', wwa_toggle, false) }, 'class');
|
||||
wwa_dom('wwa-t2w', (dom) => { dom.addEventListener('click', wwa_toggle, false) }, 'class');
|
||||
} else {
|
||||
wwa_dom('.wwa-login-form-webauthn .wp-webauthn-notice', (ele) => {
|
||||
ele.style.height = '40.6px';
|
||||
});
|
||||
wwa_dom('wwa-w2t', (dom) => { dom.parentNode.removeChild(document.getElementsByClassName('wwa-w2t')[0]) }, 'class');
|
||||
}
|
||||
// If not support
|
||||
if (window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function') {
|
||||
wwa_dom('wwa-test-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wwa-bind-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wwa-show-test', (dom) => { dom.innerText = wwa_php_vars.i18n_31 }, 'class');
|
||||
wwa_dom('wwa-show-progress', (dom) => { dom.innerText = wwa_php_vars.i18n_31 }, 'class');
|
||||
if (document.getElementsByClassName('wwa-login-form-traditional').length > 0) {
|
||||
wwa_dom('wwa-login-form-webauthn', (dom) => { dom.classList.add('wwa-hide-form') }, 'class');
|
||||
}
|
||||
return;
|
||||
}
|
||||
wwa_dom('wwa-login-form-traditional', (dom) => { dom.classList.add('wwa-hide-form') }, 'class');
|
||||
wwa_dom('wwa-bind-submit', (dom) => { dom.addEventListener('click', wwa_bind, false) }, 'class');
|
||||
wwa_dom('wwa-test-submit', (dom) => { dom.addEventListener('click', wwa_verify, false) }, 'class');
|
||||
wwa_dom('wwa-test-usernameless-submit', (dom) => { dom.addEventListener('click', wwa_verify, false) }, 'class');
|
||||
updateList();
|
||||
});
|
||||
|
||||
// Toggle form
|
||||
function wwa_toggle(e) {
|
||||
e.preventDefault();
|
||||
if (document.getElementsByClassName('wwa-login-form-traditional').length > 0) {
|
||||
// Disable buttons if it is not shown
|
||||
if (document.getElementsByClassName('wwa-login-form-traditional')[0].className.indexOf('wwa-hide-form') !== -1) {
|
||||
wwa_dom('wp-submit', (dom) => { dom.disabled = false }, 'id');
|
||||
wwa_dom('wwa-login-submit', (dom) => { dom.disabled = true }, 'class');
|
||||
setTimeout(() => {
|
||||
wwa_dom('user_login', (dom) => { dom.focus() }, 'id');
|
||||
}, 0);
|
||||
} else {
|
||||
wwa_dom('wp-submit', (dom) => { dom.disabled = true }, 'id');
|
||||
wwa_dom('wwa-login-submit', (dom) => { dom.disabled = false }, 'class');
|
||||
setTimeout(() => {
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.focus() }, 'id');
|
||||
}, 0);
|
||||
}
|
||||
document.getElementsByClassName('wwa-login-form-traditional')[0].classList.toggle('wwa-hide-form');
|
||||
document.getElementsByClassName('wwa-login-form-webauthn')[0].classList.toggle('wwa-hide-form');
|
||||
}
|
||||
}
|
||||
|
||||
// Auth
|
||||
function wwa_auth() {
|
||||
if (window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function') {
|
||||
alert(wwa_php_vars.i18n_31);
|
||||
return;
|
||||
}
|
||||
let wwa_username = this.parentNode.previousElementSibling.previousElementSibling.getElementsByClassName('wwa-user-name')[0].value;
|
||||
if (wwa_username === '' && wwa_php_vars.usernameless !== 'true') {
|
||||
alert(wwa_php_vars.i18n_11);
|
||||
return;
|
||||
}
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.readOnly = true }, 'class');
|
||||
wwa_disable_buttons();
|
||||
let button_dom = this;
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_3;
|
||||
let request = wwa_ajax();
|
||||
request.get(wwa_php_vars.ajax_url, `?action=wwa_auth_start&user=${encodeURIComponent(wwa_username)}&type=auth`, (rawData, status) => {
|
||||
if (status) {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_4;
|
||||
let data = rawData;
|
||||
try {
|
||||
data = JSON.parse(rawData);
|
||||
} catch (e) {
|
||||
console.warn(rawData);
|
||||
wwa_enable_buttons();
|
||||
if (wwa_php_vars.usernameless === 'true' && wwa_username === '') {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33;
|
||||
} else {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7;
|
||||
}
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
return;
|
||||
}
|
||||
data.challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) => c.charCodeAt(0));
|
||||
|
||||
if (data.allowCredentials) {
|
||||
data.allowCredentials = data.allowCredentials.map((item) => {
|
||||
item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0));
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
// Save client ID
|
||||
const clientID = data.clientID;
|
||||
delete data.clientID;
|
||||
|
||||
navigator.credentials.get({ 'publicKey': data }).then((credentialInfo) => {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_5;
|
||||
return credentialInfo;
|
||||
}).then((data) => {
|
||||
const publicKeyCredential = {
|
||||
id: data.id,
|
||||
type: data.type,
|
||||
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
|
||||
response: {
|
||||
authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
|
||||
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
|
||||
signature: arrayToBase64String(new Uint8Array(data.response.signature)),
|
||||
userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
|
||||
}
|
||||
};
|
||||
return publicKeyCredential;
|
||||
}).then(JSON.stringify).then((AuthenticatorResponse) => {
|
||||
let response = wwa_ajax();
|
||||
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') : 'false')}`, (data, status) => {
|
||||
if (status) {
|
||||
if (data === 'true') {
|
||||
wwa_enable_buttons();
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_6;
|
||||
if (document.querySelectorAll('p.login-submit input[name="redirect_to"]').length > 0) {
|
||||
setTimeout(() => {
|
||||
window.location.href = document.querySelectorAll('p.login-submit input[name="redirect_to"]')[0].value;
|
||||
}, 200);
|
||||
} else {
|
||||
if (document.getElementsByClassName('wwa-redirect-to').length > 0) {
|
||||
setTimeout(() => {
|
||||
window.location.href = document.getElementsByClassName('wwa-redirect-to')[0].value;
|
||||
}, 200);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wwa_enable_buttons();
|
||||
if (wwa_php_vars.usernameless === 'true' && wwa_username === '') {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33;
|
||||
} else {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7;
|
||||
}
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
}
|
||||
} else {
|
||||
wwa_enable_buttons();
|
||||
if (wwa_php_vars.usernameless === 'true' && wwa_username === '') {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33;
|
||||
} else {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7;
|
||||
}
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
}
|
||||
})
|
||||
}).catch((error) => {
|
||||
console.warn(error);
|
||||
wwa_enable_buttons();
|
||||
if (wwa_php_vars.usernameless === 'true' && wwa_username === '') {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33;
|
||||
} else {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7;
|
||||
}
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
})
|
||||
} else {
|
||||
wwa_enable_buttons();
|
||||
if (wwa_php_vars.usernameless === 'true' && wwa_username === '') {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7 + wwa_php_vars.i18n_33;
|
||||
} else {
|
||||
button_dom.parentNode.previousElementSibling.innerHTML = wwa_php_vars.i18n_7;
|
||||
}
|
||||
wwa_dom('wwa-user-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Bind
|
||||
function wwa_bind() {
|
||||
let button_dom = this;
|
||||
let wwa_name = this.parentNode.parentNode.getElementsByClassName('wwa-authenticator-name')[0].value;
|
||||
if (wwa_name === '') {
|
||||
alert(wwa_php_vars.i18n_12);
|
||||
return;
|
||||
}
|
||||
let wwa_type = this.parentNode.parentNode.getElementsByClassName('wwa-authenticator-type')[0].value;
|
||||
let wwa_usernameless = this.parentNode.parentNode.querySelectorAll('.wwa-authenticator-usernameless:checked')[0] ? this.parentNode.parentNode.querySelectorAll('.wwa-authenticator-usernameless:checked')[0].value : 'false';
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_3;
|
||||
wwa_disable_buttons();
|
||||
// Lock options
|
||||
wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = true }, 'class');
|
||||
wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = true }, 'class');
|
||||
wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = true }, 'class');
|
||||
let request = wwa_ajax();
|
||||
request.get(wwa_php_vars.ajax_url, `?action=wwa_create&name=${encodeURIComponent(wwa_name)}&type=${encodeURIComponent(wwa_type)}&usernameless=${wwa_usernameless}`, (rawData, status) => {
|
||||
if (status) {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_28;
|
||||
let data = rawData;
|
||||
try {
|
||||
data = JSON.parse(rawData);
|
||||
} catch (e) {
|
||||
console.warn(rawData);
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30;
|
||||
wwa_enable_buttons();
|
||||
wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class');
|
||||
updateList();
|
||||
return;
|
||||
}
|
||||
let challenge = new Uint8Array(32);
|
||||
let user_id = new Uint8Array(32);
|
||||
challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) => c.charCodeAt(0));
|
||||
user_id = Uint8Array.from(window.atob(base64url2base64(data.user.id)), (c) => c.charCodeAt(0));
|
||||
|
||||
let public_key = {
|
||||
challenge: challenge,
|
||||
rp: {
|
||||
id: data.rp.id,
|
||||
name: data.rp.name
|
||||
},
|
||||
user: {
|
||||
id: user_id,
|
||||
name: data.user.name,
|
||||
displayName: data.user.displayName
|
||||
},
|
||||
pubKeyCredParams: data.pubKeyCredParams,
|
||||
authenticatorSelection: data.authenticatorSelection,
|
||||
timeout: data.timeout
|
||||
}
|
||||
|
||||
// If some authenticators are already registered, exclude
|
||||
if (data.excludeCredentials) {
|
||||
public_key.excludeCredentials = data.excludeCredentials.map((item) => {
|
||||
item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0));
|
||||
return item;
|
||||
})
|
||||
}
|
||||
|
||||
// Save client ID
|
||||
const clientID = data.clientID;
|
||||
delete data.clientID;
|
||||
|
||||
// Create, a pop-up window should appear
|
||||
navigator.credentials.create({ 'publicKey': public_key }).then((newCredentialInfo) => {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_32;
|
||||
return newCredentialInfo;
|
||||
}).then((data) => {
|
||||
// Code Uint8Array into string for transmission
|
||||
const publicKeyCredential = {
|
||||
id: data.id,
|
||||
type: data.type,
|
||||
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
|
||||
response: {
|
||||
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
|
||||
attestationObject: arrayToBase64String(new Uint8Array(data.response.attestationObject))
|
||||
}
|
||||
};
|
||||
return publicKeyCredential;
|
||||
}).then(JSON.stringify).then((AuthenticatorAttestationResponse) => {
|
||||
let response = wwa_ajax();
|
||||
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) => {
|
||||
if (status) {
|
||||
if (rawData === 'true') {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_29;
|
||||
wwa_enable_buttons();
|
||||
wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false; dom.value = '' }, 'class');
|
||||
wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class');
|
||||
updateList();
|
||||
} else {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30;
|
||||
wwa_enable_buttons();
|
||||
wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class');
|
||||
updateList();
|
||||
}
|
||||
} else {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30;
|
||||
wwa_enable_buttons();
|
||||
wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class');
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
}).catch((error) => {
|
||||
console.warn(error);
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30;
|
||||
wwa_enable_buttons();
|
||||
wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class');
|
||||
updateList();
|
||||
})
|
||||
} else {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_30;
|
||||
wwa_enable_buttons();
|
||||
wwa_dom('wwa-authenticator-name', (dom) => { dom.readOnly = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-type', (dom) => { dom.disabled = false }, 'class');
|
||||
wwa_dom('wwa-authenticator-usernameless', (dom) => { dom.disabled = false }, 'class');
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Verify
|
||||
function wwa_verify() {
|
||||
let button_dom = this;
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_3;
|
||||
let usernameless = this.className.indexOf('wwa-test-usernameless-submit') === -1 ? false : true;
|
||||
wwa_disable_buttons();
|
||||
let request = wwa_ajax();
|
||||
request.get(wwa_php_vars.ajax_url, `?action=wwa_auth_start&type=test&usernameless=${usernameless ? 'true' : 'false'}`, (rawData, status) => {
|
||||
if (status) {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_4;
|
||||
if (rawData === 'User not inited.') {
|
||||
wwa_enable_buttons();
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15;
|
||||
return;
|
||||
}
|
||||
let data = rawData;
|
||||
try {
|
||||
data = JSON.parse(rawData);
|
||||
} catch (e) {
|
||||
console.warn(rawData);
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15;
|
||||
wwa_enable_buttons();
|
||||
return;
|
||||
}
|
||||
data.challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) => c.charCodeAt(0));
|
||||
|
||||
if (data.allowCredentials) {
|
||||
data.allowCredentials = data.allowCredentials.map((item) => {
|
||||
item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0));
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
if (data.allowCredentials && wwa_php_vars.allow_authenticator_type && wwa_php_vars.allow_authenticator_type !== 'none') {
|
||||
for (let credential of data.allowCredentials) {
|
||||
if (wwa_php_vars.allow_authenticator_type === 'cross-platform') {
|
||||
credential.transports = ['usb', 'nfc', 'ble'];
|
||||
} else if (wwa_php_vars.allow_authenticator_type === 'platform') {
|
||||
credential.transports = ['internal'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save client ID
|
||||
const clientID = data.clientID;
|
||||
delete data.clientID;
|
||||
|
||||
navigator.credentials.get({ 'publicKey': data }).then((credentialInfo) => {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_13;
|
||||
return credentialInfo;
|
||||
}).then((data) => {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_14;
|
||||
const publicKeyCredential = {
|
||||
id: data.id,
|
||||
type: data.type,
|
||||
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
|
||||
response: {
|
||||
authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
|
||||
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
|
||||
signature: arrayToBase64String(new Uint8Array(data.response.signature)),
|
||||
userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
|
||||
}
|
||||
};
|
||||
return publicKeyCredential;
|
||||
}).then(JSON.stringify).then((AuthenticatorResponse) => {
|
||||
let response = wwa_ajax();
|
||||
response.post(`${wwa_php_vars.ajax_url}?action=wwa_auth`, `data=${encodeURIComponent(window.btoa(AuthenticatorResponse))}&type=test&remember=false&clientid=${clientID}`, (rawData, status) => {
|
||||
if (status) {
|
||||
if (rawData === 'true') {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_16;
|
||||
wwa_enable_buttons();
|
||||
updateList();
|
||||
} else {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15;
|
||||
wwa_enable_buttons();
|
||||
}
|
||||
} else {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15;
|
||||
wwa_enable_buttons();
|
||||
}
|
||||
})
|
||||
}).catch((error) => {
|
||||
console.warn(error);
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15;
|
||||
wwa_enable_buttons();
|
||||
})
|
||||
} else {
|
||||
button_dom.nextElementSibling.innerHTML = wwa_php_vars.i18n_15;
|
||||
wwa_enable_buttons();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Update authenticator list
|
||||
function updateList() {
|
||||
if (document.getElementsByClassName('wwa-authenticator-list').length === 0) {
|
||||
return;
|
||||
}
|
||||
let request = wwa_ajax();
|
||||
request.get(wwa_php_vars.ajax_url, '?action=wwa_authenticator_list', (rawData, status) => {
|
||||
if (status) {
|
||||
let data = rawData;
|
||||
try {
|
||||
data = JSON.parse(rawData);
|
||||
} catch (e) {
|
||||
console.warn(rawData);
|
||||
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');
|
||||
return;
|
||||
}
|
||||
if (data.length === 0) {
|
||||
if (wwa_php_vars.usernameless === 'true') {
|
||||
wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'table-cell' });
|
||||
} else {
|
||||
wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'none' });
|
||||
}
|
||||
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');
|
||||
wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.innerText = '' }, 'class');
|
||||
wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = '' }, 'class');
|
||||
return;
|
||||
}
|
||||
let htmlStr = '';
|
||||
let has_usernameless = false;
|
||||
let has_disabled_type = false;
|
||||
for (let item of data) {
|
||||
let item_type_disabled = false;
|
||||
if (item.usernameless) {
|
||||
has_usernameless = true;
|
||||
}
|
||||
if (wwa_php_vars.allow_authenticator_type !== 'none') {
|
||||
if (wwa_php_vars.allow_authenticator_type !== item.type) {
|
||||
has_disabled_type = true;
|
||||
item_type_disabled = true;
|
||||
}
|
||||
}
|
||||
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>`;
|
||||
}
|
||||
wwa_dom('wwa-authenticator-list', (dom) => { dom.innerHTML = htmlStr }, 'class');
|
||||
if (has_usernameless || wwa_php_vars.usernameless === 'true') {
|
||||
wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'table-cell' });
|
||||
} else {
|
||||
wwa_dom('.wwa-usernameless-th, .wwa-usernameless-td', (dom) => { dom.style.display = 'none' });
|
||||
}
|
||||
if (has_usernameless && wwa_php_vars.usernameless !== 'true') {
|
||||
wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.innerText = wwa_php_vars.i18n_10 }, 'class');
|
||||
wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.style.display = 'block'; }, 'class');
|
||||
} else {
|
||||
wwa_dom('wwa-authenticator-list-usernameless-tip', (dom) => { dom.innerText = ''; dom.style.display = 'none' }, 'class');
|
||||
}
|
||||
if (has_disabled_type && wwa_php_vars.allow_authenticator_type !== 'none') {
|
||||
if (wwa_php_vars.allow_authenticator_type === 'platform') {
|
||||
wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = wwa_php_vars.i18n_36 }, 'class');
|
||||
} else {
|
||||
wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = wwa_php_vars.i18n_37 }, 'class');
|
||||
}
|
||||
wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.style.display = 'block'; }, 'class');
|
||||
} else {
|
||||
wwa_dom('wwa-authenticator-list-type-tip', (dom) => { dom.innerText = ''; dom.style.display = 'none' }, 'class');
|
||||
}
|
||||
} else {
|
||||
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');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** Rename an authenticator
|
||||
*
|
||||
* @param {string} id Authenticator ID
|
||||
* @param {string} name Current authenticator name
|
||||
*/
|
||||
function renameAuthenticator(id, name) {
|
||||
let new_name = prompt(wwa_php_vars.i18n_21, name);
|
||||
if (new_name === '') {
|
||||
alert(wwa_php_vars.i18n_12);
|
||||
} else if (new_name !== null && new_name !== name) {
|
||||
let request = wwa_ajax();
|
||||
wwa_dom(`wwa-key-${id}`, (dom) => { dom.innerText = wwa_php_vars.i18n_22 }, 'class');
|
||||
request.get(wwa_php_vars.ajax_url, `?action=wwa_modify_authenticator&id=${encodeURIComponent(id)}&name=${encodeURIComponent(new_name)}&target=rename`, (data, status) => {
|
||||
if (status) {
|
||||
updateList();
|
||||
} else {
|
||||
alert(`Error: ${data}`);
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove an authenticator
|
||||
*
|
||||
* @param {string} id Authenticator ID
|
||||
* @param {string} name Authenticator name
|
||||
*/
|
||||
function removeAuthenticator(id, name) {
|
||||
if (confirm(wwa_php_vars.i18n_18 + name + (document.getElementsByClassName('wwa-authenticator-list')[0].children.length === 1 ? '\n' + wwa_php_vars.i18n_34 : ''))) {
|
||||
wwa_dom(`wwa-key-${id}`, (dom) => { dom.innerText = wwa_php_vars.i18n_19 }, 'class');
|
||||
let request = wwa_ajax();
|
||||
request.get(wwa_php_vars.ajax_url, `?action=wwa_modify_authenticator&id=${encodeURIComponent(id)}&target=remove`, (data, status) => {
|
||||
if (status) {
|
||||
updateList();
|
||||
} else {
|
||||
alert(`Error: ${data}`);
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,400 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Send an AJAX request and get the response
|
||||
const wwa_ajax = function () {
|
||||
let xmlHttpReq = new XMLHttpRequest();
|
||||
return {
|
||||
/** Send an AJAX GET request and get the response
|
||||
*
|
||||
* @param {string} url URL
|
||||
* @param {string} data Attached data
|
||||
* @param {object} callback Callback function
|
||||
*/
|
||||
get: (url, data = '', callback = () => { }) => {
|
||||
xmlHttpReq.open('GET', url + data, true);
|
||||
xmlHttpReq.send();
|
||||
xmlHttpReq.onreadystatechange = () => {
|
||||
if (xmlHttpReq.readyState === 4 && xmlHttpReq.status === 200) {
|
||||
callback(xmlHttpReq.responseText, true);
|
||||
} else if (xmlHttpReq.readyState === 4) {
|
||||
callback('Network Error.', false);
|
||||
}
|
||||
}
|
||||
},
|
||||
/** Send an AJAX POST request and get the response
|
||||
*
|
||||
* @param {string} url URL
|
||||
* @param {string} data Attached data
|
||||
* @param {object} callback Callback function
|
||||
*/
|
||||
post: (url, data = '', callback = () => { }) => {
|
||||
xmlHttpReq.open('POST', url, true);
|
||||
xmlHttpReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xmlHttpReq.send(data);
|
||||
xmlHttpReq.onreadystatechange = () => {
|
||||
if (xmlHttpReq.readyState === 4 && xmlHttpReq.status === 200) {
|
||||
callback(xmlHttpReq.responseText, true);
|
||||
} else if (xmlHttpReq.readyState === 4) {
|
||||
callback('Network Error.', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Operate selected DOMs
|
||||
*
|
||||
* @param {string} selector DOM selector
|
||||
* @param {object} callback Callbck function
|
||||
* @param {string} method Selecte method
|
||||
*/
|
||||
const wwa_dom = (selector, callback = () => { }, method = 'query') => {
|
||||
let dom_list = [];
|
||||
if (method === 'id') {
|
||||
let dom = document.getElementById(selector);
|
||||
if (dom) {
|
||||
callback(dom);
|
||||
}
|
||||
return;
|
||||
} else if (method === 'class') {
|
||||
dom_list = document.getElementsByClassName(selector);
|
||||
} else if (method === 'tag') {
|
||||
dom_list = document.getElementsByTagName(selector);
|
||||
} else {
|
||||
dom_list = document.querySelectorAll(selector);
|
||||
}
|
||||
for (let dom of dom_list) {
|
||||
callback(dom);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let wwaSupported = true;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (document.querySelectorAll('#lostpasswordform, #registerform, .admin-email-confirm-form').length > 0) {
|
||||
return;
|
||||
}
|
||||
let button_check = document.createElement('button');
|
||||
button_check.id = 'wp-webauthn-check';
|
||||
button_check.type = 'button';
|
||||
button_check.className = 'button button-large button-primary';
|
||||
button_check.innerHTML = php_vars.i18n_1;
|
||||
let button_toggle = document.createElement('button');
|
||||
if (php_vars.webauthn_only !== 'true') {
|
||||
button_toggle.id = 'wp-webauthn';
|
||||
button_toggle.type = 'button';
|
||||
button_toggle.className = 'button button-large';
|
||||
button_toggle.innerHTML = '<span class="dashicons dashicons-update-alt"></span>';
|
||||
}
|
||||
let submit = document.getElementById('wp-submit');
|
||||
if (submit) {
|
||||
if (php_vars.webauthn_only !== 'true') {
|
||||
submit.parentNode.insertBefore(button_toggle, submit.nextElementSibling);
|
||||
}
|
||||
submit.parentNode.insertBefore(button_check, submit.nextElementSibling);
|
||||
}
|
||||
let notice = document.createElement('div');
|
||||
notice.className = 'wp-webauthn-notice';
|
||||
notice.innerHTML = `<span><span class="dashicons dashicons-shield-alt"></span> ${php_vars.i18n_2}<span>`;
|
||||
let forgetmenot = document.getElementsByClassName('forgetmenot');
|
||||
if (forgetmenot.length > 0) {
|
||||
forgetmenot[0].parentNode.insertBefore(notice, forgetmenot[0]);
|
||||
}
|
||||
wwa_dom('wp-webauthn-notice', (dom) => {
|
||||
const passwordInput = document.getElementsByClassName('user-pass-wrap');
|
||||
if (passwordInput.length > 0) {
|
||||
dom.style.height = (passwordInput[0].offsetHeight - 10) + 'px';
|
||||
} else {
|
||||
// WordPress 5.2-
|
||||
const legacyPasswordInput = document.getElementById('loginform').getElementsByTagName('p')[1];
|
||||
dom.style.height = (legacyPasswordInput.offsetHeight - 10) + 'px';
|
||||
}
|
||||
}, 'class');
|
||||
let btnWidth = document.getElementById('wp-submit') ? document.getElementById('wp-submit').clientWidth : 0;
|
||||
if (btnWidth < 20 || btnWidth === undefined) {
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.style.width = 'auto' }, 'id');
|
||||
} else {
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.style.width = btnWidth }, 'id');
|
||||
}
|
||||
if (window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function') {
|
||||
wwaSupported = false;
|
||||
wwa_dom('wp-webauthn', (dom) => { dom.style.display = 'none' }, 'id');
|
||||
}
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.addEventListener('click', check, false) }, 'id');
|
||||
wwa_dom('wp-webauthn', (dom) => { dom.addEventListener('click', toggle, false) }, 'id');
|
||||
})
|
||||
|
||||
window.onresize = function () {
|
||||
if (document.querySelectorAll('#lostpasswordform, #registerform').length > 0) {
|
||||
return;
|
||||
}
|
||||
let btnWidth = document.getElementById('wp-submit').clientWidth;
|
||||
if (btnWidth < 20 || btnWidth === undefined) {
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.style.width = 'auto' }, 'id');
|
||||
} else {
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.style.width = btnWidth }, 'id');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', parseKey, false);
|
||||
|
||||
function parseKey(event) {
|
||||
if (wwaSupported && document.getElementById('wp-webauthn-check').style.display === 'block') {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.click() }, 'id');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function base64url2base64(input) {
|
||||
input = input.replace(/=/g, '').replace(/-/g, '+').replace(/_/g, '/');
|
||||
const pad = input.length % 4;
|
||||
if (pad) {
|
||||
if (pad === 1) {
|
||||
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
|
||||
}
|
||||
input += new Array(5 - pad).join('=');
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
function arrayToBase64String(a) {
|
||||
return btoa(String.fromCharCode(...a));
|
||||
}
|
||||
|
||||
function getQueryString(name) {
|
||||
let reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
|
||||
let reg_rewrite = new RegExp(`(^|/)${name}/([^/]*)(/|$)`, 'i');
|
||||
let r = window.location.search.substr(1).match(reg);
|
||||
let q = window.location.pathname.substr(1).match(reg_rewrite);
|
||||
if (r != null) {
|
||||
return unescape(r[2]);
|
||||
} else if (q != null) {
|
||||
return unescape(q[2]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (document.querySelectorAll('#lostpasswordform, #registerform').length > 0) {
|
||||
return;
|
||||
}
|
||||
if (wwaSupported) {
|
||||
if (document.getElementsByClassName('wp-webauthn-notice')[0].style.display === 'flex') {
|
||||
if (document.getElementsByClassName('user-pass-wrap').length > 0) {
|
||||
wwa_dom('.user-pass-wrap, .forgetmenot, #wp-submit', (dom) => { dom.style.display = 'block' });
|
||||
} else {
|
||||
// WordPress 5.2-
|
||||
wwa_dom('.forgetmenot, #wp-submit', (dom) => { dom.style.display = 'block' });
|
||||
document.getElementById('loginform').getElementsByTagName('p')[1].style.display = 'block';
|
||||
}
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.style.display = 'none' }, 'class');
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.style.cssText = `${dom.style.cssText.split('display: block !important')[0]}display: none !important` }, 'id');
|
||||
wwa_dom('user_pass', (dom) => { dom.disabled = false }, 'id');
|
||||
wwa_dom('user_login', (dom) => { dom.focus() }, 'id');
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = `<span><span class="dashicons dashicons-shield-alt"></span> ${php_vars.i18n_2}</span>` }, 'class');
|
||||
wwa_dom('wp-submit', (dom) => { dom.disabled = false }, 'id');
|
||||
let inputDom = document.querySelectorAll('#loginform label')
|
||||
if (inputDom.length > 0) {
|
||||
if (document.getElementById('wwa-username-label')) {
|
||||
// WordPress 5.2-
|
||||
document.getElementById('wwa-username-label').innerText = php_vars.i18n_10;
|
||||
} else {
|
||||
inputDom[0].innerText = php_vars.i18n_10;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (document.getElementsByClassName('user-pass-wrap').length > 0) {
|
||||
wwa_dom('.user-pass-wrap, #wp-submit', (dom) => { dom.style.display = 'none' });
|
||||
if (php_vars.remember_me === 'false' ) {
|
||||
wwa_dom('.forgetmenot', (dom) => { dom.style.display = 'none' });
|
||||
}
|
||||
} else {
|
||||
// WordPress 5.2-
|
||||
wwa_dom('#wp-submit', (dom) => { dom.style.display = 'none' });
|
||||
if (php_vars.remember_me === 'false' ) {
|
||||
wwa_dom('.forgetmenot', (dom) => { dom.style.display = 'none' });
|
||||
}
|
||||
document.getElementById('loginform').getElementsByTagName('p')[1].style.display = 'none';
|
||||
}
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.style.display = 'flex' }, 'class');
|
||||
wwa_dom('wp-webauthn-check', (dom) => { dom.style.cssText = `${dom.style.cssText.split('display: none !important')[0]}display: block !important` }, 'id');
|
||||
wwa_dom('user_login', (dom) => { dom.focus() }, 'id');
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = `<span><span class="dashicons dashicons-shield-alt"></span> ${php_vars.i18n_2}</span>` }, 'class');
|
||||
wwa_dom('wp-submit', (dom) => { dom.disabled = true }, 'id');
|
||||
let inputDom = document.querySelectorAll('#loginform label')
|
||||
if (inputDom.length > 0) {
|
||||
if (document.getElementById('wwa-username-label')) {
|
||||
// WordPress 5.2-
|
||||
document.getElementById('wwa-username-label').innerText = php_vars.i18n_9;
|
||||
} else {
|
||||
inputDom[0].innerText = php_vars.i18n_9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shake the login form, code from WordPress
|
||||
function wwa_shake(id, a, d) {
|
||||
const c = a.shift();
|
||||
document.getElementById(id).style.left = c + 'px';
|
||||
if (a.length > 0) {
|
||||
setTimeout(() => {
|
||||
wwa_shake(id, a, d);
|
||||
}, d);
|
||||
} else {
|
||||
try {
|
||||
document.getElementById(id).style.position = 'static';
|
||||
wwa_dom('user_login', (dom) => { dom.focus() }, 'id');
|
||||
} catch (e) { }
|
||||
}
|
||||
}
|
||||
|
||||
function check() {
|
||||
if (document.querySelectorAll('#lostpasswordform, #registerform').length > 0) {
|
||||
return;
|
||||
}
|
||||
if (wwaSupported) {
|
||||
if (document.getElementById('user_login').value === '' && php_vars.usernameless !== 'true') {
|
||||
wwa_dom('login_error', (dom) => { dom.remove() }, 'id');
|
||||
wwa_dom('p.message', (dom) => { dom.remove() });
|
||||
if (document.querySelectorAll('#login > h1').length > 0) {
|
||||
let dom = document.createElement('div');
|
||||
dom.id = 'login_error';
|
||||
dom.innerHTML = php_vars.i18n_11;
|
||||
document.querySelectorAll('#login > h1')[0].parentNode.insertBefore(dom, document.querySelectorAll('#login > h1')[0].nextElementSibling)
|
||||
}
|
||||
// Shake the login form, code from WordPress
|
||||
let shake = new Array(15, 30, 15, 0, -15, -30, -15, 0);
|
||||
shake = shake.concat(shake.concat(shake));
|
||||
var form = document.forms[0].id;
|
||||
document.getElementById(form).style.position = 'relative';
|
||||
wwa_shake(form, shake, 20);
|
||||
return;
|
||||
}
|
||||
wwa_dom('user_login', (dom) => { dom.readOnly = true }, 'id');
|
||||
wwa_dom('#wp-webauthn-check, #wp-webauthn', (dom) => { dom.disabled = true });
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_3 }, 'class');
|
||||
let request = wwa_ajax();
|
||||
request.get(php_vars.ajax_url, `?action=wwa_auth_start&user=${encodeURIComponent(document.getElementById('user_login').value)}&type=auth`, (rawData, status) => {
|
||||
if (status) {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_4 }, 'class');
|
||||
let data = rawData;
|
||||
try {
|
||||
data = JSON.parse(rawData);
|
||||
} catch (e) {
|
||||
console.warn(rawData);
|
||||
if (php_vars.usernameless === 'true' && document.getElementById('user_login').value === '') {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 + php_vars.i18n_12 }, 'class');
|
||||
} else {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 }, 'class');
|
||||
}
|
||||
wwa_dom('user_login', (dom) => { dom.readOnly = false }, 'id');
|
||||
wwa_dom('#wp-webauthn-check, #wp-webauthn', (dom) => { dom.disabled = false });
|
||||
return;
|
||||
}
|
||||
data.challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) => c.charCodeAt(0));
|
||||
|
||||
if (data.allowCredentials) {
|
||||
data.allowCredentials = data.allowCredentials.map((item) => {
|
||||
item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0));
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
if (data.allowCredentials && php_vars.allow_authenticator_type && php_vars.allow_authenticator_type !== 'none') {
|
||||
for (let credential of data.allowCredentials) {
|
||||
if (php_vars.allow_authenticator_type === 'cross-platform') {
|
||||
credential.transports = ['usb', 'nfc', 'ble'];
|
||||
} else if (php_vars.allow_authenticator_type === 'platform') {
|
||||
credential.transports = ['internal'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save client ID
|
||||
const clientID = data.clientID;
|
||||
delete data.clientID;
|
||||
|
||||
navigator.credentials.get({ 'publicKey': data }).then((credentialInfo) => {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_5 }, 'class');
|
||||
return credentialInfo;
|
||||
}).then((data) => {
|
||||
const publicKeyCredential = {
|
||||
id: data.id,
|
||||
type: data.type,
|
||||
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
|
||||
response: {
|
||||
authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
|
||||
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
|
||||
signature: arrayToBase64String(new Uint8Array(data.response.signature)),
|
||||
userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
|
||||
}
|
||||
};
|
||||
return publicKeyCredential;
|
||||
}).then(JSON.stringify).then((AuthenticatorResponse) => {
|
||||
let response = wwa_ajax();
|
||||
response.post(`${php_vars.ajax_url}?action=wwa_auth`, `data=${encodeURIComponent(window.btoa(AuthenticatorResponse))}&type=auth&clientid=${clientID}&user=${encodeURIComponent(document.getElementById('user_login').value)}&remember=${php_vars.remember_me === 'false' ? 'false' : (document.getElementById('rememberme') ? (document.getElementById('rememberme').checked ? 'true' : 'false') : 'false')}`, (data, status) => {
|
||||
if (status) {
|
||||
if (data === 'true') {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_6 }, 'class');
|
||||
if (document.querySelectorAll('p.submit input[name="redirect_to"]').length > 0) {
|
||||
setTimeout(() => {
|
||||
window.location.href = document.querySelectorAll('p.submit input[name="redirect_to"]')[0].value;
|
||||
}, 200);
|
||||
} else {
|
||||
if (getQueryString('redirect_to')) {
|
||||
setTimeout(() => {
|
||||
window.location.href = getQueryString('redirect_to');
|
||||
}, 200);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
window.location.href = php_vars.admin_url
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (php_vars.usernameless === 'true' && document.getElementById('user_login').value === '') {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 + php_vars.i18n_12 }, 'class');
|
||||
} else {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 }, 'class');
|
||||
}
|
||||
wwa_dom('user_login', (dom) => { dom.readOnly = false }, 'id');
|
||||
wwa_dom('#wp-webauthn-check, #wp-webauthn', (dom) => { dom.disabled = false });
|
||||
}
|
||||
} else {
|
||||
if (php_vars.usernameless === 'true' && document.getElementById('user_login').value === '') {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 + php_vars.i18n_12 }, 'class');
|
||||
} else {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 }, 'class');
|
||||
}
|
||||
wwa_dom('user_login', (dom) => { dom.readOnly = false }, 'id');
|
||||
wwa_dom('#wp-webauthn-check, #wp-webauthn', (dom) => { dom.disabled = false });
|
||||
}
|
||||
})
|
||||
}).catch((error) => {
|
||||
console.warn(error);
|
||||
if (php_vars.usernameless === 'true' && document.getElementById('user_login').value === '') {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 + php_vars.i18n_12 }, 'class');
|
||||
} else {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 }, 'class');
|
||||
}
|
||||
wwa_dom('user_login', (dom) => { dom.readOnly = false }, 'id');
|
||||
wwa_dom('#wp-webauthn-check, #wp-webauthn', (dom) => { dom.disabled = false });
|
||||
})
|
||||
} else {
|
||||
if (php_vars.usernameless === 'true' && document.getElementById('user_login').value === '') {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 + php_vars.i18n_12 }, 'class');
|
||||
} else {
|
||||
wwa_dom('wp-webauthn-notice', (dom) => { dom.innerHTML = php_vars.i18n_7 }, 'class');
|
||||
}
|
||||
wwa_dom('user_login', (dom) => { dom.readOnly = false }, 'id');
|
||||
wwa_dom('#wp-webauthn-check, #wp-webauthn', (dom) => { dom.disabled = false });
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,466 +0,0 @@
|
||||
// Whether the broswer supports WebAuthn
|
||||
if (window.PublicKeyCredential === undefined || navigator.credentials.create === undefined || typeof navigator.credentials.create !== 'function') {
|
||||
jQuery('#wwa-bind, #wwa-test').attr('disabled', 'disabled');
|
||||
jQuery('#wwa-show-progress').html(php_vars.i18n_5);
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
updateList();
|
||||
})
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
if (document.getElementById('wp-webauthn-error-container')) {
|
||||
document.getElementById('wp-webauthn-error-container').insertBefore(document.getElementById('wp-webauthn-error'), null);
|
||||
}
|
||||
})
|
||||
|
||||
// Update authenticator list
|
||||
function updateList() {
|
||||
jQuery.ajax({
|
||||
url: php_vars.ajax_url,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'wwa_authenticator_list',
|
||||
user_id: php_vars.user_id
|
||||
},
|
||||
success: function (data) {
|
||||
if (typeof data === 'string') {
|
||||
console.warn(data);
|
||||
jQuery('#wwa-authenticator-list').html(`<tr><td colspan="${jQuery('.wwa-usernameless-th').css('display') === 'none' ? '5' : '6'}">${php_vars.i18n_8}</td></tr>`);
|
||||
return;
|
||||
}
|
||||
if (data.length === 0) {
|
||||
if (configs.usernameless === 'true') {
|
||||
jQuery('.wwa-usernameless-th, .wwa-usernameless-td').show();
|
||||
} else {
|
||||
jQuery('.wwa-usernameless-th, .wwa-usernameless-td').hide();
|
||||
}
|
||||
jQuery('#wwa-authenticator-list').html(`<tr><td colspan="${jQuery('.wwa-usernameless-th').css('display') === 'none' ? '5' : '6'}">${php_vars.i18n_17}</td></tr>`);
|
||||
jQuery('#wwa_usernameless_tip').text('');
|
||||
jQuery('#wwa_usernameless_tip').hide();
|
||||
jQuery('#wwa_type_tip').text('');
|
||||
jQuery('#wwa_type_tip').hide();
|
||||
return;
|
||||
}
|
||||
let htmlStr = '';
|
||||
let has_usernameless = false;
|
||||
let has_disabled_type = false;
|
||||
for (item of data) {
|
||||
let item_type_disabled = false;
|
||||
if (item.usernameless) {
|
||||
has_usernameless = true;
|
||||
}
|
||||
if (configs.allow_authenticator_type !== 'none') {
|
||||
if (configs.allow_authenticator_type !== item.type) {
|
||||
has_disabled_type = true;
|
||||
item_type_disabled = true;
|
||||
}
|
||||
}
|
||||
htmlStr += `<tr><td>${item.name}</td><td>${item.type === 'none' ? php_vars.i18n_9 : (item.type === 'platform' ? php_vars.i18n_10 : php_vars.i18n_11)}${item_type_disabled ? php_vars.i18n_29 : ''}</td><td>${item.added}</td><td>${item.last_used}</td><td class="wwa-usernameless-td">${item.usernameless ? php_vars.i18n_24 + (configs.usernameless === 'true' ? '' : php_vars.i18n_26) : php_vars.i18n_25}</td><td id="${item.key}"><a href="javascript:renameAuthenticator('${item.key}', '${item.name}')">${php_vars.i18n_20}</a> | <a href="javascript:removeAuthenticator('${item.key}', '${item.name}')">${php_vars.i18n_12}</a></td></tr>`;
|
||||
}
|
||||
jQuery('#wwa-authenticator-list').html(htmlStr);
|
||||
if (has_usernameless || configs.usernameless === 'true') {
|
||||
jQuery('.wwa-usernameless-th, .wwa-usernameless-td').show();
|
||||
} else {
|
||||
jQuery('.wwa-usernameless-th, .wwa-usernameless-td').hide();
|
||||
}
|
||||
if (has_usernameless && configs.usernameless !== 'true') {
|
||||
jQuery('#wwa_usernameless_tip').text(php_vars.i18n_27);
|
||||
jQuery('#wwa_usernameless_tip').show();
|
||||
} else {
|
||||
jQuery('#wwa_usernameless_tip').text('');
|
||||
jQuery('#wwa_usernameless_tip').hide();
|
||||
}
|
||||
if (has_disabled_type && configs.allow_authenticator_type !== 'none') {
|
||||
if (configs.allow_authenticator_type === 'platform') {
|
||||
jQuery('#wwa_type_tip').text(php_vars.i18n_30);
|
||||
} else {
|
||||
jQuery('#wwa_type_tip').text(php_vars.i18n_31);
|
||||
}
|
||||
jQuery('#wwa_type_tip').show();
|
||||
} else {
|
||||
jQuery('#wwa_type_tip').text('');
|
||||
jQuery('#wwa_type_tip').hide();
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
jQuery('#wwa-authenticator-list').html(`<tr><td colspan="${jQuery('.wwa-usernameless-th').css('display') === 'none' ? '5' : '6'}">${php_vars.i18n_8}</td></tr>`);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** Code Base64URL into Base64
|
||||
*
|
||||
* @param {string} input Base64URL coded string
|
||||
*/
|
||||
function base64url2base64(input) {
|
||||
input = input.replace(/=/g, '').replace(/-/g, '+').replace(/_/g, '/');
|
||||
const pad = input.length % 4;
|
||||
if (pad) {
|
||||
if (pad === 1) {
|
||||
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
|
||||
}
|
||||
input += new Array(5 - pad).join('=');
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/** Code Uint8Array into Base64 string
|
||||
*
|
||||
* @param {Uint8Array} a The Uint8Array needed to be coded into Base64 string
|
||||
*/
|
||||
function arrayToBase64String(a) {
|
||||
return btoa(String.fromCharCode(...a));
|
||||
}
|
||||
|
||||
jQuery('#wwa-add-new-btn').click((e) => {
|
||||
e.preventDefault();
|
||||
jQuery('#wwa-new-block').show();
|
||||
jQuery('#wwa-verify-block').hide();
|
||||
setTimeout(() => {
|
||||
jQuery('#wwa-new-block').focus();
|
||||
}, 0);
|
||||
})
|
||||
|
||||
jQuery('#wwa-verify-btn').click((e) => {
|
||||
e.preventDefault();
|
||||
jQuery('#wwa-new-block').hide();
|
||||
jQuery('#wwa-verify-block').show();
|
||||
setTimeout(() => {
|
||||
jQuery('#wwa-verify-block').focus();
|
||||
}, 0);
|
||||
})
|
||||
|
||||
jQuery('.wwa-cancel').click((e) => {
|
||||
e.preventDefault();
|
||||
jQuery('#wwa-new-block').hide();
|
||||
jQuery('#wwa-verify-block').hide();
|
||||
})
|
||||
|
||||
jQuery('#wwa_authenticator_name').keydown((e) => {
|
||||
if (e.keyCode === 13) {
|
||||
jQuery('#wwa-bind').trigger('click');
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Bind an authenticator
|
||||
jQuery('#wwa-bind').click((e) => {
|
||||
e.preventDefault();
|
||||
if (jQuery('#wwa_authenticator_name').val() === '') {
|
||||
alert(php_vars.i18n_7);
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable inputs to avoid changing in process
|
||||
jQuery('#wwa-show-progress').html(php_vars.i18n_1);
|
||||
jQuery('#wwa-bind').attr('disabled', 'disabled');
|
||||
jQuery('#wwa_authenticator_name').attr('disabled', 'disabled');
|
||||
jQuery('.wwa_authenticator_usernameless').attr('disabled', 'disabled');
|
||||
jQuery('#wwa_authenticator_type').attr('disabled', 'disabled');
|
||||
jQuery.ajax({
|
||||
url: php_vars.ajax_url,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'wwa_create',
|
||||
name: jQuery('#wwa_authenticator_name').val(),
|
||||
type: jQuery('#wwa_authenticator_type').val(),
|
||||
usernameless: jQuery('.wwa_authenticator_usernameless:checked').val() ? jQuery('.wwa_authenticator_usernameless:checked').val() : 'false',
|
||||
user_id: php_vars.user_id
|
||||
},
|
||||
success: function (data) {
|
||||
if (typeof data === 'string') {
|
||||
console.warn(data);
|
||||
jQuery('#wwa-show-progress').html(`${php_vars.i18n_4}: ${data}`);
|
||||
jQuery('#wwa-bind').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_name').removeAttr('disabled');
|
||||
jQuery('.wwa_authenticator_usernameless').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_type').removeAttr('disabled');
|
||||
updateList();
|
||||
return;
|
||||
}
|
||||
// Get the args, code string into Uint8Array
|
||||
jQuery('#wwa-show-progress').text(php_vars.i18n_2);
|
||||
let challenge = new Uint8Array(32);
|
||||
let user_id = new Uint8Array(32);
|
||||
challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) => c.charCodeAt(0));
|
||||
user_id = Uint8Array.from(window.atob(base64url2base64(data.user.id)), (c) => c.charCodeAt(0));
|
||||
|
||||
let public_key = {
|
||||
challenge: challenge,
|
||||
rp: {
|
||||
id: data.rp.id,
|
||||
name: data.rp.name
|
||||
},
|
||||
user: {
|
||||
id: user_id,
|
||||
name: data.user.name,
|
||||
displayName: data.user.displayName
|
||||
},
|
||||
pubKeyCredParams: data.pubKeyCredParams,
|
||||
authenticatorSelection: data.authenticatorSelection,
|
||||
timeout: data.timeout
|
||||
}
|
||||
|
||||
// If some authenticators are already registered, exclude
|
||||
if (data.excludeCredentials) {
|
||||
public_key.excludeCredentials = data.excludeCredentials.map((item) => {
|
||||
item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0));
|
||||
return item;
|
||||
})
|
||||
}
|
||||
|
||||
// Save client ID
|
||||
const clientID = data.clientID;
|
||||
delete data.clientID;
|
||||
|
||||
// Create, a pop-up window should appear
|
||||
navigator.credentials.create({ 'publicKey': public_key }).then((newCredentialInfo) => {
|
||||
jQuery('#wwa-show-progress').html(php_vars.i18n_6);
|
||||
return newCredentialInfo;
|
||||
}).then((data) => {
|
||||
// Code Uint8Array into string for transmission
|
||||
const publicKeyCredential = {
|
||||
id: data.id,
|
||||
type: data.type,
|
||||
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
|
||||
response: {
|
||||
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
|
||||
attestationObject: arrayToBase64String(new Uint8Array(data.response.attestationObject))
|
||||
}
|
||||
};
|
||||
return publicKeyCredential;
|
||||
}).then(JSON.stringify).then((AuthenticatorAttestationResponse) => {
|
||||
// Send attestation back to RP
|
||||
jQuery.ajax({
|
||||
url: `${php_vars.ajax_url}?action=wwa_create_response`,
|
||||
type: 'POST',
|
||||
data: {
|
||||
data: window.btoa(AuthenticatorAttestationResponse),
|
||||
name: jQuery('#wwa_authenticator_name').val(),
|
||||
type: jQuery('#wwa_authenticator_type').val(),
|
||||
usernameless: jQuery('.wwa_authenticator_usernameless:checked').val() ? jQuery('.wwa_authenticator_usernameless:checked').val() : 'false',
|
||||
clientid: clientID,
|
||||
user_id: php_vars.user_id
|
||||
},
|
||||
success: function (data) {
|
||||
if (data === 'true') {
|
||||
// Registered
|
||||
jQuery('#wwa-show-progress').html(php_vars.i18n_3);
|
||||
jQuery('#wwa-bind').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_name').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_name').val('');
|
||||
jQuery('.wwa_authenticator_usernameless').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_type').removeAttr('disabled');
|
||||
updateList();
|
||||
} else {
|
||||
// Register failed
|
||||
jQuery('#wwa-show-progress').html(php_vars.i18n_4);
|
||||
jQuery('#wwa-bind').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_name').removeAttr('disabled');
|
||||
jQuery('.wwa_authenticator_usernameless').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_type').removeAttr('disabled');
|
||||
updateList();
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
jQuery('#wwa-show-progress').html(php_vars.i18n_4);
|
||||
jQuery('#wwa-bind').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_name').removeAttr('disabled');
|
||||
jQuery('.wwa_authenticator_usernameless').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_type').removeAttr('disabled');
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
}).catch((error) => {
|
||||
// Creation abort
|
||||
console.warn(error);
|
||||
jQuery('#wwa-show-progress').html(`${php_vars.i18n_4}: ${error}`);
|
||||
jQuery('#wwa-bind').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_name').removeAttr('disabled');
|
||||
jQuery('.wwa_authenticator_usernameless').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_type').removeAttr('disabled');
|
||||
updateList();
|
||||
})
|
||||
},
|
||||
error: function () {
|
||||
jQuery('#wwa-show-progress').html(php_vars.i18n_4);
|
||||
jQuery('#wwa-bind').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_name').removeAttr('disabled');
|
||||
jQuery('.wwa_authenticator_usernameless').removeAttr('disabled');
|
||||
jQuery('#wwa_authenticator_type').removeAttr('disabled');
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Test WebAuthn
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').click((e) => {
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').attr('disabled', 'disabled');
|
||||
let button_id = e.target.id;
|
||||
let usernameless = 'false';
|
||||
let tip_id = '#wwa-show-test';
|
||||
if (button_id === 'wwa-test_usernameless') {
|
||||
usernameless = 'true';
|
||||
tip_id = '#wwa-show-test-usernameless';
|
||||
}
|
||||
jQuery(tip_id).text(php_vars.i18n_1);
|
||||
jQuery.ajax({
|
||||
url: php_vars.ajax_url,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'wwa_auth_start',
|
||||
type: 'test',
|
||||
usernameless: usernameless,
|
||||
user_id: php_vars.user_id
|
||||
},
|
||||
success: function (data) {
|
||||
if (typeof data === 'string') {
|
||||
console.warn(data);
|
||||
jQuery(tip_id).html(`${php_vars.i18n_15}: ${data}`);
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').removeAttr('disabled');
|
||||
return;
|
||||
}
|
||||
if (data === 'User not inited.') {
|
||||
jQuery(tip_id).html(`${php_vars.i18n_15}: ${php_vars.i18n_17}`);
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').removeAttr('disabled');
|
||||
return;
|
||||
}
|
||||
jQuery(tip_id).text(php_vars.i18n_13);
|
||||
data.challenge = Uint8Array.from(window.atob(base64url2base64(data.challenge)), (c) => c.charCodeAt(0));
|
||||
|
||||
if (data.allowCredentials) {
|
||||
data.allowCredentials = data.allowCredentials.map((item) => {
|
||||
item.id = Uint8Array.from(window.atob(base64url2base64(item.id)), (c) => c.charCodeAt(0));
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
if (data.allowCredentials && configs.allow_authenticator_type && configs.allow_authenticator_type !== 'none') {
|
||||
for (let credential of data.allowCredentials) {
|
||||
if (configs.allow_authenticator_type === 'cross-platform') {
|
||||
credential.transports = ['usb', 'nfc', 'ble'];
|
||||
} else if (configs.allow_authenticator_type === 'platform') {
|
||||
credential.transports = ['internal'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save client ID
|
||||
const clientID = data.clientID;
|
||||
delete data.clientID;
|
||||
|
||||
navigator.credentials.get({ 'publicKey': data }).then((credentialInfo) => {
|
||||
jQuery(tip_id).html(php_vars.i18n_14);
|
||||
return credentialInfo;
|
||||
}).then((data) => {
|
||||
const publicKeyCredential = {
|
||||
id: data.id,
|
||||
type: data.type,
|
||||
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
|
||||
response: {
|
||||
authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
|
||||
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
|
||||
signature: arrayToBase64String(new Uint8Array(data.response.signature)),
|
||||
userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
|
||||
}
|
||||
};
|
||||
return publicKeyCredential;
|
||||
}).then(JSON.stringify).then((AuthenticatorResponse) => {
|
||||
jQuery.ajax({
|
||||
url: `${php_vars.ajax_url}?action=wwa_auth`,
|
||||
type: 'POST',
|
||||
data: {
|
||||
data: window.btoa(AuthenticatorResponse),
|
||||
type: 'test',
|
||||
remember: 'false',
|
||||
clientid: clientID,
|
||||
user_id: php_vars.user_id
|
||||
},
|
||||
success: function (data) {
|
||||
if (data === 'true') {
|
||||
jQuery(tip_id).html(php_vars.i18n_16);
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').removeAttr('disabled');
|
||||
updateList();
|
||||
} else {
|
||||
jQuery(tip_id).html(php_vars.i18n_15);
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').removeAttr('disabled');
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
jQuery(tip_id).html(php_vars.i18n_15);
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').removeAttr('disabled');
|
||||
}
|
||||
})
|
||||
}).catch((error) => {
|
||||
console.warn(error);
|
||||
jQuery(tip_id).html(`${php_vars.i18n_15}: ${error}`);
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').removeAttr('disabled');
|
||||
})
|
||||
},
|
||||
error: function () {
|
||||
jQuery(tip_id).html(php_vars.i18n_15);
|
||||
jQuery('#wwa-test, #wwa-test_usernameless').removeAttr('disabled');
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* Rename an authenticator
|
||||
* @param {string} id Authenticator ID
|
||||
* @param {string} name Current authenticator name
|
||||
*/
|
||||
function renameAuthenticator(id, name) {
|
||||
let new_name = prompt(php_vars.i18n_21, name);
|
||||
if (new_name === '') {
|
||||
alert(php_vars.i18n_7);
|
||||
} else if (new_name !== null && new_name !== name) {
|
||||
jQuery(`#${id}`).text(php_vars.i18n_22)
|
||||
jQuery.ajax({
|
||||
url: php_vars.ajax_url,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'wwa_modify_authenticator',
|
||||
id: id,
|
||||
name: new_name,
|
||||
target: 'rename',
|
||||
user_id: php_vars.user_id
|
||||
},
|
||||
success: function () {
|
||||
updateList();
|
||||
},
|
||||
error: function (data) {
|
||||
alert(`Error: ${data}`);
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an authenticator
|
||||
* @param {string} id Authenticator ID
|
||||
* @param {string} name Authenticator name
|
||||
*/
|
||||
function removeAuthenticator(id, name) {
|
||||
if (confirm(php_vars.i18n_18 + name + (jQuery('#wwa-authenticator-list > tr').length === 1 ? '\n' + php_vars.i18n_28 : ''))) {
|
||||
jQuery(`#${id}`).text(php_vars.i18n_19)
|
||||
jQuery.ajax({
|
||||
url: php_vars.ajax_url,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'wwa_modify_authenticator',
|
||||
id: id,
|
||||
target: 'remove',
|
||||
user_id: php_vars.user_id
|
||||
},
|
||||
success: function () {
|
||||
updateList();
|
||||
},
|
||||
error: function (data) {
|
||||
alert(`Error: ${data}`);
|
||||
updateList();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user