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 += '
- ${this._getBodyHTML()}
+ ${this._getBodyHTML(uniqueId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId)}
`;
};
- 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 += `
-
@@ -603,7 +628,7 @@
if (features.enablePasswordLogin) {
html += `
`;
}
@@ -612,7 +637,7 @@
const remaining = this.maxAttempts - this.attemptCount;
html += `
-
+
${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}
@@ -627,7 +652,7 @@
const container = this.modalElement || this.containerElement;
if (!container) return;
- const mainBtn = container.querySelector('#fido2MainBtn');
+ const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
if (mainBtn) {
mainBtn.addEventListener('click', () => {
if (self.mode === LoginMode.FIDO2) {
@@ -638,15 +663,15 @@
});
}
- const toggleLink = container.querySelector('#fido2ToggleModeLink');
+ const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
if (toggleLink) {
toggleLink.addEventListener('click', () => {
- this._debugLog('[Fido2Login] Toggle link clicked, current mode:', this.mode);
+ this._debugLog('[Fido2Login] Toggle link clicked, current mode:', self.mode);
self._toggleMode();
});
}
- const userIdInput = container.querySelector('#fido2UserId');
+ const userIdInput = container.querySelector('[id^="fido2UserId_"]');
if (userIdInput) {
userIdInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
@@ -659,7 +684,7 @@
});
}
- const passwordInput = container.querySelector('#fido2Password');
+ const passwordInput = container.querySelector('[id^="fido2Password_"]');
if (passwordInput) {
passwordInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
@@ -683,10 +708,15 @@
this._stopLoading();
if (result.status === 'ok') {
this.state = LoginState.CLOSED;
- this._emit('fido2Success', result.username, result.session);
+ this._emit('fido2Success', {
+ method: 'fido2',
+ username: result.username,
+ session: result.session
+ });
this._closeUI();
} else {
this._emit('fido2Error', {
+ method: 'fido2',
code: 'AUTH_FAILED',
message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
originalError: result
@@ -696,12 +726,14 @@
this._stopLoading();
if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
this._emit('fido2Error', {
+ method: 'fido2',
code: 'CANCELED',
message: this.i18n.getText('msg_fido2_canceled'),
originalError: error
});
} else {
this._emit('fido2Error', {
+ method: 'fido2',
code: 'AUTH_FAILED',
message: error.message || this.i18n.getText('msg_fido2_failed'),
originalError: error
@@ -727,6 +759,11 @@
this._hideError();
this._startLoading();
+ // Clear any existing FIDO2 session before password login
+ if (typeof logoutFido2UserSession === 'function') {
+ logoutFido2UserSession();
+ }
+
const passwordCallback = this.config.callbacks.onPasswordLogin;
if (typeof passwordCallback !== 'function') {
this._stopLoading();
@@ -738,7 +775,11 @@
this._stopLoading();
if (success) {
this.state = LoginState.CLOSED;
- this._emit('fido2Success', userId, null);
+ this._emit('fido2Success', {
+ method: 'password',
+ username: userId,
+ session: null
+ });
this._closeUI();
} else {
this.attemptCount++;
@@ -769,11 +810,11 @@
const container = this.modalElement || this.containerElement;
if (!container) return;
- const passwordSection = container.querySelector('#fido2PasswordSection');
- const mainBtn = container.querySelector('#fido2MainBtn');
- const toggleLink = container.querySelector('#fido2ToggleModeLink');
- const titleEl = document.getElementById('fido2LoginModalTitle');
- const remainingEl = container.querySelector('#fido2RemainingAttempts');
+ const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
+ const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
+ const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
+ const titleEl = container.querySelector('[id^="fido2LoginModalTitle_"]');
+ const remainingEl = container.querySelector('[id^="fido2RemainingAttempts_"]');
if (this.mode === LoginMode.FIDO2) {
this.mode = LoginMode.PASSWORD;
@@ -789,7 +830,7 @@
} else if (this.config.features.showRemainingAttempts) {
const remainingContainer = document.createElement('div');
remainingContainer.className = 'text-center mt-3';
- remainingContainer.innerHTML = `
${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}`;
+ remainingContainer.innerHTML = `
${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}`;
toggleLink.parentNode.parentNode.insertBefore(remainingContainer, toggleLink.parentNode.nextSibling);
}
} else {
@@ -808,18 +849,21 @@
};
Fido2Login.prototype._getUserIdInput = function() {
- const input = document.getElementById('fido2UserId');
+ const container = this.modalElement || this.containerElement;
+ const input = container ? container.querySelector('[id^="fido2UserId_"]') : null;
return input ? input.value.trim() : '';
};
Fido2Login.prototype._getPasswordInput = function() {
- const input = document.getElementById('fido2Password');
+ const container = this.modalElement || this.containerElement;
+ const input = container ? container.querySelector('[id^="fido2Password_"]') : null;
return input ? input.value : '';
};
Fido2Login.prototype._showError = function(message) {
- const errorEl = document.getElementById('fido2LoginError');
- const hintEl = document.getElementById('fido2LoginHint');
+ const container = this.modalElement || this.containerElement;
+ const errorEl = container ? container.querySelector('[id^="fido2LoginError_"]') : null;
+ const hintEl = container ? container.querySelector('[id^="fido2LoginHint_"]') : null;
if (hintEl) hintEl.style.display = 'none';
if (errorEl) {
errorEl.textContent = message;
@@ -828,12 +872,14 @@
};
Fido2Login.prototype._hideError = function() {
- const errorEl = document.getElementById('fido2LoginError');
+ const container = this.modalElement || this.containerElement;
+ const errorEl = container ? container.querySelector('[id^="fido2LoginError_"]') : null;
if (errorEl) errorEl.style.display = 'none';
};
Fido2Login.prototype._showHint = function(message) {
- const hintEl = document.getElementById('fido2LoginHint');
+ const container = this.modalElement || this.containerElement;
+ const hintEl = container ? container.querySelector('[id^="fido2LoginHint_"]') : null;
if (hintEl) {
hintEl.textContent = message;
hintEl.style.display = 'block';
@@ -841,12 +887,14 @@
};
Fido2Login.prototype._hideHint = function() {
- const hintEl = document.getElementById('fido2LoginHint');
+ const container = this.modalElement || this.containerElement;
+ const hintEl = container ? container.querySelector('[id^="fido2LoginHint_"]') : null;
if (hintEl) hintEl.style.display = 'none';
};
Fido2Login.prototype._startLoading = function() {
- const mainBtn = document.getElementById('fido2MainBtn');
+ const container = this.modalElement || this.containerElement;
+ const mainBtn = container ? container.querySelector('[id^="fido2MainBtn_"]') : null;
if (mainBtn) {
mainBtn.disabled = true;
mainBtn.dataset.originalText = mainBtn.textContent;
@@ -855,7 +903,8 @@
};
Fido2Login.prototype._stopLoading = function() {
- const mainBtn = document.getElementById('fido2MainBtn');
+ const container = this.modalElement || this.containerElement;
+ const mainBtn = container ? container.querySelector('[id^="fido2MainBtn_"]') : null;
if (mainBtn) {
mainBtn.disabled = false;
mainBtn.textContent = mainBtn.dataset.originalText ||
@@ -867,17 +916,24 @@
const container = this.modalElement || this.containerElement;
if (!container) return;
- const remainingEl = container.querySelector('#fido2RemainingAttempts');
+ const remainingEl = container.querySelector('[id^="fido2RemainingAttempts_"]');
if (remainingEl) {
const remaining = this.maxAttempts - this.attemptCount;
remainingEl.textContent = this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining);
}
};
- Fido2Login.prototype._emit = function(event, ...args) {
- const callback = this.config.callbacks['on' + event.charAt(0).toUpperCase() + event.slice(1)];
+ Fido2Login.prototype._emit = function(event, data) {
+ const callbackName = 'on' + event.charAt(0).toUpperCase() + event.slice(1);
+ const callback = this.config.callbacks[callbackName];
if (typeof callback === 'function') {
- callback.apply(this, args);
+ if (event === 'fido2Success') {
+ callback(data.username, data.session, data);
+ } else if (event === 'fido2Error') {
+ callback(data, data.originalError);
+ } else {
+ callback(data);
+ }
}
};
@@ -934,11 +990,16 @@
this._hideHint();
if (result.status === 'ok') {
this.state = LoginState.CLOSED;
- this._emit('fido2Success', result.username, result.session);
+ this._emit('fido2Success', {
+ method: 'fido2',
+ username: result.username,
+ session: result.session
+ });
this._closeUI();
} else {
this.state = LoginState.FIDO2;
this._emit('fido2Error', {
+ method: 'fido2',
code: 'AUTH_FAILED',
message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
originalError: result
@@ -951,6 +1012,7 @@
if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
this.state = LoginState.FIDO2;
this._emit('fido2Error', {
+ method: 'fido2',
code: 'CANCELED',
message: this.i18n.getText('msg_fido2_canceled'),
originalError: error
@@ -958,6 +1020,7 @@
} else {
this.state = LoginState.FIDO2;
this._emit('fido2Error', {
+ method: 'fido2',
code: 'AUTH_FAILED',
message: error.message || this.i18n.getText('msg_fido2_failed'),
originalError: error
@@ -974,6 +1037,7 @@
} else {
this.state = LoginState.FIDO2;
this._emit('fido2Error', {
+ method: 'fido2',
code: 'NO_REGISTRATION',
message: this.i18n.getText('msg_no_registration'),
originalError: null
@@ -1009,9 +1073,9 @@
const container = this.modalElement || this.containerElement;
if (!container) return;
- const passwordSection = container.querySelector('#fido2PasswordSection');
- const mainBtn = container.querySelector('#fido2MainBtn');
- const toggleLink = container.querySelector('#fido2ToggleModeLink');
+ const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
+ const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
+ const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
if (passwordSection) passwordSection.style.display = 'none';
if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_fido2_login');
@@ -1027,10 +1091,10 @@
const container = this.modalElement || this.containerElement;
if (!container) return;
- const passwordSection = container.querySelector('#fido2PasswordSection');
- const mainBtn = container.querySelector('#fido2MainBtn');
- const toggleLink = container.querySelector('#fido2ToggleModeLink');
- const titleEl = document.getElementById('fido2LoginModalTitle');
+ const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
+ const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
+ const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
+ const titleEl = container.querySelector('[id^="fido2LoginModalTitle_"]');
if (passwordSection) passwordSection.style.display = 'block';
if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_password_login');
@@ -1285,6 +1349,7 @@
const result = await registerFido2(effectiveUserId, displayName || 'Device-' + effectiveUserId, this.config.rpId);
if (result.status === 'ok') {
+ // registerFido2 automatically creates/updates session in sessionStorage
await this.loadDevices();
this.eventManager.emit('deviceAdded', result);
return result;
@@ -1330,25 +1395,11 @@
DeviceManager.prototype.checkSession = async function() {
try {
- const sessionUserId = this.getSessionUserId();
-
- if (!sessionUserId) {
- this.sessionStatus = false;
- this.eventManager.emit('sessionStatusChanged', false);
- return false;
- }
-
- const validation = this.validateUserId();
- if (!validation.valid) {
- this.sessionStatus = false;
- this.eventManager.emit('sessionStatusChanged', false);
- this.eventManager.emit('userMismatch', validation.error);
- return false;
- }
-
- this.sessionStatus = true;
- this.eventManager.emit('sessionStatusChanged', true);
- return true;
+ // Simply validate session with server, same as devices.html does
+ const sessionOk = await validSession(this.config.rpId);
+ this.sessionStatus = !!sessionOk;
+ this.eventManager.emit('sessionStatusChanged', this.sessionStatus);
+ return this.sessionStatus;
} catch (error) {
this.sessionStatus = false;
this.eventManager.emit('sessionStatusChanged', false);
@@ -1420,16 +1471,23 @@
container.innerHTML = '';
+ const uniqueId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
+ const modalId = 'fido2SdkModal_' + uniqueId;
+ const sessionStatusId = 'fido2SessionStatus_' + uniqueId;
+ const addBtnId = 'fido2AddDeviceBtn_' + uniqueId;
+ const devicesListId = 'fido2DevicesList_' + uniqueId;
+
const modal = document.createElement('div');
modal.className = 'modal fade fido2-sdk-modal';
- modal.id = 'fido2SdkModal';
+ modal.id = modalId;
modal.tabIndex = -1;
modal.setAttribute('aria-hidden', 'true');
- modal.innerHTML = this._getModalHTML();
+ modal.innerHTML = this._getModalHTML(uniqueId, sessionStatusId, addBtnId, devicesListId);
container.appendChild(modal);
this.modalElement = modal;
this.containerElement = container;
+ this._uniqueId = uniqueId;
this.themeManager.applyTheme(modal);
@@ -1445,12 +1503,12 @@
this.cleanup();
});
- this._bindEvents();
+ this._bindEvents(uniqueId, addBtnId, devicesListId);
return modal;
};
- UIRenderer.prototype._getModalHTML = function() {
+ UIRenderer.prototype._getModalHTML = function(uniqueId, sessionStatusId, addBtnId, devicesListId) {
const theme = this.config.theme;
return `
@@ -1459,11 +1517,11 @@
- ${this._getBodyHTML()}
+ ${this._getBodyHTML(uniqueId, addBtnId, devicesListId)}