@ideem/zsm-client-sdk 2.8.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/PluginManager.js +1 -1
- package/WASMRustInterface.js +29 -22
- package/WebAuthnClientBase.js +68 -24
- package/ZSMClientSDK.js +1 -1
- package/package.json +1 -1
package/WebAuthnClientBase.js
CHANGED
|
@@ -74,6 +74,21 @@ class WebAuthnClientBase {
|
|
|
74
74
|
if(!config?.zsm_host_url) config.zsm_host_url = config.zsm_host = config.host_url.replace("zsm-authenticator", "zsm");
|
|
75
75
|
config.host_url = config.zsm_host_url;
|
|
76
76
|
|
|
77
|
+
// [J] API key is not strictly required for the client to function, as long as the KVP exists and is in the format of a UUID.
|
|
78
|
+
// The line below ensures that if an API key is not provided, but an application_id is (which IS required), it will use the
|
|
79
|
+
// application_id in place of the absent api_key. If it - application_id - ALSO isn't provided, we DO want the calls to fail -
|
|
80
|
+
// and to do so conspicuously (as this indicates a misconfiguration) – so the default string below will be clearly visible inside
|
|
81
|
+
// of any server-side log outputs and in DevTools Network Panel requests for easy debugging (for both us and for our customers).
|
|
82
|
+
config.api_key ??= (config?.application_id??"NO APPLICATION_ID PROVIDED! CHECK YOUR zsm_app_config.json!");
|
|
83
|
+
|
|
84
|
+
// Once we are certain there's nothing else looking for api_key in the WASM/back-end/Rust code, we can delete the above line and
|
|
85
|
+
// uncomment the line below, which will remove the api_key from the config object passed to the WASM module entirely in the event
|
|
86
|
+
// it is omitted or is not in a valid UUID format. Presently, the ability to do so is contingent upon a specific column's value
|
|
87
|
+
// inside the Authenticator database: if sdk_authorization_type != "none", then an api_key remains required. Once we've updated all
|
|
88
|
+
// database records and verified the WASM/Rust code compliance, we can just delete config?.api_key here without any conditions.
|
|
89
|
+
|
|
90
|
+
// if(!config?.api_key || !/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(config.api_key)) delete config.api_key;
|
|
91
|
+
|
|
77
92
|
this.config = GLOBAL.zsmAppConfig = config;
|
|
78
93
|
|
|
79
94
|
iid4uid(this.config.consumer_id)
|
|
@@ -210,9 +225,7 @@ class WebAuthnClientBase {
|
|
|
210
225
|
|
|
211
226
|
let passkeySupported = false; // [J] Best-effort detection of platform authenticator support
|
|
212
227
|
try {
|
|
213
|
-
passkeySupported = !!(
|
|
214
|
-
&& navigator?.credentials
|
|
215
|
-
&& await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable());
|
|
228
|
+
passkeySupported = !!(navigator?.credentials);
|
|
216
229
|
} catch { /* unsupported environment */ }
|
|
217
230
|
|
|
218
231
|
this.credentialID = localData['zsm_credential_id']??null;
|
|
@@ -425,7 +438,26 @@ class WebAuthnClientBase {
|
|
|
425
438
|
|
|
426
439
|
/**
|
|
427
440
|
* @name unbindFromDevice
|
|
428
|
-
* @description Removes the device binding and stored account data for userIdentifier
|
|
441
|
+
* @description Removes the device binding and stored account data for userIdentifier.
|
|
442
|
+
*
|
|
443
|
+
* Enrollment writes several "ideem"/"zsm" IDB entries for a single user:
|
|
444
|
+
* the uid→iid mapping (key = user identifier), iid-valued string entries
|
|
445
|
+
* (key = "", key = "<hash>/equivalence"), and a credential object
|
|
446
|
+
* (key = "<hash>", value with `zsm_credential` as a Map of WebAuthn
|
|
447
|
+
* fields). The cursor walk below matches each shape: condition (a) for
|
|
448
|
+
* uid-keyed entries, condition (b) for iid-valued strings, condition
|
|
449
|
+
* (c) for the credential Map. Missing any one leaves zombie entries
|
|
450
|
+
* that subsequent checkEnrollment finds.
|
|
451
|
+
*
|
|
452
|
+
* The IDB operations are wrapped in an awaited Promise so the function
|
|
453
|
+
* returns only after the transaction commits — without this, callers
|
|
454
|
+
* that immediately call checkEnrollment race the cursor's deletes and
|
|
455
|
+
* read stale entries. The zombie-then-stale-read pattern was invisible
|
|
456
|
+
* when install_id was stable per device (the survivors stayed
|
|
457
|
+
* self-consistent across sessions) but becomes a silent failure when
|
|
458
|
+
* install_id varies per session, because the WASM's webauthn_retrieve
|
|
459
|
+
* lookup keys depend on it and finds the prior session's residue.
|
|
460
|
+
*
|
|
429
461
|
* @param {String} userIdentifier The Customer-Defined Identifier of the user whose account is to be removed
|
|
430
462
|
* @returns {Promise<Boolean>} Returns true if the user's account was present (and subsequently removed from the device)
|
|
431
463
|
* @returns {Promise<Boolean>} Returns false if there is no credential matching userIdentifier stored on the device
|
|
@@ -442,27 +474,39 @@ class WebAuthnClientBase {
|
|
|
442
474
|
|
|
443
475
|
const rawID = await this.webauthnRetrieve(userIdentifier);
|
|
444
476
|
if (!rawID) return false;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
477
|
+
const userIid = await iid4uid(userIdentifier);
|
|
478
|
+
|
|
479
|
+
await new Promise((resolve, reject) => {
|
|
480
|
+
const request = indexedDB.open("ideem", 1);
|
|
481
|
+
request.onerror = () => reject(new Error(`[WebAuthnClient] :: unbindFromDevice :: Unable to open indexedDB for ${userIdentifier}`));
|
|
482
|
+
request.onsuccess = (event) => {
|
|
483
|
+
const db = event.target.result;
|
|
484
|
+
const tx = db.transaction("zsm", "readwrite");
|
|
485
|
+
const store = tx.objectStore("zsm");
|
|
486
|
+
tx.oncomplete = () => resolve();
|
|
487
|
+
tx.onerror = () => reject(new Error(`[WebAuthnClient] :: unbindFromDevice :: transaction failed for ${userIdentifier}`));
|
|
488
|
+
tx.onabort = () => reject(new Error(`[WebAuthnClient] :: unbindFromDevice :: transaction aborted for ${userIdentifier}`));
|
|
489
|
+
const cursorRequest = store.openCursor();
|
|
490
|
+
cursorRequest.onerror = () => reject(new Error(`[WebAuthnClient] :: unbindFromDevice :: cursor failed for ${userIdentifier}`));
|
|
491
|
+
cursorRequest.onsuccess = (event) => {
|
|
492
|
+
const cursor = event.target.result;
|
|
493
|
+
if (cursor) {
|
|
494
|
+
const { key, value } = cursor;
|
|
495
|
+
const matchesStringValue = typeof value === 'string'
|
|
496
|
+
&& (value.includes(userIdentifier) || value === userIid);
|
|
497
|
+
const matchesCredential = typeof value === 'object'
|
|
498
|
+
&& value !== null
|
|
499
|
+
&& value.zsm_credential instanceof Map
|
|
500
|
+
&& value.zsm_credential.get('rawId') === rawID;
|
|
501
|
+
if (key.includes(userIdentifier) || matchesStringValue || matchesCredential) {
|
|
502
|
+
store.delete(key);
|
|
503
|
+
}
|
|
504
|
+
cursor.continue();
|
|
505
|
+
}
|
|
506
|
+
};
|
|
461
507
|
};
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
request.onerror = (err) => { throw new Error(`[WebAuthnClient] :: unbindFromDevice :: Unable to open indexedDB for ${userIdentifier}\n${err}`); };
|
|
465
|
-
this.webauthnRetrieve(userIdentifier);
|
|
508
|
+
});
|
|
509
|
+
|
|
466
510
|
this.status = 'UNENROLL_COMPLETE';
|
|
467
511
|
return true;
|
|
468
512
|
}
|
package/ZSMClientSDK.js
CHANGED
|
@@ -37,7 +37,7 @@ zsmPluginManager.registerPlugin('Core', {
|
|
|
37
37
|
mode: 'OVERRIDE'
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
const ZSMClientSDKVersion = '2.
|
|
40
|
+
const ZSMClientSDKVersion = '2.9.0';
|
|
41
41
|
const ZSMVersions = GLOBAL.ZSMVersions = (GLOBAL.ZSMVersions) ? {...GLOBAL.ZSMVersions, CORE: ZSMClientSDKVersion} : { CORE: ZSMClientSDKVersion }
|
|
42
42
|
|
|
43
43
|
export { UMFAClient, FIDO2Client, ZSMVersions };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name" : "@ideem/zsm-client-sdk",
|
|
3
|
-
"version" : "2.
|
|
3
|
+
"version" : "2.9.0",
|
|
4
4
|
"description" : "ZSM makes 2FA easy and invisible for everyone, all the time, using advanced cryptography like MPC to establish cryptographic proof of the origin of any transaction or login attempt, while eliminating opportunities for social engineering. ZSM has no reliance on specific hardware. Our cryptographic module has completed FIPS 140-3 NIST approved lab testing, is FIDO2-ready, and is on the Modules In Progress (MIP) list. Superior Usability. no opt-in, no codes, no downloads, no authenticator apps. Better Security.",
|
|
5
5
|
"keywords" : [ "ideem", "zsm", "zsm-client-sdk", "umfaclient", "fido2client", "library", "umfa", "fido", "fido2", "client", "javascript", "multifactor", "multi-factor", "webauthn", "authentication", "security", "cryptography", "mpc", "fips", "nist", "mip", "passkeys" ],
|
|
6
6
|
"author" : "Jason Jurusz <jj@useideem.com>",
|