@ideem/zsm-client-sdk 2.5.0 → 2.6.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/ErrorHandler.js +154 -0
- package/FIDO2ClientBase.js +1 -0
- package/RelyingPartyBase.js +38 -18
- package/UMFAClientBase.js +25 -13
- package/WASMRustInterface.js +448 -357
- package/WebAuthnClientBase.js +143 -130
- package/package.json +68 -19
- package/shims/VanillaJSLoader.js +0 -96
- package/shims/es-module-shims.js +0 -1067
package/WebAuthnClientBase.js
CHANGED
|
@@ -4,6 +4,7 @@ import { uid2iid, iid4uid } from './IdentityIndexing.js';
|
|
|
4
4
|
import { toObject } from './Utils.js';
|
|
5
5
|
import {zsmPluginManager} from './PluginManager.js';
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
eventCoordinator.update('WebAuthnClientBase');
|
|
8
9
|
|
|
9
10
|
const tracePrimer = (obj=false) => {
|
|
@@ -98,7 +99,7 @@ class WebAuthnClientBase {
|
|
|
98
99
|
* @ by the initializeZsm thereafter. Fixes several potential race conditions and improves performance.
|
|
99
100
|
* @returns {Promise<ZSMAPI>} Resolves with the initialized ZSM API.
|
|
100
101
|
*/
|
|
101
|
-
primeZsmApi
|
|
102
|
+
async primeZsmApi () { return (!this.zsmApi) ? (this.zsmApi = await this.getZsmApi(Object.assign({}, GLOBAL.mpcConfig, {consumer_id: await iid4uid(GLOBAL.mpcConfig.consumer_id)}))) : this.zsmApi; }
|
|
102
103
|
|
|
103
104
|
/**
|
|
104
105
|
* @name initializeZsm
|
|
@@ -126,22 +127,66 @@ class WebAuthnClientBase {
|
|
|
126
127
|
*/
|
|
127
128
|
|
|
128
129
|
async checkIdentity (userIdentifier, primeEnroll=false) {
|
|
130
|
+
|
|
129
131
|
try {
|
|
130
|
-
this.status='CHECKING_IDENTITY_STARTING';
|
|
131
132
|
const resultOfCheck = await this.relyingParty.checkServerSideIdentity(primeEnroll);
|
|
133
|
+
|
|
132
134
|
if(!resultOfCheck) throw new Error('Unable to communicate with identity server!');
|
|
133
|
-
const identityID = resultOfCheck.identity_id;
|
|
135
|
+
const identityID = Array.isArray(resultOfCheck) ? resultOfCheck[0].identity_id : resultOfCheck.identity_id;
|
|
136
|
+
|
|
134
137
|
if(!identityID) throw new Error('Unable to acquire identity_id!');
|
|
138
|
+
|
|
135
139
|
uid2iid(userIdentifier, identityID);
|
|
136
140
|
GLOBAL.mpcConfig.consumer_id = identityID;
|
|
137
|
-
|
|
138
|
-
return Promise.resolve(identityID);
|
|
141
|
+
return identityID;
|
|
139
142
|
} catch(err) {
|
|
140
|
-
this.status = 'CHECKING_IDENTITY_FAILED';
|
|
141
143
|
return Promise.reject(err);
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
146
|
|
|
147
|
+
/**
|
|
148
|
+
* @name webauthnCreate
|
|
149
|
+
* @description Creates a new WebAuthn credential for the specified userIdentifier.
|
|
150
|
+
* @param {string} userIdentifier The user identifier.
|
|
151
|
+
* @returns {Promise<Object>} Resolves with the created credential.
|
|
152
|
+
* @throws {Error} Throws a COUNTED error if unable to retrieve the public key from the Relying Party Server.
|
|
153
|
+
* @throws {Error} [MPC] Throws an error if unable to obtain the createResponse from the Crypto Server.
|
|
154
|
+
* @throws {Error} [MPC] Throws an error if the createResponse is missing the credential.
|
|
155
|
+
* @throws {Error} [MPC] Throws an error if the credential is missing the attestation object.
|
|
156
|
+
* @throws {Error} Throws a COUNTED error if unable to verify the credential with the Relying Party Server.
|
|
157
|
+
*/
|
|
158
|
+
async webauthnCreate (userIdentifier) {
|
|
159
|
+
|
|
160
|
+
await this.initializeZsm('webauthnCreate');
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
const publicKey = await this.relyingParty.registrationStart();
|
|
164
|
+
if(publicKey instanceof Error) throw new Error(publicKey);
|
|
165
|
+
else if (!publicKey) throw new Error('[WebAuthnClient] :: webauthnCreate :: Unable to retrieve public key from Relying Party Server!');
|
|
166
|
+
|
|
167
|
+
publicKey.user_id = publicKey.user.id;
|
|
168
|
+
|
|
169
|
+
let createResponse = await this.zsmApi.webauthn_create(publicKey, undefined, userIdentifier, tracePrimer());
|
|
170
|
+
if(!createResponse) throw new Error('[WebAuthnClient - mpc_round] :: webauthnCreate :: Unable to obtain response from Crypto Server!');
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
createResponse = toObject(createResponse);
|
|
174
|
+
GLOBAL.dispatchEvent(new CustomEvent("MPCResponse", {bubbles: true, cancelable: false, detail: createResponse }));
|
|
175
|
+
|
|
176
|
+
if(!createResponse?.result) throw new Error('[WebAuthnClient - mpc_round] :: webauthnCreate :: Unable to obtain credential from Crypto Server!');
|
|
177
|
+
if(!createResponse?.result?.response) throw new Error('[WebAuthnClient - mpc_round] :: webauthnCreate :: Unable to acquire attestation object!');
|
|
178
|
+
|
|
179
|
+
const credential = createResponse.result;
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
const registrationResult = await this.relyingParty.registrationFinish(credential);
|
|
183
|
+
if(registrationResult instanceof Error) throw new Error(registrationResult);
|
|
184
|
+
else if(registrationResult == false) throw new Error('[WebAuthnClient] :: webauthnCreate :: Unable to verify credential Relying Party Server!');
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
return registrationResult;
|
|
188
|
+
};
|
|
189
|
+
|
|
145
190
|
/**
|
|
146
191
|
* @name webauthnCreate
|
|
147
192
|
* @description Creates a new WebAuthn credential for the specified userIdentifier.
|
|
@@ -154,14 +199,11 @@ class WebAuthnClientBase {
|
|
|
154
199
|
* @throws {Error} Throws an error if unable to acquire the identity ID from the Relying Party Server.
|
|
155
200
|
* @throws {Error} Throws an error if the user ID or token is missing from the response body.
|
|
156
201
|
*/
|
|
157
|
-
async
|
|
202
|
+
async webauthnCreateMpcThenFinish (userIdentifier, publicKey, pairing_token) {
|
|
158
203
|
this.status = 'ENROLLMENT_STARTING';
|
|
159
204
|
await this.initializeZsm('webauthnCreate');
|
|
160
205
|
|
|
161
|
-
|
|
162
|
-
if(!publicKey) throw new Error('[WebAuthnClient] :: webauthnCreate :: Unable to retrieve public key from Relying Party Server!');
|
|
163
|
-
|
|
164
|
-
let createResponse = await this.zsmApi.webauthn_create(publicKey, tracePrimer());
|
|
206
|
+
let createResponse = await this.zsmApi.webauthn_create(publicKey, pairing_token, userIdentifier, tracePrimer());
|
|
165
207
|
if(!createResponse) throw new Error('[WebAuthnClient - mpc_round] :: webauthnCreate :: Unable to obtain response from Crypto Server!');
|
|
166
208
|
|
|
167
209
|
createResponse = toObject(createResponse);
|
|
@@ -175,7 +217,7 @@ class WebAuthnClientBase {
|
|
|
175
217
|
const registrationResult = await this.relyingParty.registrationFinish(credential);
|
|
176
218
|
if (!registrationResult) throw new Error('[WebAuthnClient] :: webauthnCreate :: Unable to verify credential Relying Party Server!');
|
|
177
219
|
|
|
178
|
-
return registrationResult;
|
|
220
|
+
return registrationResult.credential;
|
|
179
221
|
};
|
|
180
222
|
|
|
181
223
|
/**
|
|
@@ -184,157 +226,124 @@ class WebAuthnClientBase {
|
|
|
184
226
|
* @param {string} userIdentifier The user identifier.
|
|
185
227
|
* @returns {Promise<ZSMAPI>} Resolves with the retrieved credential.
|
|
186
228
|
* @throws {Error} Throws an error if no credential ID is found.
|
|
187
|
-
* @throws {Error} Throws
|
|
229
|
+
* @throws {Error} Throws a COUNTED error if unable to start authentication with the Relying Party Server.
|
|
188
230
|
* @throws {Error} Throws an error if unable to retrieve the public key/challenge from the Relying Party Server.
|
|
189
|
-
* @throws {Error} Throws an error if unable to obtain the
|
|
231
|
+
* @throws {Error} Throws an error if unable to obtain the mpcResponse/credential from the Crypto Server.
|
|
190
232
|
* @throws {Error} Throws an error if unable to verify the credential with the Relying Party Server.
|
|
191
233
|
* @throws {Error} Throws an error if the user ID or token is missing from the response body.
|
|
192
234
|
*/
|
|
193
235
|
async webauthnGet (userIdentifier) {
|
|
194
|
-
this.status = 'AUTHENTICATION_STARTING';
|
|
195
236
|
|
|
196
237
|
await this.initializeZsm('webauthnGet');
|
|
197
|
-
await this.webauthnRetrieve(userIdentifier, true)
|
|
238
|
+
const credentialId = await this.webauthnRetrieve(userIdentifier, true);
|
|
198
239
|
|
|
240
|
+
if (!this.isEnrolled) throw new Error(`[WebAuthnClient] :: webauthnGet :: ${userIdentifier} is not enrolled, lacks a credential, or credential is corrupted!`);
|
|
199
241
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
let authStartResponse = await this.relyingParty.authenticationStart(this.credentialID);
|
|
203
|
-
if(!authStartResponse) throw new Error('[WebAuthnClient] :: webauthnGet :: Unable to start authentication with Relying Party Server!');
|
|
242
|
+
let authStartResponse = await this.relyingParty.authenticationStart(credentialId);
|
|
204
243
|
|
|
244
|
+
if (authStartResponse instanceof Error) throw new Error(authStartResponse);
|
|
245
|
+
else if (!authStartResponse) throw new Error('[WebAuthnClient] :: webauthnGet :: Unable to start authentication with Relying Party Server!');
|
|
246
|
+
|
|
205
247
|
authStartResponse = toObject(authStartResponse);
|
|
206
248
|
|
|
207
|
-
if(!authStartResponse?.rcr
|
|
208
|
-
|
|
209
|
-
const pubKey = authStartResponse?.rcr?.publicKey;
|
|
249
|
+
if(!authStartResponse?.rcr?.publicKey?.challenge) throw new Error('[WebAuthnClient] :: webauthnGet :: Unable to locate public key/challenge inside Relying Party Server response!\nResponse Body: ' + JSON.stringify(authStartResponse));
|
|
250
|
+
const publicKey = authStartResponse.rcr.publicKey;
|
|
210
251
|
|
|
211
|
-
|
|
252
|
+
let mpcResponse = await this.zsmApi.webauthn_get(publicKey, undefined, userIdentifier, tracePrimer());
|
|
253
|
+
|
|
212
254
|
|
|
213
|
-
|
|
214
|
-
if(!getResponse) throw new Error('[WebAuthnClient - mpc_round] :: webauthnGet :: Unable to obtain getResponse from Crypto Server!');
|
|
255
|
+
if(!mpcResponse) throw new Error('[WebAuthnClient - mpc_round] :: webauthnGet :: Crypto server did not respond!');
|
|
215
256
|
|
|
216
|
-
|
|
257
|
+
mpcResponse = toObject(mpcResponse);
|
|
258
|
+
if(!mpcResponse?.result) throw new Error('[WebAuthnClient - mpc_round] :: webauthnGet :: Response from Crypto Server did not contain a credential!\nResponse Body: ' + JSON.stringify(mpcResponse));
|
|
217
259
|
|
|
218
|
-
|
|
260
|
+
GLOBAL.dispatchEvent(new CustomEvent("MPCResponse", {bubbles: true, cancelable: false, detail: mpcResponse }));
|
|
219
261
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const credential = getResponse.result;
|
|
262
|
+
const credential = mpcResponse.result;
|
|
223
263
|
let authFinishResponse = await this.relyingParty.authenticationFinish(credential);
|
|
224
264
|
|
|
225
|
-
if
|
|
265
|
+
if (authFinishResponse instanceof Error) throw new Error(authFinishResponse);
|
|
266
|
+
else if (!authFinishResponse) throw new Error('[WebAuthnClient] :: webauthnGet :: Unable to verify credential from Relying Party Server!');
|
|
226
267
|
|
|
227
268
|
authFinishResponse = toObject(authFinishResponse)
|
|
228
|
-
|
|
229
|
-
if (!authFinishResponse.user_id || !authFinishResponse.token) throw new Error('[WebAuthnClient] :: webauthnGet :: User ID or token is missing from response body!');
|
|
269
|
+
if (!authFinishResponse.token) throw new Error('[WebAuthnClient] :: webauthnGet :: Token mismatched or missing from Relying Party Server verification attempt!\nResponse Body: ' + JSON.stringify(authFinishResponse));
|
|
230
270
|
|
|
231
271
|
return {
|
|
232
272
|
signature : credential.response.signature,
|
|
233
273
|
signedChallenge : credential.response.clientDataJSON,
|
|
234
274
|
user_id : authFinishResponse.user_id,
|
|
235
275
|
token : authFinishResponse.token,
|
|
236
|
-
challenge :
|
|
276
|
+
challenge : publicKey.challenge,
|
|
237
277
|
credential
|
|
238
278
|
};
|
|
239
279
|
};
|
|
240
280
|
|
|
281
|
+
|
|
282
|
+
|
|
241
283
|
/**
|
|
242
284
|
* @name webauthnRetrieve
|
|
243
285
|
* @description Looks up the Identity ID for the specified userIdentifier and retrieves its credential ID (if it exists).
|
|
244
286
|
* @ This is used to make a client-only check to see if a given userIdentifier already has an enrollment on the device.
|
|
245
287
|
* @param {string} userIdentifier The user identifier.
|
|
246
|
-
* @param {
|
|
247
|
-
* @
|
|
248
|
-
* @
|
|
288
|
+
* @param {string} targetData The specific data to retrieve. Options are:
|
|
289
|
+
* @ - 'id' or omitted: retrieves the ZSM credential ID (default)
|
|
290
|
+
* @ - 'all': retrieves an object containing all relevant credential data
|
|
291
|
+
* @ - any other string: retrieves the specified field from the response
|
|
292
|
+
* @returns {Promise<Boolean:false>} Returns `false` if retrieval fails (no enrollment found).
|
|
293
|
+
* @returns {Promise<String>} Returns the requested data if found (e.g., ").
|
|
294
|
+
* @returns {Promise<Object>} Resolves with an object containing all relevant credential data.
|
|
295
|
+
* @returns {Promise<Null>} Resolves with null if the requested data is not found.
|
|
296
|
+
* @throws {Error} Throws an error if an uncaught/unhandled error occurs while retrieving the credential.
|
|
249
297
|
*/
|
|
250
|
-
async webauthnRetrieve (userIdentifier
|
|
298
|
+
async webauthnRetrieve (userIdentifier) {
|
|
251
299
|
this.credentialID = null;
|
|
252
|
-
if (!silent) {
|
|
253
|
-
this.status = 'CREDENTIAL_RETRIEVING';
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
await this.initializeZsm('webauthnRetrieve');
|
|
258
|
-
let credentialIdentifier = await iid4uid(userIdentifier);
|
|
259
|
-
GLOBAL.mpcConfig.consumer_id = credentialIdentifier;
|
|
260
|
-
|
|
261
|
-
let response = await this.zsmApi.webauthn_retrieve(credentialIdentifier);
|
|
262
|
-
response = toObject(response);
|
|
263
|
-
|
|
264
|
-
if (response.error || !response?.result?.rawId) return false;
|
|
265
|
-
|
|
266
|
-
this.credentialID = response.result.rawId;
|
|
267
|
-
if (!silent) this.status = 'CREDENTIAL_RETRIEVED';
|
|
268
|
-
|
|
269
|
-
return this.credentialID;
|
|
270
|
-
} catch (error) {
|
|
271
|
-
if(!silent){
|
|
272
|
-
this.status = 'CREDENTIAL_RETRIEVAL_FAILED';
|
|
273
|
-
throw error;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
|
|
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
300
|
|
|
293
|
-
|
|
294
|
-
await
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
};
|
|
301
|
+
this.initializeZsm('webauthnRetrieve');
|
|
302
|
+
let credentialIdentifier = await iid4uid(userIdentifier),
|
|
303
|
+
requestedData = {};
|
|
304
|
+
GLOBAL.mpcConfig.consumer_id = credentialIdentifier;
|
|
334
305
|
|
|
335
|
-
|
|
306
|
+
let response = await this.zsmApi.webauthn_retrieve(userIdentifier);
|
|
307
|
+
response = toObject(response);
|
|
336
308
|
|
|
309
|
+
if (response?.error || !response?.result) return false;
|
|
310
|
+
|
|
311
|
+
this.credentialID = response?.result['zsm_credential_id']??null;
|
|
312
|
+
return this.credentialID || false;
|
|
337
313
|
};
|
|
314
|
+
// async webauthnRetrieve (userIdentifier, targetData='id') {
|
|
315
|
+
// this.credentialID = null;
|
|
316
|
+
//
|
|
317
|
+
// this.initializeZsm('webauthnRetrieve');
|
|
318
|
+
// let credentialIdentifier = await iid4uid(userIdentifier),
|
|
319
|
+
// requestedData = {};
|
|
320
|
+
// GLOBAL.mpcConfig.consumer_id = credentialIdentifier;
|
|
321
|
+
//
|
|
322
|
+
// let response = await this.zsmApi.webauthn_retrieve(userIdentifier);
|
|
323
|
+
// response = toObject(response);
|
|
324
|
+
//
|
|
325
|
+
// if (response?.error || !response?.result) return false;
|
|
326
|
+
//
|
|
327
|
+
// this.credentialID = response?.result['zsm_credential_id']??null;
|
|
328
|
+
//
|
|
329
|
+
// if([undefined,true,false,null,'','id'].includes(targetData)) {
|
|
330
|
+
// return this.credentialID;
|
|
331
|
+
// }
|
|
332
|
+
// let pkpCred = await iid4uid(`pk:${userIdentifier}`);
|
|
333
|
+
// if(pkpCred === `pk:${userIdentifier}`) pkpCred = undefined;
|
|
334
|
+
// requestedData = {
|
|
335
|
+
// userIdentifier,
|
|
336
|
+
// zsmCredID : this.credentialID,
|
|
337
|
+
// hasZSMCred : (this.credentialID != null),
|
|
338
|
+
// pkpCredID : pkpCred,
|
|
339
|
+
// hasLocalPasskey : (typeof pkpCred === 'string' && pkpCred !== ''),
|
|
340
|
+
// hasRemotePasskey : (typeof pkpCred === 'object')
|
|
341
|
+
// };
|
|
342
|
+
// if (targetData === "all") {
|
|
343
|
+
// return requestedData;
|
|
344
|
+
// }
|
|
345
|
+
// return response?.[targetData] ?? response?.result?.[targetData] ?? requestedData?.[targetData] ?? null;
|
|
346
|
+
// };
|
|
338
347
|
|
|
339
348
|
/**
|
|
340
349
|
* @name webauthnPartialGet
|
|
@@ -344,8 +353,8 @@ class WebAuthnClientBase {
|
|
|
344
353
|
* @throws {Error} Throws an error if the credential is missing for the userIdentifier provided.
|
|
345
354
|
* @throws {Error} Throws an error if unable to start authentication with the Relying Party Server.
|
|
346
355
|
* @throws {Error} Throws an error if unable to retrieve the public key from the Relying Party Server.
|
|
347
|
-
* @throws {Error} Throws an error if unable to obtain the
|
|
348
|
-
* @emits {CustomEvent} Emits a "MPCResponse" event with the
|
|
356
|
+
* @throws {Error} Throws an error if unable to obtain the mpcResponse/credential from the Crypto Server.
|
|
357
|
+
* @emits {CustomEvent} Emits a "MPCResponse" event with the mpcResponse data.
|
|
349
358
|
*/
|
|
350
359
|
async webauthnPartialGet (userIdentifier) {
|
|
351
360
|
this.status = 'AUTHENTICATION_STARTING';
|
|
@@ -361,16 +370,16 @@ class WebAuthnClientBase {
|
|
|
361
370
|
if(!getChallenge?.rcr || !getChallenge.rcr.publicKey) throw new Error('[WebAuthnClient] :: webauthnPartialGet :: Unable to retrieve public key from Relying Party Server!');
|
|
362
371
|
const pubKey = getChallenge.rcr.publicKey;
|
|
363
372
|
|
|
364
|
-
let
|
|
365
|
-
if(!
|
|
373
|
+
let mpcResponse = await this.zsmApi.webauthn_get(pubKey, null, userIdentifier, tracePrimer());
|
|
374
|
+
if(!mpcResponse) throw new Error('[WebAuthnClient - mpc_round] :: webauthnPartialGet :: Unable to obtain mpcResponse from Crypto Server!');
|
|
366
375
|
|
|
367
|
-
|
|
376
|
+
mpcResponse = toObject(mpcResponse);
|
|
368
377
|
|
|
369
|
-
if(!
|
|
378
|
+
if(!mpcResponse?.result) throw new Error('[WebAuthnClient - mpc_round] :: webauthnPartialGet :: Unable to obtain credential from Crypto Server!');
|
|
370
379
|
|
|
371
|
-
GLOBAL.dispatchEvent(new CustomEvent("MPCResponse", {bubbles: true, cancelable: false, detail:
|
|
380
|
+
GLOBAL.dispatchEvent(new CustomEvent("MPCResponse", {bubbles: true, cancelable: false, detail: mpcResponse }));
|
|
372
381
|
|
|
373
|
-
const getCredential =
|
|
382
|
+
const getCredential = mpcResponse.result;
|
|
374
383
|
return {
|
|
375
384
|
signature: getCredential.response.signature,
|
|
376
385
|
signedChallenge: getCredential.response.clientDataJSON,
|
|
@@ -405,7 +414,11 @@ class WebAuthnClientBase {
|
|
|
405
414
|
const cursor = event.target.result;
|
|
406
415
|
if (cursor) {
|
|
407
416
|
const { key, value } = cursor;
|
|
408
|
-
if
|
|
417
|
+
if(
|
|
418
|
+
key.includes(userIdentifier) ||
|
|
419
|
+
(typeof value === 'string' && value.includes(userIdentifier)) ||
|
|
420
|
+
typeof value === 'object' && value?.zsm_credential?.get('rawId') === rawID
|
|
421
|
+
) (store.delete(key), true);
|
|
409
422
|
cursor.continue();
|
|
410
423
|
}
|
|
411
424
|
};
|
package/package.json
CHANGED
|
@@ -1,23 +1,72 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name"
|
|
3
|
-
"version"
|
|
4
|
-
"description"
|
|
5
|
-
"author"
|
|
6
|
-
"keywords"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
2
|
+
"name": "@ideem/zsm-client-sdk",
|
|
3
|
+
"version": "2.6.0",
|
|
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
|
+
"author": "@JJ (Jason Jurusz) <jj@useideem.com>/<nerdy.deeds@done.cheap>",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"ideem",
|
|
8
|
+
"zsm",
|
|
9
|
+
"zsm-client-sdk",
|
|
10
|
+
"UMFAClient",
|
|
11
|
+
"FIDO2Client",
|
|
12
|
+
"library",
|
|
13
|
+
"UMFA",
|
|
14
|
+
"FIDO2",
|
|
15
|
+
"client",
|
|
16
|
+
"authentication",
|
|
17
|
+
"javascript",
|
|
18
|
+
"multi-factor",
|
|
19
|
+
"webauthn",
|
|
20
|
+
"authentication",
|
|
21
|
+
"security",
|
|
22
|
+
"cryptography",
|
|
23
|
+
"MPC",
|
|
24
|
+
"FIPS",
|
|
25
|
+
"NIST",
|
|
26
|
+
"FIDO2-ready",
|
|
27
|
+
"Modules In Progress",
|
|
28
|
+
"MIP"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./ZSMClientSDK.js",
|
|
32
|
+
"scripts": {
|
|
33
|
+
"start": "echo 'This is the Ideem SDK library, not a freestanding application. There is no start script defined. To use, import the library into a browser application.' 1>&2",
|
|
34
|
+
"version": "echo \"$(npm pkg get name | tr -d \\\") current version: $(npm pkg get version | tr -d \\\")\""
|
|
12
35
|
},
|
|
13
|
-
"collaborators"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
36
|
+
"collaborators": [
|
|
37
|
+
"Joel Teply <joel@useideem.com>",
|
|
38
|
+
"Todd Decker <todd@useideem.com>",
|
|
39
|
+
"Brian Koziel <brian@useideem.com>"
|
|
40
|
+
],
|
|
41
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "github : useideem/vHSM"
|
|
18
45
|
},
|
|
19
|
-
"homepage"
|
|
20
|
-
"bugs"
|
|
21
|
-
"files"
|
|
22
|
-
|
|
46
|
+
"homepage": "https://useideem.com",
|
|
47
|
+
"bugs": "https://github.com/useideem/vHSM/issues",
|
|
48
|
+
"files": [
|
|
49
|
+
"./ErrorHandler.js",
|
|
50
|
+
"./EventCoordinator.js",
|
|
51
|
+
"./FIDO2Client.js",
|
|
52
|
+
"./FIDO2ClientBase.js",
|
|
53
|
+
"./GlobalScoping.js",
|
|
54
|
+
"./IdentityIndexing.js",
|
|
55
|
+
"./package.json",
|
|
56
|
+
"./PluginManager.js",
|
|
57
|
+
"./RelyingParty.js",
|
|
58
|
+
"./RelyingPartyBase.js",
|
|
59
|
+
"./shims/",
|
|
60
|
+
"./UMFAClient.js",
|
|
61
|
+
"./UMFAClientBase.js",
|
|
62
|
+
"./Utils.js",
|
|
63
|
+
"./WASMRustInterface.js",
|
|
64
|
+
"./WebAuthnClient.js",
|
|
65
|
+
"./WebAuthnClientBase.js",
|
|
66
|
+
"./ZSMClientSDK.js",
|
|
67
|
+
"./CHANGELOG.md",
|
|
68
|
+
"./LICENSE",
|
|
69
|
+
"./README.md"
|
|
70
|
+
],
|
|
71
|
+
"sideEffects": false
|
|
23
72
|
}
|
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
|
-
})();
|