@ideem/zsm-client-sdk 2.4.0 → 2.4.1
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/{RELEASE_NOTES.txt → CHANGELOG.md} +11 -3
- package/ZSMClientSDK.js +41 -19
- package/package.json +2 -2
- package/worker.js +14 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
ZSM Client SDK for Browser Apps
|
|
2
|
-
Latest version: v2.
|
|
3
|
-
Latest release date:
|
|
2
|
+
Latest version: v2.4.1
|
|
3
|
+
Latest release date: May 5, 2025
|
|
4
4
|
|
|
5
5
|
The ZSM Client SDK for Browser Apps provides the resources needed to integrate Ideem's FIDO2 Authenticator
|
|
6
6
|
and Universal MFA into a browser application.
|
|
@@ -15,7 +15,15 @@ and Universal MFA into a browser application.
|
|
|
15
15
|
|
|
16
16
|
----
|
|
17
17
|
|
|
18
|
-
## Release 2.4.
|
|
18
|
+
## Release 2.4.1 - May 5, 2025 [ NPM PACKAGE RELEASE ] **[ LATEST ]**
|
|
19
|
+
- Minor bug fixes and improvements
|
|
20
|
+
- Updates to documentation
|
|
21
|
+
- Updates to sample apps
|
|
22
|
+
- Alters worker communication protocol to ensure that the worker is not blocked
|
|
23
|
+
by the main thread and vice versa, while still ensuring that the initialization
|
|
24
|
+
of the worker is completed before any other operations are performed.
|
|
25
|
+
|
|
26
|
+
## Release 2.4.0 - May 1, 2025
|
|
19
27
|
- Minor bug fixes and improvements
|
|
20
28
|
- Updates to documentation
|
|
21
29
|
- Adds event emitters to the UMFAClient and FIDO2Client for on-the-fly initializations
|
package/ZSMClientSDK.js
CHANGED
|
@@ -21,10 +21,23 @@ class WorkerError extends Error {
|
|
|
21
21
|
|
|
22
22
|
class ZSMClientBase {
|
|
23
23
|
constructor(config) {
|
|
24
|
-
|
|
24
|
+
GLOBAL.zsmClient = null;
|
|
25
25
|
this.clientWorker = null;
|
|
26
26
|
this.timeoutTimer = null;
|
|
27
|
-
|
|
27
|
+
this.clientType = null;
|
|
28
|
+
this.ready = false;
|
|
29
|
+
this.config = config;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
onReady(onReadyFn=null) {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
if(this.ready) return resolve(true);
|
|
35
|
+
window.addEventListener(`${this.clientType}Ready`, async (e) => {
|
|
36
|
+
this.ready = true;
|
|
37
|
+
if(onReadyFn) await onReadyFn?.();
|
|
38
|
+
resolve(this);
|
|
39
|
+
}, { once: true });
|
|
40
|
+
});
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
/**
|
|
@@ -35,7 +48,10 @@ class ZSMClientBase {
|
|
|
35
48
|
* @returns {Promise<ZSMClientBase>} A promise that resolves to the initialized ZSMClient instance.
|
|
36
49
|
*/
|
|
37
50
|
async init(clientType='UMFAClient', config=this.config) {
|
|
38
|
-
|
|
51
|
+
this.onReady();
|
|
52
|
+
this.ready = false;
|
|
53
|
+
this.clientType = clientType;
|
|
54
|
+
|
|
39
55
|
const workerPath = GLOBAL?.workerPath ? GLOBAL.workerPath
|
|
40
56
|
: config?.workerPath ? config.workerPath
|
|
41
57
|
: './worker.js';
|
|
@@ -58,6 +74,11 @@ class ZSMClientBase {
|
|
|
58
74
|
* @returns {Promise<Object>} A promise that resolves with the result of the worker function.
|
|
59
75
|
*/
|
|
60
76
|
async callWorkerFunction(functionName, data, responseExpected=true) {
|
|
77
|
+
if (functionName !== 'init' && !this.ready) await this.onReady();
|
|
78
|
+
if(!this.clientWorker) {
|
|
79
|
+
console.error('Worker not initialized. Please call init() before using this method.');
|
|
80
|
+
return Promise.reject(new WorkerError('Worker', 'Worker not initialized. Please call init() before using this method.'));
|
|
81
|
+
}
|
|
61
82
|
let workerPromise = new Promise((resolve, reject) => {
|
|
62
83
|
this.clientWorker.postMessage({functionName, data });
|
|
63
84
|
if(responseExpected) this.timeoutTimer = GLOBAL.setTimeout(() => reject(new WorkerError('Transaction', 'Transaction timed out after 60 seconds!')), 60000);
|
|
@@ -67,7 +88,7 @@ class ZSMClientBase {
|
|
|
67
88
|
|
|
68
89
|
let workerResponse = event.data;
|
|
69
90
|
|
|
70
|
-
if(GLOBAL.showWorkerTelemetry && (workerResponse.result || workerResponse.error)) {
|
|
91
|
+
if((GLOBAL.showWorkerTelemetry) && (workerResponse.result || workerResponse.error)) {
|
|
71
92
|
console.groupCollapsed(`%cWorker posted to Main Thread a response to invocation:`, 'font-weight:bold; color:#F60;', functionName)
|
|
72
93
|
console.log(`%c Function Invoked:`, 'font-weight:100; color:#AAA;', workerResponse.functionName);
|
|
73
94
|
console.log(`%c Success Status:`, 'font-weight:100; color:#AAA;', workerResponse.success ? 'true' : 'false');
|
|
@@ -110,31 +131,32 @@ class ZSMClientBase {
|
|
|
110
131
|
class UMFAClient extends ZSMClientBase {
|
|
111
132
|
constructor(config) {
|
|
112
133
|
super(config);
|
|
134
|
+
this.clientType = 'UMFAClient';
|
|
113
135
|
this.init = super.init.bind(this);
|
|
114
136
|
this.callWorkerFunction = super.callWorkerFunction.bind(this);
|
|
137
|
+
// this.onReady = super.onReady.bind(this);
|
|
115
138
|
this.checkEnrollment = this.checkEnrollment.bind(this);
|
|
116
139
|
this.enroll = this.enroll.bind(this);
|
|
117
140
|
this.authenticate = this.authenticate.bind(this);
|
|
118
|
-
this.
|
|
119
|
-
this.optimizedAuthenticate = this.optimizedAuthenticate.bind(this);
|
|
141
|
+
this.unenroll = this.unenroll.bind(this);
|
|
120
142
|
this.init('UMFAClient');
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
async optimizedAuthenticate(user) { return await this.callWorkerFunction('optimizedAuthenticate', user); }
|
|
128
|
-
async unenroll(user) { return await this.callWorkerFunction('unenroll', user); }
|
|
145
|
+
checkEnrollment(user) { return this.callWorkerFunction('checkEnrollment', user); }
|
|
146
|
+
enroll(user) { return this.callWorkerFunction('enroll', user); }
|
|
147
|
+
authenticate(user) { return this.callWorkerFunction('authenticate', user); }
|
|
148
|
+
unenroll(user) { return this.callWorkerFunction('unenroll', user); }
|
|
129
149
|
}
|
|
130
150
|
|
|
131
151
|
class FIDO2Client extends ZSMClientBase {
|
|
132
152
|
constructor(config) {
|
|
133
153
|
super(config);
|
|
154
|
+
this.clientType = 'FIDO2Client';
|
|
134
155
|
this.init = super.init.bind(this);
|
|
135
156
|
this.callWorkerFunction = super.callWorkerFunction.bind(this);
|
|
136
|
-
this.
|
|
157
|
+
this.onReady = super.onReady.bind(this);
|
|
137
158
|
this.checkIdentity = this.checkIdentity.bind(this);
|
|
159
|
+
this.checkEnrollment = this.checkEnrollment.bind(this);
|
|
138
160
|
this.webauthnCreate = this.webauthnCreate.bind(this);
|
|
139
161
|
this.webauthnGet = this.webauthnGet.bind(this);
|
|
140
162
|
this.webauthnRetrieve = this.webauthnRetrieve.bind(this);
|
|
@@ -142,12 +164,12 @@ class FIDO2Client extends ZSMClientBase {
|
|
|
142
164
|
this.init('FIDO2Client');
|
|
143
165
|
}
|
|
144
166
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
checkIdentity = (user, auto=true) => this.callWorkerFunction('checkIdentity', [user, auto]);
|
|
168
|
+
checkEnrollment = (user) => this.callWorkerFunction('checkEnrollment', user);
|
|
169
|
+
webauthnCreate = (user) => this.callWorkerFunction('webauthnCreate', user);
|
|
170
|
+
webauthnGet = (user) => this.callWorkerFunction('webauthnGet', user);
|
|
171
|
+
webauthnRetrieve = (user) => this.callWorkerFunction('webauthnRetrieve', user);
|
|
172
|
+
webauthnDelete = (user) => this.callWorkerFunction('webauthnDelete', user);
|
|
151
173
|
}
|
|
152
174
|
|
|
153
175
|
function amendPerformanceLoggingToClass(cls) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ideem/zsm-client-sdk",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.1",
|
|
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
|
"author": "@JJ (Jason Jurusz) <jj@useideem.com>/<nerdy.deeds@done.cheap>",
|
|
6
6
|
"keywords": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"files": [
|
|
41
41
|
"worker.js",
|
|
42
42
|
"ZSMClientSDK.js",
|
|
43
|
-
"
|
|
43
|
+
"CHANGELOG.md",
|
|
44
44
|
"LICENSE",
|
|
45
45
|
"README.md"
|
|
46
46
|
],
|
package/worker.js
CHANGED
|
@@ -16,12 +16,13 @@ GLOBAL.clientsReadyDash = {
|
|
|
16
16
|
|
|
17
17
|
GLOBAL.checkReadiness = (client=null, status=null) => {
|
|
18
18
|
let retVal = false;
|
|
19
|
-
if(GLOBAL.showWorkerTelemetry) console.log('GLOBAL.checkReadiness(client, status):', client, status, Object.values(GLOBAL.clientsReadyDash).reduce((c,i)=>!!c && !!i, true));
|
|
20
19
|
if(status != null) retVal = (GLOBAL.clientsReadyDash[client] = status);
|
|
21
20
|
else if(client != null) retVal = GLOBAL.clientsReadyDash[client];
|
|
21
|
+
if(GLOBAL.showWorkerTelemetry) console.log('GLOBAL.checkReadiness(client, status):', client, status, '\n Fully initialized?', Object.values(GLOBAL.clientsReadyDash).every(v=>v));
|
|
22
22
|
|
|
23
23
|
const allClientsReady = retVal = Object.values(GLOBAL.clientsReadyDash).every(v=>v);
|
|
24
24
|
if(allClientsReady) {
|
|
25
|
+
GLOBAL.dispatchEvent(new CustomEvent(`${GLOBAL.clientType}Ready`, {bubbles: true, cancelable: false, detail: true }));
|
|
25
26
|
GLOBAL.dispatchEventToMain(`${GLOBAL.clientType}Ready`, true);
|
|
26
27
|
GLOBAL.checkReadiness = () => true;
|
|
27
28
|
}
|
|
@@ -150,7 +151,7 @@ GLOBAL.uid2iid = (uid, iid) => {
|
|
|
150
151
|
idbInstance.onsuccess = (e, db=e.target.result) => {
|
|
151
152
|
const zsmTbl = db.transaction("zsm", "readwrite");
|
|
152
153
|
const write = zsmTbl.objectStore("zsm").put(iid, uid);
|
|
153
|
-
write.onsuccess = () => (db.close()
|
|
154
|
+
write.onsuccess = () => (db.close());
|
|
154
155
|
write.onerror = (err=write.error) => (db.close(), console.error(`Unable to bind UID to IID:\n${err}`));
|
|
155
156
|
};
|
|
156
157
|
}
|
|
@@ -1687,7 +1688,6 @@ class RelyingParty {
|
|
|
1687
1688
|
* @returns {Promise<Object>} Resolves with the response data.
|
|
1688
1689
|
*/
|
|
1689
1690
|
makePostRequest = (url, body={}, headers={}, method='POST') => {
|
|
1690
|
-
console.log('this.customerDefinedIdentifier :', this.customerDefinedIdentifier);
|
|
1691
1691
|
url = `${this.host.endsWith('/') ? this.host.slice(0,-1) : this.host}/${url.startsWith('/') ? url.slice(1) : url}`;
|
|
1692
1692
|
body = ((typeof body === 'object') ? body : {});
|
|
1693
1693
|
body = Object.assign({customer_defined_identifier: this.customerDefinedIdentifier, application_id: this.applicationId}, body);
|
|
@@ -1885,8 +1885,9 @@ class WebAuthnClient {
|
|
|
1885
1885
|
|
|
1886
1886
|
if(!config?.authenticator_host_url) config.authenticator_host = config.authenticator_host_url = config.host_url;
|
|
1887
1887
|
if(!config?.zsm_host_url) config.zsm_host_url = config.zsm_host = config.host_url.replace("zsm-authenticator", "zsm");
|
|
1888
|
+
config.host_url = config.zsm_host_url;
|
|
1888
1889
|
|
|
1889
|
-
this.config = config;
|
|
1890
|
+
this.config = GLOBAL.zsmAppConfig = config;
|
|
1890
1891
|
GLOBAL.iid4uid(this.config.consumer_id)
|
|
1891
1892
|
.then(v=>{
|
|
1892
1893
|
GLOBAL.mpcConfig = Object.assign({}, config, {consumer_id: v});
|
|
@@ -1935,11 +1936,13 @@ class WebAuthnClient {
|
|
|
1935
1936
|
*/
|
|
1936
1937
|
initializeZsm = async () => {
|
|
1937
1938
|
if(GLOBAL.mpcConfig.consumer_id !== this.MPCConfig.consumer_id) {
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1939
|
+
// console.log('GLOBAL.mpcConfig :', Object.assign({},GLOBAL.mpcConfig));
|
|
1940
|
+
let xyz = await this.zsmApi.configure(GLOBAL.mpcConfig);
|
|
1941
|
+
await setTimeout(() => Promise.resolve(), 5000);
|
|
1942
|
+
this.MPCConfig = await Object.assign({}, GLOBAL.mpcConfig);
|
|
1943
|
+
await GLOBAL.uid2iid(this.config.consumer_id, this.MPCConfig.consumer_id);
|
|
1941
1944
|
}
|
|
1942
|
-
return this.zsmApi;
|
|
1945
|
+
return await this.zsmApi;
|
|
1943
1946
|
}
|
|
1944
1947
|
|
|
1945
1948
|
/**
|
|
@@ -2244,7 +2247,7 @@ class WebAuthnClient {
|
|
|
2244
2247
|
await this.initializeZsm('unbindFromDevice');
|
|
2245
2248
|
|
|
2246
2249
|
const rawID = await this.webauthnRetrieve(userIdentifier);
|
|
2247
|
-
if (!rawID)
|
|
2250
|
+
if (!rawID) return false;
|
|
2248
2251
|
|
|
2249
2252
|
const request = indexedDB.open("ideem", 1);
|
|
2250
2253
|
request.onsuccess = function(event) {
|
|
@@ -2287,6 +2290,8 @@ GLOBAL.UMFAClient = class UMFAClient {
|
|
|
2287
2290
|
this.authenticate = this.authenticate.bind(this);
|
|
2288
2291
|
this.resetDevice = this.resetDevice.bind(this);
|
|
2289
2292
|
this.unenroll = this.unenroll.bind(this);
|
|
2293
|
+
this.loaded = false;
|
|
2294
|
+
GLOBAL.addEventListener(`${GLOBAL.clientType}Ready`, (e) => this.loaded=true);
|
|
2290
2295
|
GLOBAL.dispatchEvent(new CustomEvent("ActiveClientReady", {bubbles: true, cancelable: false, detail: true }));
|
|
2291
2296
|
}
|
|
2292
2297
|
|