diff --git a/devices.html b/devices.html index fdd18d2..25f5d87 100644 --- a/devices.html +++ b/devices.html @@ -46,7 +46,6 @@ - @@ -169,27 +168,25 @@ window.location.href = "login.html"; } } - $('#user_id').html(user_id); + document.getElementById('user_id').textContent = user_id; listDevices(); } async function setSessionStatus(){ const sessionOk = await validSession(); + const sessionStatusEl = document.getElementById('session_status'); if(sessionOk){ - $('#session_status').html(getI18NText(i18n_messages, 'msg_session_status_ok')) + sessionStatusEl.textContent = getI18NText(i18n_messages, 'msg_session_status_ok'); }else{ - $('#session_status').html(getI18NText(i18n_messages, 'msg_session_status_fail')) + sessionStatusEl.textContent = getI18NText(i18n_messages, 'msg_session_status_fail'); } } async function addDevice(display_name){ - //You can change the display name or ask the user to input it, if the display_name is null. const result = await registerFido2(user_id, display_name?display_name:'dis_'+user_id); if(result.status === 'ok'){ - //You can try to insert user_id into your user db now if this user_id does not exist in your db. - - alert($('#msg_register_ok').html()); + alert(document.getElementById('msg_register_ok').textContent); listDevices(); }else{ errProcessFido2(result) @@ -213,17 +210,17 @@ async function listDevices(){ const result = await listUserDevicesFido2() - $("#devices_list").html(""); + const devicesListEl = document.getElementById('devices_list'); + devicesListEl.innerHTML = ""; if("ok" === result.status){ - let lst_html = "" for(let dev of result.devices){ var dev_desc = dev.desc if(!dev_desc || 0 === dev_desc.length){ - let parser = new UAParser(dev.userAgent); - - if(parser.getOS().name){ - dev_desc = parser.getDevice().model + ',' + parser.getOS().name + ',' + parser.getBrowser().name; + const parser = new UAParser(dev.userAgent); + const osName = parser.getOS().name; + if(osName){ + dev_desc = parser.getDevice().model + ',' + osName + ',' + parser.getBrowser().name; }else{ dev_desc = dev.userAgent; } @@ -231,14 +228,35 @@ var date = new Date(dev.registered_time); - lst_html += ''+ dev_desc +''+ - date.toLocaleString()+''+ - ""+ - ' '+ getI18NText(i18n_messages, 'title_del') +'' + const row = document.createElement('tr'); + + const tdDevice = document.createElement('td'); + const strong = document.createElement('strong'); + strong.textContent = dev_desc; + tdDevice.appendChild(strong); + + const tdTime = document.createElement('td'); + tdTime.textContent = date.toLocaleString(); + + const tdAction = document.createElement('td'); + const link = document.createElement('a'); + link.className = 'dropdown-item'; + link.href = "javascript:delDevice('" + dev.device_id + "');"; + const icon = document.createElement('i'); + icon.className = 'bx bx-trash me-1'; + const span = document.createElement('span'); + span.textContent = ' ' + getI18NText(i18n_messages, 'title_del'); + link.appendChild(icon); + link.appendChild(span); + tdAction.appendChild(link); + + row.appendChild(tdDevice); + row.appendChild(tdTime); + row.appendChild(tdAction); + devicesListEl.appendChild(row); } - $("#devices_list").html(lst_html); }else{ - const msg = getI18NErrorMessage(result.errorMessage); //fido2LibErrMsgLanguages.japanese + const msg = getI18NErrorMessage(result.errorMessage); alert(msg?msg:result.errorMessage); } } diff --git a/files/amipro_utils.js b/files/amipro_utils.js index e9da7c9..af11a41 100644 --- a/files/amipro_utils.js +++ b/files/amipro_utils.js @@ -1,15 +1,15 @@ /** - * amiPro utils + * amiPro utils - jQuery-free version */ 'use strict'; function setI18NText(i18n_map){ for (const key of i18n_map.keys()) { - const elm = $("#"+key); + const elm = document.getElementById(key); if(elm){ const lang = window.navigator.language; - var elem = i18n_map.get(key) + var elem = i18n_map.get(key) var msg = null if(elem){ msg = elem.get(lang) @@ -17,14 +17,14 @@ function setI18NText(i18n_map){ } if(!msg)msg = key+"-"+lang - $("#"+key).html(msg); + elm.textContent = msg; } } } function getI18NText(i18n_map, key){ const lang = window.navigator.language; - var elem = i18n_map.get(key) + var elem = i18n_map.get(key) var msg = null if(elem){ msg = elem.get(lang) diff --git a/files/dfido2-lib.js b/files/dfido2-lib.js index d48598b..e1cb5e2 100644 --- a/files/dfido2-lib.js +++ b/files/dfido2-lib.js @@ -2,6 +2,8 @@ const DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION = 'fido2_user_session' const DFIDO2_LIB_LOCALSTG_NAME_REGISTERED = 'dfido2_lib_registered' const DFIDO2_LIB_LOCALSTG_NAME_SVR_URL = 'dfido2_lib_svr_url' +let configuredServerUrl = null; + /** ===APIs=== */ if(!localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)){ @@ -9,9 +11,25 @@ if(!localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)){ } function setFidoServerURL(url){ + if (!url || !url.startsWith('https://')) { + throw new Error('serverUrl must be a valid HTTPS URL'); + } + configuredServerUrl = url; localStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL, url); } +function getServerUrl() { + if (configuredServerUrl) { + return configuredServerUrl; + } + const stored = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL); + if (stored) { + configuredServerUrl = stored; + return stored; + } + return 'https://fido2.amipro.me'; +} + function canTryAutoAuthentication(){ //const session_text = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION) //alert('canTryAuth:'+session_text+"|"+(null != localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED))) @@ -67,7 +85,7 @@ async function listUserDevicesFido2(rpId = null) { req.rp = { id: rpId }; } - const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/dvc/lst", { + const response = await fetch(getServerUrl() + "/usr/dvc/lst", { method: "POST", cache: "no-cache", headers: { @@ -104,7 +122,7 @@ async function delUserDeviceFido2(device_id, rpId = null) { req.rp = { id: rpId }; } - const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/dvc/rm", { + const response = await fetch(getServerUrl() + "/usr/dvc/rm", { method: "POST", cache: "no-cache", headers: { @@ -154,7 +172,7 @@ async function validSession(rpId = null) { req.rp = { id: rpId }; } - const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/validsession", { + const response = await fetch(getServerUrl() + "/usr/validsession", { method: "POST", cache: "no-cache", headers: { @@ -177,7 +195,7 @@ async function logoutFido2UserSession(){ const session_data = JSON.parse(session_text) let req = {session: session_data['session'], username: session_data['uid']} - const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/delsession", { + const response = await fetch(getServerUrl() + "/usr/delsession", { method: "POST", cache: "no-cache", headers: { @@ -192,7 +210,7 @@ async function logoutFido2UserSession(){ async function getRegistrationUser(reg_session_id){ try { let req = {session_id: reg_session_id} - const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/reg/username", { + const response = await fetch(getServerUrl() + "/reg/username", { method: "POST", cache: "no-cache", headers: { @@ -410,7 +428,7 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr attestationOptions.rp = { id: rpId } } - const svrUrl = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + const svrUrl = getServerUrl() const response = await fetch(svrUrl + "/attestation/options", { method: "POST", cache: "no-cache", @@ -445,7 +463,7 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr attResult.transports = res.response.getTransports(); } - const result = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/attestation/result", { + const result = await fetch(getServerUrl() + "/attestation/result", { method: "POST", cache: "no-cache", headers: { @@ -524,7 +542,7 @@ async function doAssertion(username = null, rpId = null, userVerification = 'pre authnOptions.rp = { id: rpId }; } - const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/assertion/options", { + const response = await fetch(getServerUrl() + "/assertion/options", { method: "POST", cache: "no-cache", headers: { @@ -566,7 +584,7 @@ async function doAssertion(username = null, rpId = null, userVerification = 'pre userHandle: _toBase64URL(btoa(_bufferToString(cred.response.userHandle))) //_toBase64URL(btoa(_bufferToString(cred.response.userHandle))) } }; - const res = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/assertion/result", { + const res = await fetch(getServerUrl() + "/assertion/result", { method: "POST", cache: "no-cache", headers: { diff --git a/files/fido2-ui-sdk.js b/files/fido2-ui-sdk.js index 3affe6e..42cf6d2 100644 --- a/files/fido2-ui-sdk.js +++ b/files/fido2-ui-sdk.js @@ -1,6 +1,19 @@ (function(window) { 'use strict'; + // Check required dependencies + if (typeof authenticateFido2 === 'undefined' || + typeof registerFido2 === 'undefined' || + typeof listUserDevicesFido2 === 'undefined' || + typeof delUserDeviceFido2 === 'undefined' || + typeof setFidoServerURL === 'undefined' || + typeof logoutFido2UserSession === 'undefined') { + throw new Error( + 'FIDO2 UI SDK requires dfido2-lib.js to be loaded first. ' + + 'Please add: before fido2-ui-sdk.js' + ); + } + const FIDO2_UI_VERSION = '1.0.0'; const DEFAULT_CONFIG = { @@ -459,8 +472,8 @@ showRemainingAttempts: true, }, callbacks: { - onFido2Success: null, - onFido2Error: null, + onLoginSuccess: null, + onLoginError: null, onPasswordLogin: null, onPasswordExhausted: null, onLoginClosed: null, @@ -513,18 +526,30 @@ container.innerHTML = ''; + const uniqueId = Date.now() + '_' + Math.random().toString(36).substr(2, 9); + const modalId = 'fido2LoginModal_' + uniqueId; + const titleId = 'fido2LoginModalTitle_' + uniqueId; + const errorId = 'fido2LoginError_' + uniqueId; + const hintId = 'fido2LoginHint_' + uniqueId; + const userIdId = 'fido2UserId_' + uniqueId; + const passwordId = 'fido2Password_' + uniqueId; + const passwordSectionId = 'fido2PasswordSection_' + uniqueId; + const mainBtnId = 'fido2MainBtn_' + uniqueId; + const toggleLinkId = 'fido2ToggleModeLink_' + uniqueId; + const modal = document.createElement('div'); modal.className = 'modal fade fido2-sdk-login-modal'; - modal.id = 'fido2LoginModal'; + modal.id = modalId; modal.tabIndex = -1; modal.setAttribute('aria-hidden', 'true'); - modal.innerHTML = this._getModalHTML(); + modal.innerHTML = this._getModalHTML(uniqueId, titleId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId); container.appendChild(modal); this.modalElement = modal; this.containerElement = container; + this._uniqueId = uniqueId; - this._debugLog('[Fido2Login] Modal element created'); + this._debugLog('[Fido2Login] Modal element created with ID:', modalId); this.themeManager.applyTheme(modal); @@ -545,7 +570,7 @@ return modal; }; - Fido2Login.prototype._getModalHTML = function() { + Fido2Login.prototype._getModalHTML = function(uniqueId, titleId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId) { const theme = this.config.theme; const features = this.config.features; @@ -554,45 +579,45 @@ `; }; - Fido2Login.prototype._getBodyHTML = function() { + Fido2Login.prototype._getBodyHTML = function(uniqueId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId) { const features = this.config.features; const theme = this.config.theme; let html = ''; html += ` - - + + `; html += `
- - + +
`; html += ` -