@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 +13 -8
- package/WebAuthnClientBase.js +10 -69
- package/package.json +1 -1
- package/shims/VanillaJSLoader.js +0 -96
- package/shims/es-module-shims.js +0 -1067
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
|
|
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
|
-
|
|
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
|
}
|
package/WebAuthnClientBase.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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"],
|
package/shims/VanillaJSLoader.js
DELETED
|
@@ -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
|
-
})();
|