@ideem/zsm-client-sdk 2.5.0 → 2.5.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/UMFAClientBase.js CHANGED
@@ -8,7 +8,6 @@ class UMFAClientBase {
8
8
  * @param {Object} config - The configuration for ZSM initialization (ref: ./zsm_app_config.json)
9
9
  */
10
10
  constructor(config) {
11
- if(eventCoordinator.done) eventCoordinator.reset();
12
11
  eventCoordinator.update('UMFAClientBase');
13
12
  this.config = config;
14
13
  const WebAuthnClient = zsmPluginManager.classes('WEBAUTHNCLIENT');
@@ -24,6 +23,16 @@ class UMFAClientBase {
24
23
  get userIdentifier() { return this.zsmAPI.userIdentifier; }
25
24
  get credentialID() { return this.zsmAPI.credentialID; }
26
25
 
26
+ /**
27
+ * @name checkIdentity
28
+ * @description Pass-through method to call WebAuthnClient's checkIdentity
29
+ * @param {string} userIdentifier The identifier for the user
30
+ * @param {boolean} primeEnroll Whether to create a new identity if it doesn't exist
31
+ * @returns {Promise<Object>} Returns the results of zsmAPI.checkIdentity
32
+ * @memberOf FIDO2ClientBase, WebAuthnClient
33
+ */
34
+ async checkIdentity (userIdentifier, primeEnroll) { return this.zsmAPI.checkIdentity(userIdentifier, primeEnroll); }
35
+
27
36
  /**
28
37
  * @name checkEnrollment
29
38
  * @description Checks if a ZSM credential is enrolled on the device for the specified user
@@ -57,14 +66,10 @@ class UMFAClientBase {
57
66
  try {
58
67
  if(userIdentifier == null
59
68
  || userIdentifier === '') throw new Error(`[UMFAClient] :: enroll :: A userIdentifier String is required! Received: ${userIdentifier}.`);
60
-
61
- const userIsEnrolled = await this.checkEnrollment(userIdentifier);
62
- if(userIsEnrolled !== false) return false;
63
-
64
- const createResult = await this.zsmAPI.webauthnCreateThenPartialGet(userIdentifier);
69
+ await this.zsmAPI.checkIdentity(userIdentifier, true);
70
+ const createResult = await this.zsmAPI.webauthnCreate(userIdentifier);
65
71
  if(createResult instanceof Error) throw(`[UMFAClient] :: enroll :: Unable to associate ${userIdentifier}'s identity with device profile.\nDetails:\n${createResult}`);
66
- if(!createResult?.credential) throw(`[UMFAClient] :: enroll :: Unable to acquire authentication token for ${userIdentifier}!`);
67
- return createResult.credential;
72
+ return createResult;
68
73
  }catch(e) {
69
74
  return new Error(e.message || e || '[UMFAClient] :: enroll :: An error occurred during enrollment.');
70
75
  }
@@ -89,7 +89,7 @@ class WebAuthnClientBase {
89
89
  * @param {Object} config The configuration object for ZSM initialization.
90
90
  * @returns {Promise<ZSMAPI>} Resolves with the initialized ZSM API.
91
91
  */
92
- async getZsmApi (config) {}
92
+ getZsmApi = async (config) => {}
93
93
 
94
94
  /**
95
95
  * @name primeZsmApi
@@ -105,7 +105,7 @@ class WebAuthnClientBase {
105
105
  * @description Reconfigures the ZSM API, should the consumer_id change (as after an identity_id is obtained)
106
106
  * @returns {Promise<ZSMAPI>} Resolves when the ZSM API is reconfigured.
107
107
  */
108
- async initializeZsm () {
108
+ initializeZsm = async () => {
109
109
  if(GLOBAL.mpcConfig.consumer_id !== this.MPCConfig.consumer_id) {
110
110
  // console.log('GLOBAL.mpcConfig :', Object.assign({},GLOBAL.mpcConfig));
111
111
  await this.zsmApi.configure(GLOBAL.mpcConfig);
@@ -135,7 +135,7 @@ class WebAuthnClientBase {
135
135
  uid2iid(userIdentifier, identityID);
136
136
  GLOBAL.mpcConfig.consumer_id = identityID;
137
137
  this.status = 'CHECKING_IDENTITY_FINISHED';
138
- return Promise.resolve(identityID);
138
+ return identityID;
139
139
  } catch(err) {
140
140
  this.status = 'CHECKING_IDENTITY_FAILED';
141
141
  return Promise.reject(err);
@@ -158,6 +158,8 @@ class WebAuthnClientBase {
158
158
  this.status = 'ENROLLMENT_STARTING';
159
159
  await this.initializeZsm('webauthnCreate');
160
160
 
161
+ // await this.checkIdentity(userIdentifier, true);
162
+
161
163
  const publicKey = await this.relyingParty.registrationStart();
162
164
  if(!publicKey) throw new Error('[WebAuthnClient] :: webauthnCreate :: Unable to retrieve public key from Relying Party Server!');
163
165
 
@@ -190,7 +192,7 @@ class WebAuthnClientBase {
190
192
  * @throws {Error} Throws an error if unable to verify the credential with the Relying Party Server.
191
193
  * @throws {Error} Throws an error if the user ID or token is missing from the response body.
192
194
  */
193
- async webauthnGet (userIdentifier) {
195
+ webauthnGet = async (userIdentifier) => {
194
196
  this.status = 'AUTHENTICATION_STARTING';
195
197
 
196
198
  await this.initializeZsm('webauthnGet');
@@ -247,14 +249,14 @@ class WebAuthnClientBase {
247
249
  * @returns {Promise<Boolean>} Resolves with the credential ID or `false` if retrieval fails.
248
250
  * @returns {Promise<String>} Resolves with the retrieved rawID for the specified user.
249
251
  */
250
- async webauthnRetrieve (userIdentifier, silent=false) {
252
+ webauthnRetrieve = async (userIdentifier, silent=false) => {
251
253
  this.credentialID = null;
252
254
  if (!silent) {
253
255
  this.status = 'CREDENTIAL_RETRIEVING';
254
256
  }
255
257
 
256
258
  try {
257
- await this.initializeZsm('webauthnRetrieve');
259
+ this.initializeZsm('webauthnRetrieve');
258
260
  let credentialIdentifier = await iid4uid(userIdentifier);
259
261
  GLOBAL.mpcConfig.consumer_id = credentialIdentifier;
260
262
 
@@ -275,67 +277,6 @@ class WebAuthnClientBase {
275
277
  }
276
278
  };
277
279
 
278
- /**
279
- * @name webauthnCreateThenPartialGet
280
- * @description UMFA-OPTIMIZED FLOW: Creates a new WebAuthn credential and then performs a partial get operation.
281
- * @description This is used to create a new credential and then immediately retrieve it for authentication.
282
- * @param {string} userIdentifier The user identifier.
283
- * @returns {Promise<Object>} Resolves with the created credential.
284
- * @throws {Error} Throws an error if unable to retieve public key/regFinishAuthStart from the Relying Party Server.
285
- * @throws {Error} Throws an error if unable to obtain the createResponse/credential from the Crypto Server.
286
- */
287
- async webauthnCreateThenPartialGet (userIdentifier) {
288
- this.status = 'ENROLLMENT_STARTING';
289
-
290
- const returnObj = await this.relyingParty.createIdentityThenRegistrationStart();
291
- if (!returnObj) throw new Error('[WebAuthnClient] :: webauthnCreateThenPartialGet :: Unable to retrieve public key from Relying Party Server!');
292
-
293
- const {publicKey, identity_id} = returnObj;
294
- await this.initializeZsm('webauthnCreateThenPartialGet');
295
-
296
- let createResponse = await this.zsmApi.webauthn_create(publicKey, tracePrimer());
297
- if (!createResponse) throw new Error('[WebAuthnClient - mpc_round] :: webauthnCreateThenPartialGet :: Unable to obtain response from Crypto Server!');
298
-
299
- createResponse = toObject(createResponse);
300
-
301
- GLOBAL.dispatchEvent(new CustomEvent("MPCResponse", {bubbles: true, cancelable: false, detail: createResponse }));
302
-
303
- if(!createResponse?.result) throw new Error('[WebAuthnClient - mpc_round] :: webauthnCreateThenPartialGet :: Unable to obtain credential from Crypto Server!');
304
-
305
- const createCredential = createResponse.result;
306
-
307
- let regFinishAuthStartResponse = await this.relyingParty.registrationFinishAuthenticationStart(createCredential);
308
- if (!regFinishAuthStartResponse) throw new Error('[WebAuthnClient] :: webauthnCreateThenPartialGet :: Unable to retrieve response from Relying Party Server!');
309
-
310
- regFinishAuthStartResponse = toObject(regFinishAuthStartResponse);
311
-
312
- if(!regFinishAuthStartResponse?.rcr || !regFinishAuthStartResponse?.rcr?.publicKey) throw new Error('[WebAuthnClient] :: webauthnCreateThenPartialGet :: Unable to retrieve public key from Relying Party Server!');
313
-
314
- const getChallenge = regFinishAuthStartResponse.rcr.publicKey;
315
-
316
- let getResponse = await this.zsmApi.webauthn_get(getChallenge, tracePrimer());
317
- if(!getResponse) throw new Error('[WebAuthnClient - mpc_round] :: webauthnGet :: Unable to obtain response from Crypto Server!');
318
-
319
- getResponse = toObject(getResponse);
320
-
321
- if(!getResponse?.result) throw new Error('[WebAuthnClient - mpc_round] :: webauthnGet :: Unable to obtain credential from Crypto Server!');
322
-
323
- GLOBAL.dispatchEvent(new CustomEvent("MPCResponse", {bubbles: true, cancelable: false, detail: getResponse }));
324
-
325
-
326
- const getCredential = getResponse.result;
327
- let op = {
328
- user_id : userIdentifier,
329
- credential : getCredential,
330
- signature : getCredential.response.signature,
331
- signedChallenge : getCredential.response.clientDataJSON,
332
- challenge : getChallenge.challenge,
333
- };
334
-
335
- return op;
336
-
337
- };
338
-
339
280
  /**
340
281
  * @name webauthnPartialGet
341
282
  * @description UMFA-OPTIMIZED FLOW: Performs a partial get operation for WebAuthn authentication.
@@ -347,7 +288,7 @@ class WebAuthnClientBase {
347
288
  * @throws {Error} Throws an error if unable to obtain the getResponse/credential from the Crypto Server.
348
289
  * @emits {CustomEvent} Emits a "MPCResponse" event with the getResponse data.
349
290
  */
350
- async webauthnPartialGet (userIdentifier) {
291
+ webauthnPartialGet = async (userIdentifier) => {
351
292
  this.status = 'AUTHENTICATION_STARTING';
352
293
  await this.initializeZsm('webauthnPartialGet');
353
294
 
@@ -389,7 +330,7 @@ class WebAuthnClientBase {
389
330
  * @throws {Error<CredentialError>} Throws an CredentialError if the credential is missing for the userIdentifier provided
390
331
  * @throws {Error<UnenrollError>} Throws an UnenrollError if a credential matching userIdentifier IS stored on the device, but was unable to be removed.
391
332
  */
392
- async unbindFromDevice (userIdentifier) {
333
+ unbindFromDevice = async (userIdentifier) => {
393
334
  this.status = 'UNENROLL_STARTING';
394
335
  if(userIdentifier !== this.userIdentifier) return false;
395
336
  await this.initializeZsm('unbindFromDevice');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name" : "@ideem/zsm-client-sdk",
3
- "version" : "2.5.0",
3
+ "version" : "2.5.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" : ["ideem", "zsm", "zsm-client-sdk", "UMFAClient", "FIDO2Client", "library", "UMFA", "FIDO2", "client", "authentication", "javascript", "multi-factor", "webauthn", "authentication", "security", "cryptography", "MPC", "FIPS", "NIST", "FIDO2-ready", "Modules In Progress", "MIP"],
@@ -1,96 +0,0 @@
1
- // @ideem/zsm-client-sdk/shims/VanillaJSLoader.js (ESM; single-tag, passive loader)
2
-
3
- /* ---------- tiny helpers ---------- */
4
- const $tag = () =>
5
- document.querySelector('script[type="module"][src$="shims/VanillaJSLoader.js"]') ||
6
- document.currentScript ||
7
- null;
8
-
9
- const join = (base, rel) => new URL(String(rel).replace(/^\.\//, ""), base).href;
10
-
11
- function computeNodeModulesBase({ allowGuessAtRoot = true } = {}) {
12
- const el = $tag();
13
-
14
- // 1) Explicit overrides (absolute or relative to page)
15
- const override = el?.dataset?.nodeModules || window.IDEEM_NODE_MODULES;
16
- if (override) return new URL(override, document.baseURI).href.replace(/\/+$/, "/");
17
-
18
- // 2) Infer from the actual URL this loader was fetched from (or its tag src)
19
- const candidates = [
20
- (typeof import.meta !== "undefined" && import.meta && import.meta.url) ? import.meta.url : null,
21
- el?.src || null,
22
- ];
23
-
24
- for (const c of candidates) {
25
- if (!c) continue;
26
- try {
27
- const href = new URL(c, document.baseURI).href;
28
- const m = href.match(/^(.*\/node_modules\/)/i);
29
- if (m) return m[1];
30
- } catch { /* ignore */ }
31
- }
32
-
33
- // 3) Optional last-ditch guess at site root
34
- if (allowGuessAtRoot) return new URL("/node_modules/", document.baseURI).href;
35
-
36
- throw new TypeError(
37
- '[VanillaJSLoader] :: Could not determine the `/node_modules/` base path.\n' +
38
- 'Pass the path to your `node_modules/` folder via either a `data-node-modules="<URL>"` attribute on the loader <script> ' +
39
- 'or set window.IDEEM_NODE_MODULES="<URL>" before including the shim <script>.'
40
- );
41
- }
42
-
43
- function injectImportMap(imports) {
44
- const el = document.createElement("script");
45
- el.type = "importmap";
46
- el.textContent = JSON.stringify({ imports }, null, 2);
47
- document.head.appendChild(el);
48
- }
49
-
50
- async function ensureImportMapSupport() {
51
- const hasNative =
52
- typeof HTMLScriptElement !== "undefined" &&
53
- "supports" in HTMLScriptElement &&
54
- HTMLScriptElement.supports?.("importmap");
55
-
56
- if (hasNative) return; // synchronous path on modern browsers
57
-
58
- // Load shim from the same shims/ folder as this file, then proceed
59
- const s = document.createElement("script");
60
- s.defer = true;
61
- s.src = new URL("./es-module-shims.js", import.meta.url).href;
62
- document.head.appendChild(s);
63
- await new Promise((res, rej) => {
64
- s.addEventListener("load", res, { once: true });
65
- s.addEventListener("error", () => rej(new Error("Failed to load es-module-shims")));
66
- });
67
- }
68
-
69
- /* ---------- first-party plugin registry ---------- */
70
- const PLUGINS = [
71
- { pkg: "@ideem/plugins.passkeys-plus", entry: "PasskeysPlus.js" },
72
- { pkg: "@ideem/plugins.perf-logger", entry: "index.js" },
73
- { pkg: "@ideem/plugins.worker-thread", entry: "WorkerThread.js" },
74
- ];
75
-
76
- /* ---------- main ---------- */
77
- (async function main() {
78
- // Only async if we need to load the shim; users never touch promises.
79
- await ensureImportMapSupport();
80
-
81
- const base = computeNodeModulesBase(); // or { allowGuessAtRoot: true } if you prefer
82
-
83
- // Core SDK (adjust entry filename if different)
84
- const imports = {
85
- "@ideem/zsm-client-sdk": join(base, "@ideem/zsm-client-sdk/ZSMClientSDK.js"),
86
- "@ideem/zsm-client-sdk/": join(base, "@ideem/zsm-client-sdk/"),
87
- };
88
-
89
- // Map all official plugins (harmless unless actually imported)
90
- for (const { pkg, entry } of PLUGINS) {
91
- imports[pkg] = join(base, `${pkg}/${entry}`);
92
- imports[`${pkg}/`] = join(base, `${pkg}/`);
93
- }
94
-
95
- injectImportMap(imports);
96
- })();