Compare commits
4 Commits
9bbbae8dd2
...
feature/RE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bd3ca0e2b | ||
|
|
9218662a4b | ||
| 89ea8a48fc | |||
|
|
055d59f979 |
62
devices.html
62
devices.html
@@ -46,7 +46,6 @@
|
|||||||
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
|
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
|
||||||
<script src="files/config.js"></script>
|
<script src="files/config.js"></script>
|
||||||
|
|
||||||
<script src="files/jquery.js"></script>
|
|
||||||
<script src="files/popper.js"></script>
|
<script src="files/popper.js"></script>
|
||||||
<script src="files/bootstrap.js"></script>
|
<script src="files/bootstrap.js"></script>
|
||||||
<script src="files/perfect-scrollbar.js"></script>
|
<script src="files/perfect-scrollbar.js"></script>
|
||||||
@@ -55,8 +54,8 @@
|
|||||||
|
|
||||||
<script src="files/ua-parser.js"></script>
|
<script src="files/ua-parser.js"></script>
|
||||||
|
|
||||||
<script src="files/amipro_utils.js?v=20230401402"></script>
|
<script src="files/amipro_utils.js?v=20260118"></script>
|
||||||
<script src="files/dfido2-lib.js?v=20230918"></script>
|
<script src="files/dfido2-lib.js?v=20260118"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
@@ -169,27 +168,25 @@
|
|||||||
window.location.href = "login.html";
|
window.location.href = "login.html";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$('#user_id').html(user_id);
|
document.getElementById('user_id').textContent = user_id;
|
||||||
listDevices();
|
listDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setSessionStatus(){
|
async function setSessionStatus(){
|
||||||
const sessionOk = await validSession();
|
const sessionOk = await validSession();
|
||||||
|
const sessionStatusEl = document.getElementById('session_status');
|
||||||
if(sessionOk){
|
if(sessionOk){
|
||||||
$('#session_status').html(getI18NText(i18n_messages, 'msg_session_status_ok'))
|
sessionStatusEl.textContent = getI18NText(i18n_messages, 'msg_session_status_ok');
|
||||||
}else{
|
}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){
|
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);
|
const result = await registerFido2(user_id, display_name?display_name:'dis_'+user_id);
|
||||||
|
|
||||||
if(result.status === 'ok'){
|
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(document.getElementById('msg_register_ok').textContent);
|
||||||
|
|
||||||
alert($('#msg_register_ok').html());
|
|
||||||
listDevices();
|
listDevices();
|
||||||
}else{
|
}else{
|
||||||
errProcessFido2(result)
|
errProcessFido2(result)
|
||||||
@@ -213,17 +210,17 @@
|
|||||||
|
|
||||||
async function listDevices(){
|
async function listDevices(){
|
||||||
const result = await listUserDevicesFido2()
|
const result = await listUserDevicesFido2()
|
||||||
$("#devices_list").html("");
|
const devicesListEl = document.getElementById('devices_list');
|
||||||
|
devicesListEl.innerHTML = "";
|
||||||
if("ok" === result.status){
|
if("ok" === result.status){
|
||||||
let lst_html = ""
|
|
||||||
|
|
||||||
for(let dev of result.devices){
|
for(let dev of result.devices){
|
||||||
var dev_desc = dev.desc
|
var dev_desc = dev.desc
|
||||||
if(!dev_desc || 0 === dev_desc.length){
|
if(!dev_desc || 0 === dev_desc.length){
|
||||||
let parser = new UAParser(dev.userAgent);
|
const parser = new UAParser(dev.userAgent);
|
||||||
|
const osName = parser.getOS().name;
|
||||||
if(parser.getOS().name){
|
if(osName){
|
||||||
dev_desc = parser.getDevice().model + ',' + parser.getOS().name + ',' + parser.getBrowser().name;
|
dev_desc = parser.getDevice().model + ',' + osName + ',' + parser.getBrowser().name;
|
||||||
}else{
|
}else{
|
||||||
dev_desc = dev.userAgent;
|
dev_desc = dev.userAgent;
|
||||||
}
|
}
|
||||||
@@ -231,14 +228,35 @@
|
|||||||
|
|
||||||
var date = new Date(dev.registered_time);
|
var date = new Date(dev.registered_time);
|
||||||
|
|
||||||
lst_html += '<tr><td><strong>'+ dev_desc +'</strong></td><td>'+
|
const row = document.createElement('tr');
|
||||||
date.toLocaleString()+'</td><td>'+
|
|
||||||
"<a style=\"padding-left: 0px;\" class=\"dropdown-item\" href=\"javascript:delDevice('"+dev.device_id+"');\">"+
|
const tdDevice = document.createElement('td');
|
||||||
'<i class="bx bx-trash me-1"></i><span id="title_del"> '+ getI18NText(i18n_messages, 'title_del') +'</span></a></div></td></tr>'
|
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{
|
}else{
|
||||||
const msg = getI18NErrorMessage(result.errorMessage); //fido2LibErrMsgLanguages.japanese
|
const msg = getI18NErrorMessage(result.errorMessage);
|
||||||
alert(msg?msg:result.errorMessage);
|
alert(msg?msg:result.errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* amiPro utils
|
* amiPro utils - jQuery-free version
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function setI18NText(i18n_map){
|
function setI18NText(i18n_map){
|
||||||
for (const key of i18n_map.keys()) {
|
for (const key of i18n_map.keys()) {
|
||||||
const elm = $("#"+key);
|
const elm = document.getElementById(key);
|
||||||
if(elm){
|
if(elm){
|
||||||
const lang = window.navigator.language;
|
const lang = window.navigator.language;
|
||||||
var elem = i18n_map.get(key)
|
var elem = i18n_map.get(key)
|
||||||
@@ -17,7 +17,7 @@ function setI18NText(i18n_map){
|
|||||||
}
|
}
|
||||||
if(!msg)msg = key+"-"+lang
|
if(!msg)msg = key+"-"+lang
|
||||||
|
|
||||||
$("#"+key).html(msg);
|
elm.textContent = msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_REGISTERED = 'dfido2_lib_registered'
|
||||||
const DFIDO2_LIB_LOCALSTG_NAME_SVR_URL = 'dfido2_lib_svr_url'
|
const DFIDO2_LIB_LOCALSTG_NAME_SVR_URL = 'dfido2_lib_svr_url'
|
||||||
|
|
||||||
|
let configuredServerUrl = null;
|
||||||
|
|
||||||
/** ===APIs=== */
|
/** ===APIs=== */
|
||||||
|
|
||||||
if(!localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)){
|
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){
|
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);
|
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(){
|
function canTryAutoAuthentication(){
|
||||||
//const session_text = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
//const session_text = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
||||||
//alert('canTryAuth:'+session_text+"|"+(null != localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED)))
|
//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 };
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -104,7 +122,7 @@ async function delUserDeviceFido2(device_id, rpId = null) {
|
|||||||
req.rp = { id: rpId };
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -154,7 +172,7 @@ async function validSession(rpId = null) {
|
|||||||
req.rp = { id: rpId };
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -177,7 +195,7 @@ async function logoutFido2UserSession(){
|
|||||||
|
|
||||||
const session_data = JSON.parse(session_text)
|
const session_data = JSON.parse(session_text)
|
||||||
let req = {session: session_data['session'], username: session_data['uid']}
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -192,7 +210,7 @@ async function logoutFido2UserSession(){
|
|||||||
async function getRegistrationUser(reg_session_id){
|
async function getRegistrationUser(reg_session_id){
|
||||||
try {
|
try {
|
||||||
let req = {session_id: reg_session_id}
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -410,7 +428,7 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr
|
|||||||
attestationOptions.rp = { id: rpId }
|
attestationOptions.rp = { id: rpId }
|
||||||
}
|
}
|
||||||
|
|
||||||
const svrUrl = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)
|
const svrUrl = getServerUrl()
|
||||||
const response = await fetch(svrUrl + "/attestation/options", {
|
const response = await fetch(svrUrl + "/attestation/options", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
@@ -445,7 +463,7 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr
|
|||||||
attResult.transports = res.response.getTransports();
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -524,7 +542,7 @@ async function doAssertion(username = null, rpId = null, userVerification = 'pre
|
|||||||
authnOptions.rp = { id: rpId };
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
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)))
|
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",
|
method: "POST",
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -86,33 +86,9 @@
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: #e7f1ff;
|
background-color: #e7f1ff;
|
||||||
border-left: 4px solid #0d6efd;
|
border-left: 4px solid #0d6efd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fido2-sdk-standalone {
|
.fido2-sdk-table .text-danger {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 40px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-sdk-standalone .container {
|
|
||||||
max-width: 1000px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-sdk-standalone .card {
|
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-sdk-standalone .card-header {
|
|
||||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
||||||
border-bottom: 2px solid #dee2e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-sdk-standalone .card-body {
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-sdk-table .text-danger {
|
|
||||||
color: #dc3545 !important;
|
color: #dc3545 !important;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
@@ -134,15 +110,11 @@
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.fido2-sdk-modal .modal-dialog {
|
.fido2-sdk-modal .modal-dialog {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
max-width: calc(100% - 20px);
|
max-width: calc(100% - 20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fido2-sdk-standalone {
|
.fido2-sdk-table th,
|
||||||
padding: 20px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-sdk-table th,
|
|
||||||
.fido2-sdk-table td {
|
.fido2-sdk-table td {
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
(function(window) {
|
(function(window) {
|
||||||
'use strict';
|
'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: <script src="files/dfido2-lib.js"></script> before fido2-ui-sdk.js'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const FIDO2_UI_VERSION = '1.0.0';
|
const FIDO2_UI_VERSION = '1.0.0';
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
@@ -459,8 +472,8 @@
|
|||||||
showRemainingAttempts: true,
|
showRemainingAttempts: true,
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
onFido2Success: null,
|
onLoginSuccess: null,
|
||||||
onFido2Error: null,
|
onLoginError: null,
|
||||||
onPasswordLogin: null,
|
onPasswordLogin: null,
|
||||||
onPasswordExhausted: null,
|
onPasswordExhausted: null,
|
||||||
onLoginClosed: null,
|
onLoginClosed: null,
|
||||||
@@ -513,18 +526,30 @@
|
|||||||
|
|
||||||
container.innerHTML = '';
|
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');
|
const modal = document.createElement('div');
|
||||||
modal.className = 'modal fade fido2-sdk-login-modal';
|
modal.className = 'modal fade fido2-sdk-login-modal';
|
||||||
modal.id = 'fido2LoginModal';
|
modal.id = modalId;
|
||||||
modal.tabIndex = -1;
|
modal.tabIndex = -1;
|
||||||
modal.setAttribute('aria-hidden', 'true');
|
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);
|
container.appendChild(modal);
|
||||||
this.modalElement = modal;
|
this.modalElement = modal;
|
||||||
this.containerElement = container;
|
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);
|
this.themeManager.applyTheme(modal);
|
||||||
|
|
||||||
@@ -545,7 +570,7 @@
|
|||||||
return modal;
|
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 theme = this.config.theme;
|
||||||
const features = this.config.features;
|
const features = this.config.features;
|
||||||
|
|
||||||
@@ -554,45 +579,45 @@
|
|||||||
<div class="modal-content fido2-sdk-card">
|
<div class="modal-content fido2-sdk-card">
|
||||||
<div class="modal-header fido2-sdk-header">
|
<div class="modal-header fido2-sdk-header">
|
||||||
${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo me-2" alt="Logo">` : ''}
|
${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo me-2" alt="Logo">` : ''}
|
||||||
<h5 class="modal-title fido2-sdk-text" id="fido2LoginModalTitle">${this.i18n.getText('title_login')}</h5>
|
<h5 class="modal-title fido2-sdk-text" id="${titleId}">${this.i18n.getText('title_login')}</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body fido2-sdk-container">
|
<div class="modal-body fido2-sdk-container">
|
||||||
${this._getBodyHTML()}
|
${this._getBodyHTML(uniqueId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._getBodyHTML = function() {
|
Fido2Login.prototype._getBodyHTML = function(uniqueId, errorId, hintId, userIdId, passwordId, passwordSectionId, mainBtnId, toggleLinkId) {
|
||||||
const features = this.config.features;
|
const features = this.config.features;
|
||||||
const theme = this.config.theme;
|
const theme = this.config.theme;
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div id="fido2LoginError" class="alert alert-danger mb-3" style="display:none;"></div>
|
<div id="${errorId}" class="alert alert-danger mb-3" style="display:none;"></div>
|
||||||
<div id="fido2LoginHint" class="alert alert-info mb-3" style="display:none;"></div>
|
<div id="${hintId}" class="alert alert-info mb-3" style="display:none;"></div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="fido2UserId" class="form-label fido2-sdk-text">${this.i18n.getText('placeholder_user_id')}</label>
|
<label for="${userIdId}" class="form-label fido2-sdk-text">${this.i18n.getText('placeholder_user_id')}</label>
|
||||||
<input type="text" class="form-control" id="fido2UserId" placeholder="${this.i18n.getText('placeholder_user_id')}">
|
<input type="text" class="form-control" id="${userIdId}" placeholder="${this.i18n.getText('placeholder_user_id')}">
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="mb-3" id="fido2PasswordSection" style="display:none;">
|
<div class="mb-3" id="${passwordSectionId}" style="display:none;">
|
||||||
<label for="fido2Password" class="form-label fido2-sdk-text">${this.i18n.getText('placeholder_password')}</label>
|
<label for="${passwordId}" class="form-label fido2-sdk-text">${this.i18n.getText('placeholder_password')}</label>
|
||||||
<input type="password" class="form-control" id="fido2Password" placeholder="${this.i18n.getText('placeholder_password')}">
|
<input type="password" class="form-control" id="${passwordId}" placeholder="${this.i18n.getText('placeholder_password')}">
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="d-grid gap-2">
|
<div class="d-grid gap-2">
|
||||||
<button type="button" class="btn btn-primary fido2-sdk-btn fido2-sdk-btn-primary" id="fido2MainBtn">
|
<button type="button" class="btn btn-primary fido2-sdk-btn fido2-sdk-btn-primary" id="${mainBtnId}">
|
||||||
${this.i18n.getText('btn_fido2_login')}
|
${this.i18n.getText('btn_fido2_login')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -603,7 +628,7 @@
|
|||||||
if (features.enablePasswordLogin) {
|
if (features.enablePasswordLogin) {
|
||||||
html += `
|
html += `
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a href="javascript:void(0)" class="fido2-sdk-link" id="fido2ToggleModeLink">${this.i18n.getText('link_use_password')}</a>
|
<a href="javascript:void(0)" class="fido2-sdk-link" id="${toggleLinkId}">${this.i18n.getText('link_use_password')}</a>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -612,7 +637,7 @@
|
|||||||
const remaining = this.maxAttempts - this.attemptCount;
|
const remaining = this.maxAttempts - this.attemptCount;
|
||||||
html += `
|
html += `
|
||||||
<div class="text-center mt-3">
|
<div class="text-center mt-3">
|
||||||
<small class="text-muted fido2-sdk-text" id="fido2RemainingAttempts">
|
<small class="text-muted fido2-sdk-text" id="fido2RemainingAttempts_${uniqueId}">
|
||||||
${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}
|
${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
@@ -627,7 +652,7 @@
|
|||||||
const container = this.modalElement || this.containerElement;
|
const container = this.modalElement || this.containerElement;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const mainBtn = container.querySelector('#fido2MainBtn');
|
const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
|
||||||
if (mainBtn) {
|
if (mainBtn) {
|
||||||
mainBtn.addEventListener('click', () => {
|
mainBtn.addEventListener('click', () => {
|
||||||
if (self.mode === LoginMode.FIDO2) {
|
if (self.mode === LoginMode.FIDO2) {
|
||||||
@@ -638,15 +663,15 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleLink = container.querySelector('#fido2ToggleModeLink');
|
const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
|
||||||
if (toggleLink) {
|
if (toggleLink) {
|
||||||
toggleLink.addEventListener('click', () => {
|
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();
|
self._toggleMode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const userIdInput = container.querySelector('#fido2UserId');
|
const userIdInput = container.querySelector('[id^="fido2UserId_"]');
|
||||||
if (userIdInput) {
|
if (userIdInput) {
|
||||||
userIdInput.addEventListener('keypress', (e) => {
|
userIdInput.addEventListener('keypress', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
@@ -659,7 +684,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const passwordInput = container.querySelector('#fido2Password');
|
const passwordInput = container.querySelector('[id^="fido2Password_"]');
|
||||||
if (passwordInput) {
|
if (passwordInput) {
|
||||||
passwordInput.addEventListener('keypress', (e) => {
|
passwordInput.addEventListener('keypress', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
@@ -683,10 +708,15 @@
|
|||||||
this._stopLoading();
|
this._stopLoading();
|
||||||
if (result.status === 'ok') {
|
if (result.status === 'ok') {
|
||||||
this.state = LoginState.CLOSED;
|
this.state = LoginState.CLOSED;
|
||||||
this._emit('fido2Success', result.username, result.session);
|
this._emit('fido2Success', {
|
||||||
|
method: 'fido2',
|
||||||
|
username: result.username,
|
||||||
|
session: result.session
|
||||||
|
});
|
||||||
this._closeUI();
|
this._closeUI();
|
||||||
} else {
|
} else {
|
||||||
this._emit('fido2Error', {
|
this._emit('fido2Error', {
|
||||||
|
method: 'fido2',
|
||||||
code: 'AUTH_FAILED',
|
code: 'AUTH_FAILED',
|
||||||
message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
|
message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
|
||||||
originalError: result
|
originalError: result
|
||||||
@@ -696,12 +726,14 @@
|
|||||||
this._stopLoading();
|
this._stopLoading();
|
||||||
if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
|
if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
|
||||||
this._emit('fido2Error', {
|
this._emit('fido2Error', {
|
||||||
|
method: 'fido2',
|
||||||
code: 'CANCELED',
|
code: 'CANCELED',
|
||||||
message: this.i18n.getText('msg_fido2_canceled'),
|
message: this.i18n.getText('msg_fido2_canceled'),
|
||||||
originalError: error
|
originalError: error
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this._emit('fido2Error', {
|
this._emit('fido2Error', {
|
||||||
|
method: 'fido2',
|
||||||
code: 'AUTH_FAILED',
|
code: 'AUTH_FAILED',
|
||||||
message: error.message || this.i18n.getText('msg_fido2_failed'),
|
message: error.message || this.i18n.getText('msg_fido2_failed'),
|
||||||
originalError: error
|
originalError: error
|
||||||
@@ -727,6 +759,11 @@
|
|||||||
this._hideError();
|
this._hideError();
|
||||||
this._startLoading();
|
this._startLoading();
|
||||||
|
|
||||||
|
// Clear any existing FIDO2 session before password login
|
||||||
|
if (typeof logoutFido2UserSession === 'function') {
|
||||||
|
logoutFido2UserSession();
|
||||||
|
}
|
||||||
|
|
||||||
const passwordCallback = this.config.callbacks.onPasswordLogin;
|
const passwordCallback = this.config.callbacks.onPasswordLogin;
|
||||||
if (typeof passwordCallback !== 'function') {
|
if (typeof passwordCallback !== 'function') {
|
||||||
this._stopLoading();
|
this._stopLoading();
|
||||||
@@ -738,7 +775,11 @@
|
|||||||
this._stopLoading();
|
this._stopLoading();
|
||||||
if (success) {
|
if (success) {
|
||||||
this.state = LoginState.CLOSED;
|
this.state = LoginState.CLOSED;
|
||||||
this._emit('fido2Success', userId, null);
|
this._emit('fido2Success', {
|
||||||
|
method: 'password',
|
||||||
|
username: userId,
|
||||||
|
session: null
|
||||||
|
});
|
||||||
this._closeUI();
|
this._closeUI();
|
||||||
} else {
|
} else {
|
||||||
this.attemptCount++;
|
this.attemptCount++;
|
||||||
@@ -769,11 +810,11 @@
|
|||||||
const container = this.modalElement || this.containerElement;
|
const container = this.modalElement || this.containerElement;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const passwordSection = container.querySelector('#fido2PasswordSection');
|
const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
|
||||||
const mainBtn = container.querySelector('#fido2MainBtn');
|
const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
|
||||||
const toggleLink = container.querySelector('#fido2ToggleModeLink');
|
const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
|
||||||
const titleEl = document.getElementById('fido2LoginModalTitle');
|
const titleEl = container.querySelector('[id^="fido2LoginModalTitle_"]');
|
||||||
const remainingEl = container.querySelector('#fido2RemainingAttempts');
|
const remainingEl = container.querySelector('[id^="fido2RemainingAttempts_"]');
|
||||||
|
|
||||||
if (this.mode === LoginMode.FIDO2) {
|
if (this.mode === LoginMode.FIDO2) {
|
||||||
this.mode = LoginMode.PASSWORD;
|
this.mode = LoginMode.PASSWORD;
|
||||||
@@ -789,7 +830,7 @@
|
|||||||
} else if (this.config.features.showRemainingAttempts) {
|
} else if (this.config.features.showRemainingAttempts) {
|
||||||
const remainingContainer = document.createElement('div');
|
const remainingContainer = document.createElement('div');
|
||||||
remainingContainer.className = 'text-center mt-3';
|
remainingContainer.className = 'text-center mt-3';
|
||||||
remainingContainer.innerHTML = `<small class="text-muted fido2-sdk-text" id="fido2RemainingAttempts">${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}</small>`;
|
remainingContainer.innerHTML = `<small class="text-muted fido2-sdk-text" id="fido2RemainingAttempts_${this._uniqueId}">${this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining)}</small>`;
|
||||||
toggleLink.parentNode.parentNode.insertBefore(remainingContainer, toggleLink.parentNode.nextSibling);
|
toggleLink.parentNode.parentNode.insertBefore(remainingContainer, toggleLink.parentNode.nextSibling);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -808,18 +849,21 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._getUserIdInput = function() {
|
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() : '';
|
return input ? input.value.trim() : '';
|
||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._getPasswordInput = function() {
|
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 : '';
|
return input ? input.value : '';
|
||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._showError = function(message) {
|
Fido2Login.prototype._showError = function(message) {
|
||||||
const errorEl = document.getElementById('fido2LoginError');
|
const container = this.modalElement || this.containerElement;
|
||||||
const hintEl = document.getElementById('fido2LoginHint');
|
const errorEl = container ? container.querySelector('[id^="fido2LoginError_"]') : null;
|
||||||
|
const hintEl = container ? container.querySelector('[id^="fido2LoginHint_"]') : null;
|
||||||
if (hintEl) hintEl.style.display = 'none';
|
if (hintEl) hintEl.style.display = 'none';
|
||||||
if (errorEl) {
|
if (errorEl) {
|
||||||
errorEl.textContent = message;
|
errorEl.textContent = message;
|
||||||
@@ -828,12 +872,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._hideError = function() {
|
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';
|
if (errorEl) errorEl.style.display = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._showHint = function(message) {
|
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) {
|
if (hintEl) {
|
||||||
hintEl.textContent = message;
|
hintEl.textContent = message;
|
||||||
hintEl.style.display = 'block';
|
hintEl.style.display = 'block';
|
||||||
@@ -841,12 +887,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._hideHint = function() {
|
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';
|
if (hintEl) hintEl.style.display = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._startLoading = function() {
|
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) {
|
if (mainBtn) {
|
||||||
mainBtn.disabled = true;
|
mainBtn.disabled = true;
|
||||||
mainBtn.dataset.originalText = mainBtn.textContent;
|
mainBtn.dataset.originalText = mainBtn.textContent;
|
||||||
@@ -855,7 +903,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._stopLoading = function() {
|
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) {
|
if (mainBtn) {
|
||||||
mainBtn.disabled = false;
|
mainBtn.disabled = false;
|
||||||
mainBtn.textContent = mainBtn.dataset.originalText ||
|
mainBtn.textContent = mainBtn.dataset.originalText ||
|
||||||
@@ -867,17 +916,24 @@
|
|||||||
const container = this.modalElement || this.containerElement;
|
const container = this.modalElement || this.containerElement;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const remainingEl = container.querySelector('#fido2RemainingAttempts');
|
const remainingEl = container.querySelector('[id^="fido2RemainingAttempts_"]');
|
||||||
if (remainingEl) {
|
if (remainingEl) {
|
||||||
const remaining = this.maxAttempts - this.attemptCount;
|
const remaining = this.maxAttempts - this.attemptCount;
|
||||||
remainingEl.textContent = this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining);
|
remainingEl.textContent = this.i18n.getText('msg_remaining_attempts').replace('{n}', remaining);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Fido2Login.prototype._emit = function(event, ...args) {
|
Fido2Login.prototype._emit = function(event, data) {
|
||||||
const callback = this.config.callbacks['on' + event.charAt(0).toUpperCase() + event.slice(1)];
|
const callbackName = 'on' + event.charAt(0).toUpperCase() + event.slice(1);
|
||||||
|
const callback = this.config.callbacks[callbackName];
|
||||||
if (typeof callback === 'function') {
|
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();
|
this._hideHint();
|
||||||
if (result.status === 'ok') {
|
if (result.status === 'ok') {
|
||||||
this.state = LoginState.CLOSED;
|
this.state = LoginState.CLOSED;
|
||||||
this._emit('fido2Success', result.username, result.session);
|
this._emit('fido2Success', {
|
||||||
|
method: 'fido2',
|
||||||
|
username: result.username,
|
||||||
|
session: result.session
|
||||||
|
});
|
||||||
this._closeUI();
|
this._closeUI();
|
||||||
} else {
|
} else {
|
||||||
this.state = LoginState.FIDO2;
|
this.state = LoginState.FIDO2;
|
||||||
this._emit('fido2Error', {
|
this._emit('fido2Error', {
|
||||||
|
method: 'fido2',
|
||||||
code: 'AUTH_FAILED',
|
code: 'AUTH_FAILED',
|
||||||
message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
|
message: result.errorMessage || this.i18n.getText('msg_fido2_failed'),
|
||||||
originalError: result
|
originalError: result
|
||||||
@@ -951,6 +1012,7 @@
|
|||||||
if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
|
if (error.name === 'AbortError' || (error.message && error.message.toLowerCase().includes('canceled'))) {
|
||||||
this.state = LoginState.FIDO2;
|
this.state = LoginState.FIDO2;
|
||||||
this._emit('fido2Error', {
|
this._emit('fido2Error', {
|
||||||
|
method: 'fido2',
|
||||||
code: 'CANCELED',
|
code: 'CANCELED',
|
||||||
message: this.i18n.getText('msg_fido2_canceled'),
|
message: this.i18n.getText('msg_fido2_canceled'),
|
||||||
originalError: error
|
originalError: error
|
||||||
@@ -958,6 +1020,7 @@
|
|||||||
} else {
|
} else {
|
||||||
this.state = LoginState.FIDO2;
|
this.state = LoginState.FIDO2;
|
||||||
this._emit('fido2Error', {
|
this._emit('fido2Error', {
|
||||||
|
method: 'fido2',
|
||||||
code: 'AUTH_FAILED',
|
code: 'AUTH_FAILED',
|
||||||
message: error.message || this.i18n.getText('msg_fido2_failed'),
|
message: error.message || this.i18n.getText('msg_fido2_failed'),
|
||||||
originalError: error
|
originalError: error
|
||||||
@@ -974,6 +1037,7 @@
|
|||||||
} else {
|
} else {
|
||||||
this.state = LoginState.FIDO2;
|
this.state = LoginState.FIDO2;
|
||||||
this._emit('fido2Error', {
|
this._emit('fido2Error', {
|
||||||
|
method: 'fido2',
|
||||||
code: 'NO_REGISTRATION',
|
code: 'NO_REGISTRATION',
|
||||||
message: this.i18n.getText('msg_no_registration'),
|
message: this.i18n.getText('msg_no_registration'),
|
||||||
originalError: null
|
originalError: null
|
||||||
@@ -1009,9 +1073,9 @@
|
|||||||
const container = this.modalElement || this.containerElement;
|
const container = this.modalElement || this.containerElement;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const passwordSection = container.querySelector('#fido2PasswordSection');
|
const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
|
||||||
const mainBtn = container.querySelector('#fido2MainBtn');
|
const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
|
||||||
const toggleLink = container.querySelector('#fido2ToggleModeLink');
|
const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
|
||||||
|
|
||||||
if (passwordSection) passwordSection.style.display = 'none';
|
if (passwordSection) passwordSection.style.display = 'none';
|
||||||
if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_fido2_login');
|
if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_fido2_login');
|
||||||
@@ -1027,10 +1091,10 @@
|
|||||||
const container = this.modalElement || this.containerElement;
|
const container = this.modalElement || this.containerElement;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const passwordSection = container.querySelector('#fido2PasswordSection');
|
const passwordSection = container.querySelector('[id^="fido2PasswordSection_"]');
|
||||||
const mainBtn = container.querySelector('#fido2MainBtn');
|
const mainBtn = container.querySelector('[id^="fido2MainBtn_"]');
|
||||||
const toggleLink = container.querySelector('#fido2ToggleModeLink');
|
const toggleLink = container.querySelector('[id^="fido2ToggleModeLink_"]');
|
||||||
const titleEl = document.getElementById('fido2LoginModalTitle');
|
const titleEl = container.querySelector('[id^="fido2LoginModalTitle_"]');
|
||||||
|
|
||||||
if (passwordSection) passwordSection.style.display = 'block';
|
if (passwordSection) passwordSection.style.display = 'block';
|
||||||
if (mainBtn) mainBtn.textContent = this.i18n.getText('btn_password_login');
|
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);
|
const result = await registerFido2(effectiveUserId, displayName || 'Device-' + effectiveUserId, this.config.rpId);
|
||||||
|
|
||||||
if (result.status === 'ok') {
|
if (result.status === 'ok') {
|
||||||
|
// registerFido2 automatically creates/updates session in sessionStorage
|
||||||
await this.loadDevices();
|
await this.loadDevices();
|
||||||
this.eventManager.emit('deviceAdded', result);
|
this.eventManager.emit('deviceAdded', result);
|
||||||
return result;
|
return result;
|
||||||
@@ -1330,25 +1395,11 @@
|
|||||||
|
|
||||||
DeviceManager.prototype.checkSession = async function() {
|
DeviceManager.prototype.checkSession = async function() {
|
||||||
try {
|
try {
|
||||||
const sessionUserId = this.getSessionUserId();
|
// Simply validate session with server, same as devices.html does
|
||||||
|
const sessionOk = await validSession(this.config.rpId);
|
||||||
if (!sessionUserId) {
|
this.sessionStatus = !!sessionOk;
|
||||||
this.sessionStatus = false;
|
this.eventManager.emit('sessionStatusChanged', this.sessionStatus);
|
||||||
this.eventManager.emit('sessionStatusChanged', false);
|
return this.sessionStatus;
|
||||||
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;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.sessionStatus = false;
|
this.sessionStatus = false;
|
||||||
this.eventManager.emit('sessionStatusChanged', false);
|
this.eventManager.emit('sessionStatusChanged', false);
|
||||||
@@ -1420,16 +1471,23 @@
|
|||||||
|
|
||||||
container.innerHTML = '';
|
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');
|
const modal = document.createElement('div');
|
||||||
modal.className = 'modal fade fido2-sdk-modal';
|
modal.className = 'modal fade fido2-sdk-modal';
|
||||||
modal.id = 'fido2SdkModal';
|
modal.id = modalId;
|
||||||
modal.tabIndex = -1;
|
modal.tabIndex = -1;
|
||||||
modal.setAttribute('aria-hidden', 'true');
|
modal.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
modal.innerHTML = this._getModalHTML();
|
modal.innerHTML = this._getModalHTML(uniqueId, sessionStatusId, addBtnId, devicesListId);
|
||||||
container.appendChild(modal);
|
container.appendChild(modal);
|
||||||
this.modalElement = modal;
|
this.modalElement = modal;
|
||||||
this.containerElement = container;
|
this.containerElement = container;
|
||||||
|
this._uniqueId = uniqueId;
|
||||||
|
|
||||||
this.themeManager.applyTheme(modal);
|
this.themeManager.applyTheme(modal);
|
||||||
|
|
||||||
@@ -1445,12 +1503,12 @@
|
|||||||
this.cleanup();
|
this.cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
this._bindEvents();
|
this._bindEvents(uniqueId, addBtnId, devicesListId);
|
||||||
|
|
||||||
return modal;
|
return modal;
|
||||||
};
|
};
|
||||||
|
|
||||||
UIRenderer.prototype._getModalHTML = function() {
|
UIRenderer.prototype._getModalHTML = function(uniqueId, sessionStatusId, addBtnId, devicesListId) {
|
||||||
const theme = this.config.theme;
|
const theme = this.config.theme;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@@ -1459,11 +1517,11 @@
|
|||||||
<div class="modal-header fido2-sdk-header">
|
<div class="modal-header fido2-sdk-header">
|
||||||
${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo" alt="Logo">` : ''}
|
${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo" alt="Logo">` : ''}
|
||||||
<h5 class="modal-title fido2-sdk-text">${this.i18n.getText('my_devices')}</h5>
|
<h5 class="modal-title fido2-sdk-text">${this.i18n.getText('my_devices')}</h5>
|
||||||
${this.config.features.showSessionStatus ? '<span class="badge fido2-sdk-status-badge" id="fido2SessionStatus"></span>' : ''}
|
${this.config.features.showSessionStatus ? `<span class="badge fido2-sdk-status-badge" id="${sessionStatusId}"></span>` : ''}
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body fido2-sdk-container">
|
<div class="modal-body fido2-sdk-container">
|
||||||
${this._getBodyHTML()}
|
${this._getBodyHTML(uniqueId, addBtnId, devicesListId)}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary fido2-sdk-btn" data-bs-dismiss="modal">${this.i18n.getText('btn_close')}</button>
|
<button type="button" class="btn btn-secondary fido2-sdk-btn" data-bs-dismiss="modal">${this.i18n.getText('btn_close')}</button>
|
||||||
@@ -1473,7 +1531,7 @@
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
UIRenderer.prototype._getBodyHTML = function() {
|
UIRenderer.prototype._getBodyHTML = function(uniqueId, addBtnId, devicesListId) {
|
||||||
const features = this.config.features;
|
const features = this.config.features;
|
||||||
const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;
|
const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;
|
||||||
const userId = dm ? dm.getEffectiveUserId() : (this.config.userId || '');
|
const userId = dm ? dm.getEffectiveUserId() : (this.config.userId || '');
|
||||||
@@ -1488,7 +1546,7 @@
|
|||||||
|
|
||||||
if (features.showAddButton) {
|
if (features.showAddButton) {
|
||||||
html += `
|
html += `
|
||||||
<button type="button" class="btn btn-info mb-3 fido2-sdk-btn fido2-sdk-btn-primary" id="fido2AddDeviceBtn">
|
<button type="button" class="btn btn-info mb-3 fido2-sdk-btn fido2-sdk-btn-primary" id="${addBtnId}">
|
||||||
${this.i18n.getText('btn_add')}
|
${this.i18n.getText('btn_add')}
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
@@ -1504,7 +1562,7 @@
|
|||||||
${features.showDeleteButton ? `<th>${this.i18n.getText('title_act')}</th>` : ''}
|
${features.showDeleteButton ? `<th>${this.i18n.getText('title_act')}</th>` : ''}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="fido2DevicesList">
|
<tbody id="${devicesListId}">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" class="text-center fido2-sdk-text">${this.i18n.getText('title_empty_list')}</td>
|
<td colspan="3" class="text-center fido2-sdk-text">${this.i18n.getText('title_empty_list')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -1516,7 +1574,7 @@
|
|||||||
return html;
|
return html;
|
||||||
};
|
};
|
||||||
|
|
||||||
UIRenderer.prototype._bindEvents = function() {
|
UIRenderer.prototype._bindEvents = function(uniqueId, addBtnId, devicesListId) {
|
||||||
const container = this.modalElement || this.containerElement;
|
const container = this.modalElement || this.containerElement;
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
@@ -1524,10 +1582,10 @@
|
|||||||
btn.replaceWith(btn.cloneNode(true));
|
btn.replaceWith(btn.cloneNode(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
const addBtn = container.querySelector('#fido2AddDeviceBtn');
|
const addBtn = container.querySelector('#' + addBtnId);
|
||||||
if (addBtn) {
|
if (addBtn) {
|
||||||
addBtn.replaceWith(addBtn.cloneNode(true));
|
addBtn.replaceWith(addBtn.cloneNode(true));
|
||||||
const newAddBtn = container.querySelector('#fido2AddDeviceBtn');
|
const newAddBtn = container.querySelector('#' + addBtnId);
|
||||||
newAddBtn.addEventListener('click', () => {
|
newAddBtn.addEventListener('click', () => {
|
||||||
this.eventManager.emit('addDevice');
|
this.eventManager.emit('addDevice');
|
||||||
});
|
});
|
||||||
@@ -1545,7 +1603,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
UIRenderer.prototype.updateDevicesList = function(devices) {
|
UIRenderer.prototype.updateDevicesList = function(devices) {
|
||||||
const tbody = document.getElementById('fido2DevicesList');
|
const container = this.modalElement || this.containerElement;
|
||||||
|
const tbody = container ? container.querySelector('[id^="fido2DevicesList_"]') : null;
|
||||||
if (!tbody) return;
|
if (!tbody) return;
|
||||||
|
|
||||||
if (!devices || devices.length === 0) {
|
if (!devices || devices.length === 0) {
|
||||||
@@ -1583,12 +1642,13 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
tbody.innerHTML = html;
|
tbody.innerHTML = html;
|
||||||
this._bindEvents();
|
this._bindEvents(this._uniqueId, 'fido2AddDeviceBtn_' + this._uniqueId, 'fido2DevicesList_' + this._uniqueId);
|
||||||
};
|
};
|
||||||
|
|
||||||
UIRenderer.prototype.updateSessionStatus = function(isValid) {
|
UIRenderer.prototype.updateSessionStatus = function(isValid) {
|
||||||
const badge = document.getElementById('fido2SessionStatus');
|
const container = this.modalElement || this.containerElement;
|
||||||
const addBtn = document.getElementById('fido2AddDeviceBtn');
|
const badge = container ? container.querySelector('[id^="fido2SessionStatus_"]') : null;
|
||||||
|
const addBtn = container ? container.querySelector('[id^="fido2AddDeviceBtn_"]') : null;
|
||||||
|
|
||||||
if (badge) {
|
if (badge) {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
@@ -1601,7 +1661,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (addBtn) {
|
if (addBtn) {
|
||||||
addBtn.disabled = !isValid;
|
addBtn.disabled = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1625,137 +1685,6 @@
|
|||||||
this.containerElement = null;
|
this.containerElement = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
UIRenderer.prototype.renderStandalone = function() {
|
|
||||||
if (this.config.container) {
|
|
||||||
const container = typeof this.config.container === 'string'
|
|
||||||
? document.querySelector(this.config.container)
|
|
||||||
: this.config.container;
|
|
||||||
|
|
||||||
if (container) {
|
|
||||||
container.innerHTML = this._getStandaloneBodyHTML();
|
|
||||||
this.containerElement = container;
|
|
||||||
this.themeManager.applyTheme(container);
|
|
||||||
this._bindEvents();
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.innerHTML = this._getStandaloneBodyHTML();
|
|
||||||
document.body.className = 'fido2-sdk-standalone';
|
|
||||||
this.containerElement = document.body;
|
|
||||||
this.themeManager.applyTheme(document.body);
|
|
||||||
this._bindEvents();
|
|
||||||
return document.body;
|
|
||||||
};
|
|
||||||
|
|
||||||
UIRenderer.prototype._getStandaloneBodyHTML = function() {
|
|
||||||
const theme = this.config.theme;
|
|
||||||
const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;
|
|
||||||
const userId = dm ? dm.getEffectiveUserId() : (this.config.userId || '');
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="container fido2-sdk-container">
|
|
||||||
<div class="card fido2-sdk-card">
|
|
||||||
<div class="card-header fido2-sdk-header d-flex justify-content-between align-items-center">
|
|
||||||
<div>
|
|
||||||
${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo me-2" alt="Logo">` : ''}
|
|
||||||
<h4 class="mb-0 fido2-sdk-text">${this.i18n.getText('my_devices')}</h4>
|
|
||||||
</div>
|
|
||||||
${this.config.features.showSessionStatus ? '<span class="badge fido2-sdk-status-badge" id="fido2SessionStatus"></span>' : ''}
|
|
||||||
</div>
|
|
||||||
<div class="card-body fido2-sdk-body">
|
|
||||||
${this.config.features.showUserInfo && userId ? `
|
|
||||||
<div class="alert alert-info fido2-sdk-user-info fido2-sdk-text">
|
|
||||||
<strong>${this.i18n.getText('title_welcome')}:</strong> ${userId}
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${this.config.features.showAddButton ? `
|
|
||||||
<button type="button" class="btn btn-info mt-2 mb-3 fido2-sdk-btn fido2-sdk-btn-primary" id="fido2AddDeviceBtn">
|
|
||||||
${this.i18n.getText('btn_add')}
|
|
||||||
</button>
|
|
||||||
` : ''}
|
|
||||||
<div class="table-responsive mt-2">
|
|
||||||
<table class="table table-striped fido2-sdk-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>${this.i18n.getText('title_device')}</th>
|
|
||||||
<th>${this.i18n.getText('title_time')}</th>
|
|
||||||
${this.config.features.showDeleteButton ? `<th>${this.i18n.getText('title_act')}</th>` : ''}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="fido2DevicesList">
|
|
||||||
<tr>
|
|
||||||
<td colspan="3" class="text-center fido2-sdk-text">${this.i18n.getText('title_empty_list')}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
UIRenderer.prototype._getStandaloneHTML = function() {
|
|
||||||
const theme = this.config.theme;
|
|
||||||
const dm = window.Fido2UIManager ? window.Fido2UIManager.deviceManager : null;
|
|
||||||
const userId = dm ? dm.getEffectiveUserId() : (this.config.userId || '');
|
|
||||||
|
|
||||||
return `
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>${this.i18n.getText('my_devices')}</title>
|
|
||||||
<link rel="stylesheet" href="files/bootstrap.css">
|
|
||||||
<link rel="stylesheet" href="files/boxicons.css">
|
|
||||||
<link rel="stylesheet" href="files/fido2-ui-sdk.css">
|
|
||||||
</head>
|
|
||||||
<body class="fido2-sdk-standalone">
|
|
||||||
<div class="container fido2-sdk-container">
|
|
||||||
<div class="card fido2-sdk-card">
|
|
||||||
<div class="card-header fido2-sdk-header d-flex justify-content-between align-items-center">
|
|
||||||
<div>
|
|
||||||
${theme.logo ? `<img src="${theme.logo}" class="fido2-sdk-logo me-2" alt="Logo">` : ''}
|
|
||||||
<h4 class="mb-0 fido2-sdk-text">${this.i18n.getText('my_devices')}</h4>
|
|
||||||
</div>
|
|
||||||
${this.config.features.showSessionStatus ? '<span class="badge fido2-sdk-status-badge" id="fido2SessionStatus"></span>' : ''}
|
|
||||||
</div>
|
|
||||||
<div class="card-body fido2-sdk-body">
|
|
||||||
${this.config.features.showUserInfo && userId ? `
|
|
||||||
<div class="alert alert-info fido2-sdk-user-info fido2-sdk-text">
|
|
||||||
<strong>${this.i18n.getText('title_welcome')}:</strong> ${userId}
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
${this.config.features.showAddButton ? `
|
|
||||||
<button type="button" class="btn btn-info mt-2 mb-3 fido2-sdk-btn fido2-sdk-btn-primary" id="fido2AddDeviceBtn">
|
|
||||||
${this.i18n.getText('btn_add')}
|
|
||||||
</button>
|
|
||||||
` : ''}
|
|
||||||
<div class="table-responsive mt-2">
|
|
||||||
<table class="table table-striped fido2-sdk-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>${this.i18n.getText('title_device')}</th>
|
|
||||||
<th>${this.i18n.getText('title_time')}</th>
|
|
||||||
${this.config.features.showDeleteButton ? `<th>${this.i18n.getText('title_act')}</th>` : ''}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="fido2DevicesList">
|
|
||||||
<tr>
|
|
||||||
<td colspan="3" class="text-center fido2-sdk-text">${this.i18n.getText('title_empty_list')}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
function Fido2UIManager() {
|
function Fido2UIManager() {
|
||||||
this.config = null;
|
this.config = null;
|
||||||
@@ -1773,10 +1702,6 @@
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!window.jQuery) {
|
|
||||||
throw new Error('jQuery is required. Please include jQuery before fido2-ui-sdk.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!window.bootstrap) {
|
if (!window.bootstrap) {
|
||||||
throw new Error('Bootstrap is required. Please include Bootstrap JS before fido2-ui-sdk.js');
|
throw new Error('Bootstrap is required. Please include Bootstrap JS before fido2-ui-sdk.js');
|
||||||
}
|
}
|
||||||
@@ -1832,11 +1757,7 @@
|
|||||||
|
|
||||||
const mode = manager.config.mode;
|
const mode = manager.config.mode;
|
||||||
|
|
||||||
if (mode === 'standalone') {
|
manager.uiRenderer.renderModal();
|
||||||
manager.uiRenderer.renderStandalone();
|
|
||||||
} else {
|
|
||||||
manager.uiRenderer.renderModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
manager._bindInternalEvents();
|
manager._bindInternalEvents();
|
||||||
manager._loadInitialData();
|
manager._loadInitialData();
|
||||||
@@ -1856,7 +1777,9 @@
|
|||||||
try {
|
try {
|
||||||
await self.deviceManager.addDevice();
|
await self.deviceManager.addDevice();
|
||||||
self.uiRenderer.updateDevicesList(self.deviceManager.devices);
|
self.uiRenderer.updateDevicesList(self.deviceManager.devices);
|
||||||
|
// Re-check session after device list is updated
|
||||||
await self.deviceManager.checkSession();
|
await self.deviceManager.checkSession();
|
||||||
|
self.uiRenderer.updateSessionStatus(self.deviceManager.sessionStatus);
|
||||||
alert(self.i18n.getText('msg_register_ok'));
|
alert(self.i18n.getText('msg_register_ok'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Add device error:', error);
|
console.error('Add device error:', error);
|
||||||
@@ -1913,6 +1836,13 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Fido2UIManager.prototype.logout = function() {
|
||||||
|
if (typeof logoutFido2UserSession === 'function') {
|
||||||
|
logoutFido2UserSession();
|
||||||
|
}
|
||||||
|
this.eventManager.emit('logout');
|
||||||
|
};
|
||||||
|
|
||||||
Fido2UIManager.prototype.destroy = function() {
|
Fido2UIManager.prototype.destroy = function() {
|
||||||
if (this.deviceManager) {
|
if (this.deviceManager) {
|
||||||
this.deviceManager.stopAutoRefresh();
|
this.deviceManager.stopAutoRefresh();
|
||||||
@@ -1926,10 +1856,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
Fido2UIManager.prototype.renderLogin = function(config) {
|
Fido2UIManager.prototype.renderLogin = function(config) {
|
||||||
if (!window.jQuery) {
|
|
||||||
throw new Error('jQuery is required. Please include jQuery before fido2-ui-sdk.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!window.bootstrap) {
|
if (!window.bootstrap) {
|
||||||
throw new Error('Bootstrap is required. Please include Bootstrap JS before fido2-ui-sdk.js');
|
throw new Error('Bootstrap is required. Please include Bootstrap JS before fido2-ui-sdk.js');
|
||||||
}
|
}
|
||||||
@@ -1954,6 +1880,11 @@
|
|||||||
window.Fido2UIManager.refresh();
|
window.Fido2UIManager.refresh();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
window.Fido2UIManager.logout = function() {
|
||||||
|
if (window.Fido2UIManager && window.Fido2UIManager.logout) {
|
||||||
|
window.Fido2UIManager.logout();
|
||||||
|
}
|
||||||
|
};
|
||||||
window.Fido2UIManager.destroy = function() {
|
window.Fido2UIManager.destroy = function() {
|
||||||
if (window.Fido2UIManager && window.Fido2UIManager.destroy) {
|
if (window.Fido2UIManager && window.Fido2UIManager.destroy) {
|
||||||
window.Fido2UIManager.destroy();
|
window.Fido2UIManager.destroy();
|
||||||
|
|||||||
14
login.html
14
login.html
@@ -47,22 +47,21 @@
|
|||||||
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
|
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
|
||||||
<script src="files/config.js"></script>
|
<script src="files/config.js"></script>
|
||||||
|
|
||||||
<script src="files/jquery.js"></script>
|
|
||||||
<script src="files/popper.js"></script>
|
<script src="files/popper.js"></script>
|
||||||
<script src="files/bootstrap.js"></script>
|
<script src="files/bootstrap.js"></script>
|
||||||
<script src="files/perfect-scrollbar.js"></script>
|
<script src="files/perfect-scrollbar.js"></script>
|
||||||
<script src="files/menu.js"></script>
|
<script src="files/menu.js"></script>
|
||||||
<script src="files/main.js"></script>
|
<script src="files/main.js"></script>
|
||||||
|
|
||||||
<script src="files/amipro_utils.js?v=20230414"></script>
|
<script src="files/amipro_utils.js?v=20260118"></script>
|
||||||
<script src="files/dfido2-lib.js?v=2025092701"></script>
|
<script src="files/dfido2-lib.js?v=20260118"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
<!--
|
<!--
|
||||||
// For stand alone: 'https://local.dqj-macpro.com'
|
// For stand alone: 'https://local.dqj-macpro.com'
|
||||||
// For proxy: set 'https://mac-air-m2.dqj-home.com'
|
// For proxy: set 'https://mac-air-m2.dqj-home.com'
|
||||||
-->
|
-->
|
||||||
setFidoServerURL('https://local.dqj-macpro.com');//'https://fido2.amipro.me');
|
setFidoServerURL('https://fido2.amipro.me'); //'https://local.dqj-macpro.com');
|
||||||
|
|
||||||
const i18n_messages = new Map();
|
const i18n_messages = new Map();
|
||||||
|
|
||||||
@@ -167,13 +166,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function authenticate(){
|
async function authenticate(){
|
||||||
var uid = $('#uid').val()
|
var uid = document.getElementById('uid').value
|
||||||
if(uid && 0==uid.length)uid=null
|
if(uid && 0==uid.length)uid=null
|
||||||
|
|
||||||
const result = await authenticateFido2(uid)
|
const result = await authenticateFido2(uid)
|
||||||
|
|
||||||
if(result.status === 'ok'){
|
if(result.status === 'ok'){
|
||||||
//Set your logged in status and jump to user's top page.
|
|
||||||
location.href = 'devices.html?uid='+result.username
|
location.href = 'devices.html?uid='+result.username
|
||||||
}else{
|
}else{
|
||||||
errProcessFido2(result)
|
errProcessFido2(result)
|
||||||
@@ -181,9 +179,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkInput(){
|
function checkInput(){
|
||||||
const uid = $('#uid').val()
|
const uid = document.getElementById('uid').value
|
||||||
if(!uid || 0>=uid.length){
|
if(!uid || 0>=uid.length){
|
||||||
alert($('#msg_uid_input').html())
|
alert(document.getElementById('msg_uid_input').textContent)
|
||||||
return false;
|
return false;
|
||||||
}else return true;
|
}else return true;
|
||||||
}
|
}
|
||||||
|
|||||||
173
modal-demo.html
173
modal-demo.html
@@ -177,9 +177,8 @@
|
|||||||
.log-type-error {
|
.log-type-error {
|
||||||
color: #f48771;
|
color: #f48771;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script src="files/jquery.js"></script>
|
|
||||||
<script src="files/popper.js"></script>
|
<script src="files/popper.js"></script>
|
||||||
<script src="files/bootstrap.js"></script>
|
<script src="files/bootstrap.js"></script>
|
||||||
<script src="files/dfido2-lib.js"></script>
|
<script src="files/dfido2-lib.js"></script>
|
||||||
@@ -214,9 +213,9 @@
|
|||||||
i18n_messages.set("msg_login_desc", lang_map);
|
i18n_messages.set("msg_login_desc", lang_map);
|
||||||
|
|
||||||
lang_map = new Map();
|
lang_map = new Map();
|
||||||
lang_map.set("en-US", "Login");
|
lang_map.set("en-US", "Passkey Login");
|
||||||
lang_map.set("zh-CN", "登录");
|
lang_map.set("zh-CN", "Passkey 登录");
|
||||||
lang_map.set("ja", "ログイン");
|
lang_map.set("ja", "パスキーログイン");
|
||||||
i18n_messages.set("msg_btn_passkey_login", lang_map);
|
i18n_messages.set("msg_btn_passkey_login", lang_map);
|
||||||
|
|
||||||
lang_map = new Map();
|
lang_map = new Map();
|
||||||
@@ -362,30 +361,16 @@
|
|||||||
lang_map.set("zh-CN", "事件回调系统");
|
lang_map.set("zh-CN", "事件回调系统");
|
||||||
lang_map.set("ja", "イベント コールバック システム");
|
lang_map.set("ja", "イベント コールバック システム");
|
||||||
i18n_messages.set("msg_feature_9", lang_map);
|
i18n_messages.set("msg_feature_9", lang_map);
|
||||||
lang_map.set("zh-CN", "主题色和样式定制");
|
|
||||||
lang_map.set("ja", "テーマの色とスタイルのカスタマイズ");
|
|
||||||
i18n_messages.set("msg_feature_7", lang_map);
|
|
||||||
|
|
||||||
lang_map = new Map();
|
|
||||||
lang_map.set("en-US", "Multi-language support (English/Japanese/Chinese)");
|
|
||||||
lang_map.set("zh-CN", "多语言支持(英/日/中)");
|
|
||||||
lang_map.set("ja", "多言語対応(英/日/中)");
|
|
||||||
i18n_messages.set("msg_feature_8", lang_map);
|
|
||||||
|
|
||||||
lang_map = new Map();
|
|
||||||
lang_map.set("en-US", "Event callback system");
|
lang_map.set("en-US", "Event callback system");
|
||||||
lang_map.set("zh-CN", "事件回调系统");
|
lang_map.set("zh-CN", "事件回调系统");
|
||||||
lang_map.set("ja", "イベント コールバック システム");
|
lang_map.set("ja", "イベント コールバック システム");
|
||||||
i18n_messages.set("msg_feature_9", lang_map);
|
i18n_messages.set("msg_feature_9", lang_map);
|
||||||
lang_map.set("zh-CN", "多语言支持(英/日/中)");
|
|
||||||
lang_map.set("ja", "多言語対応(英/日/中)");
|
|
||||||
i18n_messages.set("msg_feature_8", lang_map);
|
|
||||||
|
|
||||||
lang_map = new Map();
|
lang_map = new Map();
|
||||||
lang_map.set("en-US", "Event callback system");
|
lang_map.set("en-US", "Login Default");
|
||||||
lang_map.set("zh-CN", "事件回调系统");
|
lang_map.set("zh-CN", "登录缺省");
|
||||||
lang_map.set("ja", "イベント コールバック システム");
|
lang_map.set("ja", "ログイン デフォルト");
|
||||||
i18n_messages.set("msg_feature_9", lang_map);
|
i18n_messages.set("msg_code_login_default", lang_map);
|
||||||
|
|
||||||
lang_map = new Map();
|
lang_map = new Map();
|
||||||
lang_map.set("en-US", "📊 Event Log");
|
lang_map.set("en-US", "📊 Event Log");
|
||||||
@@ -453,6 +438,12 @@
|
|||||||
lang_map.set("ja", "エラーが発生しました");
|
lang_map.set("ja", "エラーが発生しました");
|
||||||
i18n_messages.set("msg_log_error", lang_map);
|
i18n_messages.set("msg_log_error", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Password attempts exhausted");
|
||||||
|
lang_map.set("zh-CN", "密码尝试次数已用完");
|
||||||
|
lang_map.set("ja", "パスワード試行回数が上限に達しました");
|
||||||
|
i18n_messages.set("msg_password_exhausted", lang_map);
|
||||||
|
|
||||||
lang_map = new Map();
|
lang_map = new Map();
|
||||||
lang_map.set("en-US", "Device manager closed");
|
lang_map.set("en-US", "Device manager closed");
|
||||||
lang_map.set("zh-CN", "设备管理器已关闭");
|
lang_map.set("zh-CN", "设备管理器已关闭");
|
||||||
@@ -510,41 +501,107 @@
|
|||||||
|
|
||||||
<div class="demo-section">
|
<div class="demo-section">
|
||||||
<h2 id="msg_section_code_title">💻 Code Examples</h2>
|
<h2 id="msg_section_code_title">💻 Code Examples</h2>
|
||||||
<p id="msg_code_simple">Simplest integration:</p>
|
|
||||||
|
<p id="msg_js_import_examples">JS Import Examples:</p>
|
||||||
<div class="code-block">
|
<div class="code-block">
|
||||||
<code>Fido2UIManager.renderLogin({
|
<code><script src="files/popper.js"></script><br>
|
||||||
container: '#login-container',
|
<script src="files/bootstrap.js"></script><br>
|
||||||
mode: 'modal',
|
<script src="files/dfido2-lib.js"></script><br>
|
||||||
serverUrl: SERVER_URL
|
<script src="files/fido2-ui-sdk.js"></script><br>
|
||||||
|
<script src="files/amipro_utils.js"></script></code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="msg_code_login_default">Login Default:</p>
|
||||||
|
<div class="code-block">
|
||||||
|
<code>// Login Default<br>
|
||||||
|
Fido2UIManager.renderLogin({<br>
|
||||||
|
container: '#login-container',<br>
|
||||||
|
mode: 'modal',<br>
|
||||||
|
serverUrl: SERVER_URL,<br>
|
||||||
|
language: CURRENT_LANG<br>
|
||||||
});</code>
|
});</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p id="msg_code_theme">With theme customization:</p>
|
<p id="msg_code_login_custom">Login Custom:</p>
|
||||||
<div class="code-block">
|
<div class="code-block">
|
||||||
<code>Fido2UIManager.renderLogin({
|
<code>// Login with Custom Theme<br>
|
||||||
container: '#login-container',
|
Fido2UIManager.renderLogin({<br>
|
||||||
mode: 'modal',
|
container: '#login-container',<br>
|
||||||
serverUrl: SERVER_URL,
|
mode: 'modal',<br>
|
||||||
language: 'ja',
|
serverUrl: SERVER_URL,<br>
|
||||||
theme: {
|
language: 'ja',<br>
|
||||||
primaryColor: '#ce59d9',
|
theme: {<br>
|
||||||
backgroundColor: '#faf5ff'
|
logo: 'files/favicon.ico',<br>
|
||||||
},
|
primaryColor: '#ce59d9',<br>
|
||||||
features: {
|
backgroundColor: '#faf5ff',<br>
|
||||||
enablePasswordLogin: true,
|
textColor: '#6b21a8',<br>
|
||||||
autoShowPassword: true
|
borderRadius: '12px'<br>
|
||||||
}
|
},<br>
|
||||||
|
features: {<br>
|
||||||
|
autoAuth: false,<br>
|
||||||
|
enablePasswordLogin: true,<br>
|
||||||
|
autoShowPassword: true,<br>
|
||||||
|
maxPasswordAttempts: 3<br>
|
||||||
|
},<br>
|
||||||
|
callbacks: {<br>
|
||||||
|
onFido2Success: function(username, session) {<br>
|
||||||
|
// Handle login success<br>
|
||||||
|
},<br>
|
||||||
|
onFido2Error: function(error) {<br>
|
||||||
|
// Handle error<br>
|
||||||
|
}<br>
|
||||||
|
}<br>
|
||||||
});</code>
|
});</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p id="msg_code_device">Device Manager:</p>
|
<p id="msg_code_device_default">Device Manager Default:</p>
|
||||||
<div class="code-block">
|
<div class="code-block">
|
||||||
<code>Fido2UIManager.renderDeviceManager({
|
<code>// Device Manager Default<br>
|
||||||
userId: currentUserId,
|
Fido2UIManager.renderDeviceManager({<br>
|
||||||
container: '#device-container',
|
userId: currentUserId,<br>
|
||||||
mode: 'modal',
|
container: '#device-container',<br>
|
||||||
serverUrl: SERVER_URL,
|
mode: 'modal',<br>
|
||||||
language: 'ja'
|
serverUrl: SERVER_URL,<br>
|
||||||
|
language: CURRENT_LANG<br>
|
||||||
|
});</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p id="msg_code_device_custom">Device Manager Custom:</p>
|
||||||
|
<div class="code-block">
|
||||||
|
<code>// Device Manager with Custom Style<br>
|
||||||
|
Fido2UIManager.renderDeviceManager({<br>
|
||||||
|
userId: currentUserId,<br>
|
||||||
|
container: '#device-container',<br>
|
||||||
|
mode: 'modal',<br>
|
||||||
|
serverUrl: SERVER_URL,<br>
|
||||||
|
language: 'ja',<br>
|
||||||
|
theme: {<br>
|
||||||
|
logo: 'files/favicon.ico',<br>
|
||||||
|
primaryColor: '#ce59d9',<br>
|
||||||
|
backgroundColor: '#faf5ff',<br>
|
||||||
|
textColor: '#6b21a8',<br>
|
||||||
|
borderRadius: '12px'<br>
|
||||||
|
},<br>
|
||||||
|
features: {<br>
|
||||||
|
showAddButton: true,<br>
|
||||||
|
showDeleteButton: true,<br>
|
||||||
|
showUserInfo: true,<br>
|
||||||
|
showSessionStatus: true<br>
|
||||||
|
},<br>
|
||||||
|
callbacks: {<br>
|
||||||
|
onInit: function(manager) {<br>
|
||||||
|
// Manager initialized<br>
|
||||||
|
},<br>
|
||||||
|
onDeviceAdded: function(device) {<br>
|
||||||
|
// Device added<br>
|
||||||
|
},<br>
|
||||||
|
onDeviceDeleted: function(deviceId) {<br>
|
||||||
|
// Device deleted<br>
|
||||||
|
},<br>
|
||||||
|
onClose: function() {<br>
|
||||||
|
// Manager closed<br>
|
||||||
|
}<br>
|
||||||
|
}<br>
|
||||||
});</code>
|
});</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -570,7 +627,7 @@
|
|||||||
<div id="device-container"></div>
|
<div id="device-container"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const SERVER_URL = 'https://local.dqj-macpro.com';//'https://fido2.amipro.me';
|
const SERVER_URL = 'https://fido2.amipro.me'; //'https://local.dqj-macpro.com';
|
||||||
|
|
||||||
let currentUserId = null;
|
let currentUserId = null;
|
||||||
let isLoggedIn = false;
|
let isLoggedIn = false;
|
||||||
@@ -585,7 +642,7 @@
|
|||||||
return 'en-US';
|
return 'en-US';
|
||||||
}
|
}
|
||||||
|
|
||||||
const CURRENT_LANG = getBrowserLanguage();//'https://fido2.amipro.me';
|
const CURRENT_LANG = getBrowserLanguage();
|
||||||
|
|
||||||
function enableDeviceManagementButtons() {
|
function enableDeviceManagementButtons() {
|
||||||
document.getElementById('btn-manage-devices').disabled = false;
|
document.getElementById('btn-manage-devices').disabled = false;
|
||||||
@@ -602,6 +659,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
|
Fido2UIManager.logout();
|
||||||
currentUserId = null;
|
currentUserId = null;
|
||||||
isLoggedIn = false;
|
isLoggedIn = false;
|
||||||
log('success', getI18NText(i18n_messages, 'msg_logout_success'));
|
log('success', getI18NText(i18n_messages, 'msg_logout_success'));
|
||||||
@@ -808,7 +866,7 @@
|
|||||||
const entry = document.createElement('div');
|
const entry = document.createElement('div');
|
||||||
entry.className = 'log-entry';
|
entry.className = 'log-entry';
|
||||||
|
|
||||||
const time = new Date().toLocaleTimeString('zh-CN');
|
const time = new Date().toLocaleTimeString(CURRENT_LANG);
|
||||||
const typeClass = 'log-type-' + type;
|
const typeClass = 'log-type-' + type;
|
||||||
|
|
||||||
entry.innerHTML = `<span class="log-time">[${time}]</span><span class="${typeClass}">[${type.toUpperCase()}]</span> ${message}`;
|
entry.innerHTML = `<span class="log-time">[${time}]</span><span class="${typeClass}">[${type.toUpperCase()}]</span> ${message}`;
|
||||||
@@ -825,5 +883,16 @@
|
|||||||
https://amipro.me/fido2_top.html
|
https://amipro.me/fido2_top.html
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
CSP (Content Security Policy) 示例 - 生产环境部署建议添加:
|
||||||
|
|
||||||
|
<meta http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self';
|
||||||
|
script-src 'self' 'unsafe-inline';
|
||||||
|
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
|
||||||
|
font-src 'self' https://fonts.gstatic.com;
|
||||||
|
connect-src 'self' https://your-server.com;">
|
||||||
|
-->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -149,9 +149,8 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script src="files/jquery.js"></script>
|
|
||||||
<script src="files/popper.js"></script>
|
<script src="files/popper.js"></script>
|
||||||
<script src="files/bootstrap.js"></script>
|
<script src="files/bootstrap.js"></script>
|
||||||
<script src="files/dfido2-lib.js"></script>
|
<script src="files/dfido2-lib.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user