@transmitsecurity/platform-web-sdk 1.18.0 → 1.18.2
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 +10 -0
- package/dist/drs.cjs +1 -4844
- package/dist/drs.d.ts +6 -0
- package/dist/drs.js +1 -4831
- package/dist/ido.cjs +2 -15121
- package/dist/ido.js +2 -15114
- package/dist/idv.cjs +1 -42003
- package/dist/idv.js +1 -41995
- package/dist/index.cjs +2 -55347
- package/dist/index.esm.js +2 -55331
- package/dist/index.umd.js +3 -55354
- package/dist/ts-platform-websdk.js +3 -55353
- package/dist/web-sdk-drs+idv+webauthn+ido.js +2 -2
- package/dist/web-sdk.d.ts +21 -2
- package/dist/webauthn.cjs +1 -1994
- package/dist/webauthn.js +1 -1983
- package/package.json +5 -5
package/dist/webauthn.cjs
CHANGED
|
@@ -1,1994 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// Global polyfills for browser compatibility
|
|
3
|
-
(function() {
|
|
4
|
-
if (typeof globalThis !== 'undefined') return;
|
|
5
|
-
if (typeof window !== 'undefined') {
|
|
6
|
-
window.globalThis = window;
|
|
7
|
-
window.global = window;
|
|
8
|
-
} else if (typeof self !== 'undefined') {
|
|
9
|
-
self.globalThis = self;
|
|
10
|
-
self.global = self;
|
|
11
|
-
}
|
|
12
|
-
})();
|
|
13
|
-
'use strict';
|
|
14
|
-
|
|
15
|
-
const MODULE_INITIALIZED = Symbol('MODULE_INITIALIZED');
|
|
16
|
-
|
|
17
|
-
const events$1 = new Map();
|
|
18
|
-
function on(eventName, method) {
|
|
19
|
-
var _a;
|
|
20
|
-
if (events$1.has(eventName)) {
|
|
21
|
-
(_a = events$1.get(eventName)) === null || _a === void 0 ? void 0 : _a.push(method);
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
events$1.set(eventName, [method]);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function off(eventName, method) {
|
|
28
|
-
const eventArray = events$1.get(eventName);
|
|
29
|
-
if (!eventArray) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
const methodIndex = eventArray.indexOf(method);
|
|
33
|
-
if (methodIndex === -1) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
eventArray.splice(methodIndex, 1);
|
|
37
|
-
}
|
|
38
|
-
function emit(eventName, eventData) {
|
|
39
|
-
var _a;
|
|
40
|
-
(_a = events$1.get(eventName)) === null || _a === void 0 ? void 0 : _a.forEach(safeCallback((callbackMethod) => callbackMethod(eventData)));
|
|
41
|
-
}
|
|
42
|
-
function safeCallback(callback) {
|
|
43
|
-
return (...args) => {
|
|
44
|
-
try {
|
|
45
|
-
return callback(...args);
|
|
46
|
-
}
|
|
47
|
-
catch (e) {
|
|
48
|
-
console.log(e);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let initConfig = null;
|
|
54
|
-
function getInitConfig() {
|
|
55
|
-
return initConfig;
|
|
56
|
-
}
|
|
57
|
-
function setInitConfig(config) {
|
|
58
|
-
initConfig = config;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
var moduleMetadata = /*#__PURE__*/Object.freeze({
|
|
62
|
-
__proto__: null,
|
|
63
|
-
getInitConfig: getInitConfig,
|
|
64
|
-
get initConfig () { return initConfig; },
|
|
65
|
-
setInitConfig: setInitConfig
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
function initialize(params) {
|
|
69
|
-
setInitConfig(params);
|
|
70
|
-
emit(MODULE_INITIALIZED, undefined);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
var mainEntry = /*#__PURE__*/Object.freeze({
|
|
74
|
-
__proto__: null,
|
|
75
|
-
initialize: initialize
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
function bindMethods(agent, methods) {
|
|
79
|
-
return Object.entries(methods).reduce((result, [key, value]) => ({
|
|
80
|
-
...result,
|
|
81
|
-
[key]: Agent.isPrototypeOf(value)
|
|
82
|
-
? new value(agent.slug)
|
|
83
|
-
: typeof value === 'function'
|
|
84
|
-
? value.bind(agent)
|
|
85
|
-
: typeof value === 'object' && !Array.isArray(value) && !!value
|
|
86
|
-
? bindMethods(agent, value)
|
|
87
|
-
: value,
|
|
88
|
-
}), {});
|
|
89
|
-
}
|
|
90
|
-
class Agent {
|
|
91
|
-
constructor(slug) {
|
|
92
|
-
this.slug = slug;
|
|
93
|
-
}
|
|
94
|
-
static create(method) {
|
|
95
|
-
return class extends Agent {
|
|
96
|
-
constructor(slug) {
|
|
97
|
-
super(slug);
|
|
98
|
-
Object.assign(this, bindMethods(this, method(this)));
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
var agent$1 = /*#__PURE__*/Object.freeze({
|
|
105
|
-
__proto__: null,
|
|
106
|
-
Agent: Agent
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
var events = /*#__PURE__*/Object.freeze({
|
|
110
|
-
__proto__: null,
|
|
111
|
-
MODULE_INITIALIZED: MODULE_INITIALIZED,
|
|
112
|
-
emit: emit,
|
|
113
|
-
off: off,
|
|
114
|
-
on: on
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
function safeParse(string) {
|
|
118
|
-
if (!string)
|
|
119
|
-
return {};
|
|
120
|
-
try {
|
|
121
|
-
return JSON.parse(string);
|
|
122
|
-
}
|
|
123
|
-
catch (e) {
|
|
124
|
-
return {};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
/** @return tuple of `[value, newObject]`
|
|
128
|
-
* `value` is `object.path[0].path[1]` etc. while every node in the way is guaranteed to be an object
|
|
129
|
-
* `newObject` for given `object` will be itself while the full path to `value` added to it if wasn't already exists
|
|
130
|
-
*
|
|
131
|
-
* @note - the original `object` might be modified, but you should always use the returned `newObject` for
|
|
132
|
-
* case the original one was a premitive or an array.
|
|
133
|
-
*/
|
|
134
|
-
function safeGetObject(object, path) {
|
|
135
|
-
const newObject = !object || typeof object !== 'object' || Array.isArray(object) ? {} : object;
|
|
136
|
-
return [
|
|
137
|
-
path.reduce((object, key) => {
|
|
138
|
-
if (key in object) {
|
|
139
|
-
const next = object[key];
|
|
140
|
-
if (next !== null && typeof next === 'object' && !Array.isArray(next)) {
|
|
141
|
-
return next;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const newValue = {};
|
|
145
|
-
object[key] = newValue;
|
|
146
|
-
return newValue;
|
|
147
|
-
}, newObject),
|
|
148
|
-
newObject,
|
|
149
|
-
];
|
|
150
|
-
}
|
|
151
|
-
function safeHas(object, path) {
|
|
152
|
-
let currentObject = object;
|
|
153
|
-
return path.every((key) => {
|
|
154
|
-
if (!currentObject ||
|
|
155
|
-
typeof currentObject !== 'object' ||
|
|
156
|
-
Array.isArray(currentObject) ||
|
|
157
|
-
!(key in currentObject)) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
currentObject = currentObject[key];
|
|
161
|
-
return true;
|
|
162
|
-
}, object);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const COMMON_STORAGE_KEY = 'tsec';
|
|
166
|
-
const GENERAL_ID_KEY = 'general';
|
|
167
|
-
function getClientKey(isGeneral) {
|
|
168
|
-
return isGeneral ? GENERAL_ID_KEY : initConfig.clientId;
|
|
169
|
-
}
|
|
170
|
-
function getStoredObject(sessionOnly) {
|
|
171
|
-
const storage = sessionOnly ? sessionStorage : localStorage;
|
|
172
|
-
return safeParse(storage.getItem(COMMON_STORAGE_KEY));
|
|
173
|
-
}
|
|
174
|
-
function setStoredObject(sessionOnly, callback) {
|
|
175
|
-
const storage = sessionOnly ? sessionStorage : localStorage;
|
|
176
|
-
const storedObject = getStoredObject(sessionOnly);
|
|
177
|
-
const newValue = callback(storedObject);
|
|
178
|
-
storage.setItem(COMMON_STORAGE_KEY, JSON.stringify(newValue));
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Storage module handles local storage and session storgae for multyple modules in
|
|
182
|
-
* same storage key, with JSON serialization. It stores data in 'tsec' key, as an
|
|
183
|
-
* object with that structure:
|
|
184
|
-
* { [moduleSlug]: { [clientId | 'general']: { [key]: value } } }
|
|
185
|
-
*/
|
|
186
|
-
function setValue(key, value, options = {}) {
|
|
187
|
-
const idKey = getClientKey(!!options.isGeneral);
|
|
188
|
-
setStoredObject(!!options.sessionOnly, (storedObject) => {
|
|
189
|
-
const [lastLevel, newObject] = safeGetObject(storedObject, [this.slug.toString(), idKey]);
|
|
190
|
-
lastLevel[key] = value;
|
|
191
|
-
return newObject;
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
function removeValue(key, options = {}) {
|
|
195
|
-
const idKey = getClientKey(!!options.isGeneral);
|
|
196
|
-
setStoredObject(!!options.sessionOnly, (storedObject) => {
|
|
197
|
-
const [lastLevel, newObject] = safeGetObject(storedObject, [this.slug.toString(), idKey]);
|
|
198
|
-
delete lastLevel[key];
|
|
199
|
-
return newObject;
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
function getValue(key, options = {}) {
|
|
203
|
-
const idKey = getClientKey(!!options.isGeneral);
|
|
204
|
-
const storedObject = getStoredObject(!!options.sessionOnly);
|
|
205
|
-
const [lastLevel] = safeGetObject(storedObject, [this.slug.toString(), idKey]);
|
|
206
|
-
return lastLevel[key];
|
|
207
|
-
}
|
|
208
|
-
function hasValue(key, options = {}) {
|
|
209
|
-
const idKey = getClientKey(!!options.isGeneral);
|
|
210
|
-
const storedObject = getStoredObject(!!options.sessionOnly);
|
|
211
|
-
return safeHas(storedObject, [this.slug.toString(), idKey, key]);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
var storage = /*#__PURE__*/Object.freeze({
|
|
215
|
-
__proto__: null,
|
|
216
|
-
COMMON_STORAGE_KEY: COMMON_STORAGE_KEY,
|
|
217
|
-
GENERAL_ID_KEY: GENERAL_ID_KEY,
|
|
218
|
-
getValue: getValue,
|
|
219
|
-
hasValue: hasValue,
|
|
220
|
-
removeValue: removeValue,
|
|
221
|
-
setValue: setValue
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const ASYMMETRIC_ENCRYPTION_ALGORITHM = 'RSA-OAEP';
|
|
225
|
-
const ASYMMETRIC_SIGN_ALGORITHM = 'RSA-PSS';
|
|
226
|
-
const generateKeyPair = async (algorithm, usages) => {
|
|
227
|
-
return await window.crypto.subtle.generateKey({
|
|
228
|
-
name: algorithm,
|
|
229
|
-
modulusLength: 2048,
|
|
230
|
-
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
|
231
|
-
hash: 'SHA-256',
|
|
232
|
-
}, false, // setting the private key non-extractable
|
|
233
|
-
usages);
|
|
234
|
-
};
|
|
235
|
-
const generateRSAKeyPair = async () => {
|
|
236
|
-
return await generateKeyPair(ASYMMETRIC_ENCRYPTION_ALGORITHM, ['encrypt', 'decrypt']);
|
|
237
|
-
};
|
|
238
|
-
const generateRSASignKeyPair = async () => {
|
|
239
|
-
return await generateKeyPair(ASYMMETRIC_SIGN_ALGORITHM, ['sign']);
|
|
240
|
-
};
|
|
241
|
-
const signAssymetric = async (privateKey, message) => {
|
|
242
|
-
const encoded = new TextEncoder().encode(message);
|
|
243
|
-
const signature = await window.crypto.subtle.sign({
|
|
244
|
-
name: ASYMMETRIC_SIGN_ALGORITHM,
|
|
245
|
-
saltLength: 32,
|
|
246
|
-
}, privateKey, encoded);
|
|
247
|
-
return signature;
|
|
248
|
-
};
|
|
249
|
-
const verifyAssymetric = async (publicKey, message, signature) => {
|
|
250
|
-
const encoded = new TextEncoder().encode(message);
|
|
251
|
-
const isValid = await window.crypto.subtle.verify(ASYMMETRIC_SIGN_ALGORITHM, publicKey, signature, encoded);
|
|
252
|
-
return isValid;
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* @param slug - product name (like 'drs'/'idv' or 'platform' for a global platform shared storage)
|
|
257
|
-
* @param dbName - database name, concatinated later after clientId
|
|
258
|
-
* @param dbVersion - our own database versioning, note that for any table addition/deletion dbVersion must be bumped - DON'T CHANGE IT IF NOT REALLY NECESSARY.
|
|
259
|
-
* @description The database different situations are covered here:
|
|
260
|
-
- database wasn't created yet on the user's browser
|
|
261
|
-
- database was created, but currently closed before we use it
|
|
262
|
-
- database is already opened by another module (extension / this sdk), and requested indexedDB.open with the same version
|
|
263
|
-
- database is already opened with different version by another module - pending (promise) until the previous transaction will be closed - then executing
|
|
264
|
-
*/
|
|
265
|
-
class IndexedDBClient {
|
|
266
|
-
constructor(slug, dbName, dbVersion) {
|
|
267
|
-
this.slug = slug;
|
|
268
|
-
this.dbName = dbName;
|
|
269
|
-
this.dbVersion = dbVersion;
|
|
270
|
-
}
|
|
271
|
-
queryObjectStore(objectStoreName, queryStore, options = {}) {
|
|
272
|
-
const { attemptToRecoverDB = true } = options;
|
|
273
|
-
// One of those databases is supported in any device/browser
|
|
274
|
-
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
|
|
275
|
-
const databaseName = `${this.slug}:${this.dbName}`;
|
|
276
|
-
// make it product scoped in case multiple sdks for different products could work in parallel
|
|
277
|
-
const openRequest = indexedDB.open(databaseName, this.dbVersion || 1); // Opening (or creating) the database
|
|
278
|
-
openRequest.onupgradeneeded = () => {
|
|
279
|
-
var _a;
|
|
280
|
-
const db = openRequest.result;
|
|
281
|
-
if (((_a = db === null || db === void 0 ? void 0 : db.objectStoreNames) === null || _a === void 0 ? void 0 : _a.contains) && !db.objectStoreNames.contains(objectStoreName)) { // if there's no `tableName` store
|
|
282
|
-
db.createObjectStore(objectStoreName, { keyPath: 'key' });
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
openRequest.onsuccess = () => {
|
|
286
|
-
const db = openRequest.result;
|
|
287
|
-
let transaction;
|
|
288
|
-
try {
|
|
289
|
-
transaction = db.transaction(objectStoreName, (options === null || options === void 0 ? void 0 : options.operation) || 'readwrite');
|
|
290
|
-
}
|
|
291
|
-
catch (error) {
|
|
292
|
-
// There is a Safari-specific race condition - the database is created, but the object store is missing.
|
|
293
|
-
// It happens when a user stops the script during store creation (e.g. by refreshing the page).
|
|
294
|
-
// We recover by deleting the broken database and recreating it with a proper schema.
|
|
295
|
-
// There is only one attempt to recover.
|
|
296
|
-
if (attemptToRecoverDB && error instanceof DOMException && error.name === 'NotFoundError') {
|
|
297
|
-
db.close();
|
|
298
|
-
const deleteRequest = indexedDB.deleteDatabase(databaseName);
|
|
299
|
-
deleteRequest.onsuccess = () => {
|
|
300
|
-
this.queryObjectStore(objectStoreName, queryStore, { ...options, attemptToRecoverDB: false });
|
|
301
|
-
};
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
throw error;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
const store = transaction.objectStore(objectStoreName);
|
|
309
|
-
queryStore(store);
|
|
310
|
-
// Closing the db when the transaction is done
|
|
311
|
-
transaction.oncomplete = () => {
|
|
312
|
-
db.close();
|
|
313
|
-
};
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
put(tableName, key, object) {
|
|
317
|
-
return new Promise((res, rej) => {
|
|
318
|
-
this.queryObjectStore(tableName, (objectStore) => {
|
|
319
|
-
const request = objectStore.put({ key, value: object });
|
|
320
|
-
request.onsuccess = () => {
|
|
321
|
-
res(request.result);
|
|
322
|
-
};
|
|
323
|
-
request.onerror = (err) => {
|
|
324
|
-
rej('Failed adding item to objectStore, err: ' + err);
|
|
325
|
-
};
|
|
326
|
-
});
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
add(tableName, key, object) {
|
|
330
|
-
return new Promise((res, rej) => {
|
|
331
|
-
this.queryObjectStore(tableName, (objectStore) => {
|
|
332
|
-
const request = objectStore.add({ key, value: object });
|
|
333
|
-
request.onsuccess = () => {
|
|
334
|
-
res(request.result);
|
|
335
|
-
};
|
|
336
|
-
request.onerror = (err) => {
|
|
337
|
-
const error = err.target.error;
|
|
338
|
-
rej(error);
|
|
339
|
-
};
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
get(tableName, key) {
|
|
344
|
-
return new Promise((res, rej) => {
|
|
345
|
-
this.queryObjectStore(tableName, (objectStore) => {
|
|
346
|
-
const request = objectStore.get(key);
|
|
347
|
-
request.onsuccess = () => {
|
|
348
|
-
var _a;
|
|
349
|
-
if (request.result) {
|
|
350
|
-
// since it can trigger 'request.onsuccess' also when the objectStore exist but no such item found (`request.result` would be undefined)
|
|
351
|
-
res((_a = request.result) === null || _a === void 0 ? void 0 : _a.value);
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
res(undefined);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
request.onerror = (err) => {
|
|
358
|
-
rej('Failed adding item to objectStore, err: ' + err);
|
|
359
|
-
};
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
getAll(tableName, count) {
|
|
364
|
-
return new Promise((res, rej) => {
|
|
365
|
-
this.queryObjectStore(tableName, (objectStore) => {
|
|
366
|
-
const request = objectStore.getAll(null, count);
|
|
367
|
-
request.onsuccess = () => {
|
|
368
|
-
if (request.result) {
|
|
369
|
-
const itemsList = request.result;
|
|
370
|
-
if (itemsList === null || itemsList === void 0 ? void 0 : itemsList.length) {
|
|
371
|
-
res(itemsList.map((item) => item === null || item === void 0 ? void 0 : item.value));
|
|
372
|
-
}
|
|
373
|
-
else {
|
|
374
|
-
res(itemsList);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
res([]);
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
request.onerror = (err) => {
|
|
382
|
-
rej('Failed getting items, err: ' + err);
|
|
383
|
-
};
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
delete(tableName, key) {
|
|
388
|
-
return new Promise((res, rej) => {
|
|
389
|
-
this.queryObjectStore(tableName, (objectStore) => {
|
|
390
|
-
const request = objectStore.delete(key);
|
|
391
|
-
request.onsuccess = () => {
|
|
392
|
-
res();
|
|
393
|
-
};
|
|
394
|
-
request.onerror = (err) => {
|
|
395
|
-
rej(`Failed deleting key: '${key}' from objectStore, err: ` + err);
|
|
396
|
-
};
|
|
397
|
-
});
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
clear(tableName) {
|
|
401
|
-
return new Promise((res, rej) => {
|
|
402
|
-
this.queryObjectStore(tableName, (objectStore) => {
|
|
403
|
-
const request = objectStore.clear();
|
|
404
|
-
request.onsuccess = () => {
|
|
405
|
-
res();
|
|
406
|
-
};
|
|
407
|
-
request.onerror = (err) => {
|
|
408
|
-
rej('Failed clearing objectStore, err: ' + err);
|
|
409
|
-
};
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const PLATFORM_GLOBAL_SLUG = 'platform';
|
|
416
|
-
const DEFAULT_KEYSTORE_NAME = 'identifiers_store';
|
|
417
|
-
const DEFAULT_DATABASE_NAME = 'ts_crypto_binding';
|
|
418
|
-
const DEFAULT_DB_VERSION = 1;
|
|
419
|
-
const PLATFORM_GLOBAL_KEYS_DB_VERSION = 1; // Change this only when the platform db-version changes, it will effect all products
|
|
420
|
-
const ROTATED_KEYS_SLOT = 'rotated';
|
|
421
|
-
const ROTATED_KEYS_SLOT_PENDING = 'rotated_pending';
|
|
422
|
-
const EXPIRY_DAYS_THRESHOLD = 30; // The number of days before the expiration of the current key to trigger the rotation
|
|
423
|
-
const INIT_ROTATION_RESPONSE = 'init';
|
|
424
|
-
const COMPLETED_ROTATION_RESPONSE = 'completed';
|
|
425
|
-
const RSA_PUBLIC_KEY_TYPE = 'RSA2048';
|
|
426
|
-
const VALID_ROTATION_RESPONSES = [INIT_ROTATION_RESPONSE, COMPLETED_ROTATION_RESPONSE];
|
|
427
|
-
/**
|
|
428
|
-
* @param keysType - the purpose of the keys, will use different key generator
|
|
429
|
-
* @param options - typeof CryptoBindingOptions
|
|
430
|
-
*/
|
|
431
|
-
class CryptoBinding {
|
|
432
|
-
constructor(agent, keysType = 'sign', options) {
|
|
433
|
-
var _a, _b, _c, _d;
|
|
434
|
-
this.agent = agent;
|
|
435
|
-
this.keysType = keysType;
|
|
436
|
-
this.options = options;
|
|
437
|
-
this._extractingKeysPromise = null;
|
|
438
|
-
const isGlobal = !((_a = this.options) === null || _a === void 0 ? void 0 : _a.productScope);
|
|
439
|
-
this.keysDatabaseName = isGlobal || !((_b = this.options) === null || _b === void 0 ? void 0 : _b.indexedDBName) ? DEFAULT_DATABASE_NAME : this.options.indexedDBName;
|
|
440
|
-
this.dbVersion = isGlobal ? PLATFORM_GLOBAL_KEYS_DB_VERSION : (((_c = this.options) === null || _c === void 0 ? void 0 : _c.dbVersion) || DEFAULT_DB_VERSION);
|
|
441
|
-
this.keysStoreName = isGlobal || !((_d = this.options) === null || _d === void 0 ? void 0 : _d.keysStoreName) ? DEFAULT_KEYSTORE_NAME : this.options.keysStoreName;
|
|
442
|
-
this.indexedDBClient = new IndexedDBClient(isGlobal ? PLATFORM_GLOBAL_SLUG : agent.slug, this.keysDatabaseName, this.dbVersion);
|
|
443
|
-
// created a fallback indexedDBClient for crypto-keys migration from old versions (clientId dependent on the dbName):
|
|
444
|
-
this.indexedDBClientFallback = new IndexedDBClient((isGlobal ? PLATFORM_GLOBAL_SLUG : agent.slug) + `:${initConfig.clientId}`, this.keysDatabaseName, this.dbVersion);
|
|
445
|
-
}
|
|
446
|
-
getKeysRecordKey() {
|
|
447
|
-
return `${this.keysType}_keys`;
|
|
448
|
-
}
|
|
449
|
-
getRotatedKeysRecordKey() {
|
|
450
|
-
return `${ROTATED_KEYS_SLOT}_${this.keysType}_keys`;
|
|
451
|
-
}
|
|
452
|
-
getRotatedKeysRecordKeyPending() {
|
|
453
|
-
return `${ROTATED_KEYS_SLOT_PENDING}_${this.keysType}_keys`;
|
|
454
|
-
}
|
|
455
|
-
arrayBufferToBase64(arrayBuffer) {
|
|
456
|
-
return window.btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
|
|
457
|
-
}
|
|
458
|
-
async getPKRepresentations(publicKey) {
|
|
459
|
-
const exportedKey = await crypto.subtle.exportKey('spki', publicKey);
|
|
460
|
-
return { arrayBufferKey: exportedKey, base64Key: this.arrayBufferToBase64(exportedKey) };
|
|
461
|
-
}
|
|
462
|
-
async generateKeyPair() {
|
|
463
|
-
if (this.keysType == 'sign') {
|
|
464
|
-
return await generateRSASignKeyPair();
|
|
465
|
-
}
|
|
466
|
-
return await generateRSAKeyPair();
|
|
467
|
-
}
|
|
468
|
-
async calcKeyIdentifier(publicKeyExported) {
|
|
469
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", publicKeyExported);
|
|
470
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
471
|
-
const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
|
|
472
|
-
return hashHex;
|
|
473
|
-
}
|
|
474
|
-
async extractKeysData() {
|
|
475
|
-
// if an extraction is already in progress, return the existing promise
|
|
476
|
-
if (this._extractingKeysPromise) {
|
|
477
|
-
return this._extractingKeysPromise;
|
|
478
|
-
}
|
|
479
|
-
this._extractingKeysPromise = (async () => {
|
|
480
|
-
var _a, _b;
|
|
481
|
-
const keysData = ((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.keyRotation) === null || _b === void 0 ? void 0 : _b.isEnabled)
|
|
482
|
-
? await this.getRotatedKeysData()
|
|
483
|
-
: await this.getKeysData();
|
|
484
|
-
// update the cache to prevent stale data
|
|
485
|
-
const { base64Key } = await this.getPKRepresentations(keysData.publicKey);
|
|
486
|
-
this.publicKeyBase64 = base64Key;
|
|
487
|
-
this.keyIdentifier = keysData.keyIdentifier;
|
|
488
|
-
return keysData;
|
|
489
|
-
})();
|
|
490
|
-
try {
|
|
491
|
-
return await this._extractingKeysPromise;
|
|
492
|
-
}
|
|
493
|
-
finally {
|
|
494
|
-
// reset the promise once done to allow future extractions if needed
|
|
495
|
-
this._extractingKeysPromise = null;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
async generateKeyPairData(keyIdentifier) {
|
|
499
|
-
const keys = await this.generateKeyPair();
|
|
500
|
-
const { arrayBufferKey } = await this.getPKRepresentations(keys.publicKey);
|
|
501
|
-
const keyPairIdentifier = keyIdentifier || (await this.calcKeyIdentifier(arrayBufferKey));
|
|
502
|
-
const keysData = {
|
|
503
|
-
...keys,
|
|
504
|
-
keyIdentifier: keyPairIdentifier,
|
|
505
|
-
createdDate: Date.now(),
|
|
506
|
-
};
|
|
507
|
-
return keysData;
|
|
508
|
-
}
|
|
509
|
-
shouldKeyBeRotated(keysData) {
|
|
510
|
-
var _a;
|
|
511
|
-
const keyRotation = (_a = this.options) === null || _a === void 0 ? void 0 : _a.keyRotation;
|
|
512
|
-
if (!(keyRotation === null || keyRotation === void 0 ? void 0 : keyRotation.isEnabled) || !keyRotation.expiryDays || keyRotation.startedAt === undefined) {
|
|
513
|
-
return false;
|
|
514
|
-
}
|
|
515
|
-
const expiryDaysMilliseconds = keyRotation.expiryDays * 24 * 60 * 60 * 1000;
|
|
516
|
-
const expiryDaysThresholdMilliseconds = EXPIRY_DAYS_THRESHOLD * 24 * 60 * 60 * 1000;
|
|
517
|
-
// Count time from:
|
|
518
|
-
// - rotation start, if the key was created before rotation was enabled
|
|
519
|
-
// - key creation, if it was created after rotation was enabled
|
|
520
|
-
const referenceTimestamp = keysData.createdDate && keysData.createdDate >= keyRotation.startedAt
|
|
521
|
-
? keysData.createdDate
|
|
522
|
-
: keyRotation.startedAt;
|
|
523
|
-
return Date.now() - referenceTimestamp > expiryDaysMilliseconds - expiryDaysThresholdMilliseconds;
|
|
524
|
-
}
|
|
525
|
-
async extractMainKeysData() {
|
|
526
|
-
return await this.indexedDBClient.get(this.keysStoreName, this.getKeysRecordKey());
|
|
527
|
-
}
|
|
528
|
-
async extractFallbackMainKeysData() {
|
|
529
|
-
return await this.indexedDBClientFallback.get(this.keysStoreName, this.getKeysRecordKey());
|
|
530
|
-
}
|
|
531
|
-
async extractRotatedKeysData() {
|
|
532
|
-
return await this.indexedDBClient.get(this.keysStoreName, this.getRotatedKeysRecordKey());
|
|
533
|
-
}
|
|
534
|
-
async extractPendingRotatedKeysData() {
|
|
535
|
-
return await this.indexedDBClient.get(this.keysStoreName, this.getRotatedKeysRecordKeyPending());
|
|
536
|
-
}
|
|
537
|
-
async saveKeyData(recordKey, keysDataToSave) {
|
|
538
|
-
try {
|
|
539
|
-
await this.indexedDBClient.add(this.keysStoreName, recordKey, keysDataToSave);
|
|
540
|
-
return keysDataToSave;
|
|
541
|
-
}
|
|
542
|
-
catch (error) {
|
|
543
|
-
if (error instanceof DOMException && error.name === 'ConstraintError') {
|
|
544
|
-
// duplicate key error, retrieve existing key data
|
|
545
|
-
const existingKey = (await this.indexedDBClient.get(this.keysStoreName, recordKey));
|
|
546
|
-
if (existingKey) {
|
|
547
|
-
return existingKey;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
throw error;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
async getKeysData() {
|
|
554
|
-
const recordKey = this.getKeysRecordKey();
|
|
555
|
-
let keysData = await this.extractMainKeysData();
|
|
556
|
-
if (keysData) {
|
|
557
|
-
return keysData;
|
|
558
|
-
}
|
|
559
|
-
keysData = await this.extractFallbackMainKeysData();
|
|
560
|
-
if (keysData) {
|
|
561
|
-
return this.saveKeyData(recordKey, keysData);
|
|
562
|
-
}
|
|
563
|
-
const newKeysData = await this.generateKeyPairData();
|
|
564
|
-
return this.saveKeyData(recordKey, newKeysData);
|
|
565
|
-
}
|
|
566
|
-
async getOrCreateRotatedKeys() {
|
|
567
|
-
let currentRotatedKeys = await this.extractRotatedKeysData();
|
|
568
|
-
if (!currentRotatedKeys) {
|
|
569
|
-
const rotatedRecordKey = this.getRotatedKeysRecordKey();
|
|
570
|
-
const mainSlotKeys = await this.getKeysData();
|
|
571
|
-
const rotatedKey = {
|
|
572
|
-
...mainSlotKeys,
|
|
573
|
-
createdDate: mainSlotKeys.createdDate || Date.now(),
|
|
574
|
-
};
|
|
575
|
-
currentRotatedKeys = await this.saveKeyData(rotatedRecordKey, rotatedKey);
|
|
576
|
-
}
|
|
577
|
-
return currentRotatedKeys;
|
|
578
|
-
}
|
|
579
|
-
async getRotatedKeysData() {
|
|
580
|
-
const currentRotatedKeys = await this.getOrCreateRotatedKeys();
|
|
581
|
-
if (this.shouldKeyBeRotated(currentRotatedKeys)) {
|
|
582
|
-
const pendingRotatedKeys = await this.extractPendingRotatedKeysData();
|
|
583
|
-
if (!pendingRotatedKeys) {
|
|
584
|
-
const pendingRecordKey = this.getRotatedKeysRecordKeyPending();
|
|
585
|
-
const newPendingKeys = await this.generateKeyPairData(currentRotatedKeys.keyIdentifier);
|
|
586
|
-
await this.saveKeyData(pendingRecordKey, newPendingKeys);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
return currentRotatedKeys;
|
|
590
|
-
}
|
|
591
|
-
async getPublicData() {
|
|
592
|
-
if (!this.publicKeyBase64 || !this.keyIdentifier) {
|
|
593
|
-
await this.extractKeysData();
|
|
594
|
-
}
|
|
595
|
-
return { publicKey: this.publicKeyBase64, keyIdentifier: this.keyIdentifier };
|
|
596
|
-
}
|
|
597
|
-
async sign(message) {
|
|
598
|
-
if (this.keysType == 'sign') {
|
|
599
|
-
const { privateKey } = await this.extractKeysData();
|
|
600
|
-
const signatureBuffer = await signAssymetric(privateKey, message);
|
|
601
|
-
return this.arrayBufferToBase64(signatureBuffer);
|
|
602
|
-
}
|
|
603
|
-
throw new Error("keysType must be 'sign' in order to use sign keys");
|
|
604
|
-
}
|
|
605
|
-
async clearKeys() {
|
|
606
|
-
const recordKey = this.getKeysRecordKey();
|
|
607
|
-
await this.indexedDBClient.delete(this.keysStoreName, recordKey);
|
|
608
|
-
}
|
|
609
|
-
getBaseRotationPayload() {
|
|
610
|
-
return {
|
|
611
|
-
keyIdentifier: this.keyIdentifier,
|
|
612
|
-
slot: this.getRotatedKeysRecordKey(),
|
|
613
|
-
publicKey: this.publicKeyBase64,
|
|
614
|
-
publicKeyType: RSA_PUBLIC_KEY_TYPE,
|
|
615
|
-
tenantId: this.options.keyRotation.tenantId,
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
async getRotationData() {
|
|
619
|
-
var _a, _b;
|
|
620
|
-
if (!((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.keyRotation) === null || _b === void 0 ? void 0 : _b.isEnabled)) {
|
|
621
|
-
return undefined;
|
|
622
|
-
}
|
|
623
|
-
// Ensure keys are loaded and cached
|
|
624
|
-
if (!this.publicKeyBase64 || !this.keyIdentifier) {
|
|
625
|
-
await this.extractKeysData();
|
|
626
|
-
}
|
|
627
|
-
// The new key is in the pending slot, creating the new key rotation payload
|
|
628
|
-
const pendingRotatedKeysData = await this.extractPendingRotatedKeysData();
|
|
629
|
-
if (pendingRotatedKeysData) {
|
|
630
|
-
const { base64Key: newPublicKey } = await this.getPKRepresentations(pendingRotatedKeysData.publicKey);
|
|
631
|
-
const { privateKey: currentActivePrivateKey } = await this.extractKeysData();
|
|
632
|
-
const initiateRotationPayload = {
|
|
633
|
-
...this.getBaseRotationPayload(),
|
|
634
|
-
newPublicKey,
|
|
635
|
-
createdDate: pendingRotatedKeysData.createdDate,
|
|
636
|
-
newPublicKeyType: RSA_PUBLIC_KEY_TYPE,
|
|
637
|
-
};
|
|
638
|
-
const stringifiedPayload = JSON.stringify(initiateRotationPayload);
|
|
639
|
-
const signature = await this.signPayload(stringifiedPayload, currentActivePrivateKey); // Sign with the current active key
|
|
640
|
-
return { data: stringifiedPayload, signature };
|
|
641
|
-
}
|
|
642
|
-
// This is solution only for the phase 1, until we sign all the requests
|
|
643
|
-
// The first request after the rotation should be signed with the new key to confirm to the backend that the rotation is completed
|
|
644
|
-
// The payload doesn't contain the new key, just the current active one
|
|
645
|
-
const rotatedKeys = await this.extractRotatedKeysData();
|
|
646
|
-
if (rotatedKeys && rotatedKeys.confirmed === false) {
|
|
647
|
-
await this.extractKeysData(); // refresh the cache (in case other tab completed the rotation)
|
|
648
|
-
const stringifiedBasePayload = JSON.stringify(this.getBaseRotationPayload());
|
|
649
|
-
const signature = await this.signPayload(stringifiedBasePayload, rotatedKeys.privateKey); // Sign with the rotated key (currently active)
|
|
650
|
-
return { data: stringifiedBasePayload, signature };
|
|
651
|
-
}
|
|
652
|
-
return undefined;
|
|
653
|
-
}
|
|
654
|
-
async signPayload(payload, privateKey) {
|
|
655
|
-
const signatureBuffer = await signAssymetric(privateKey, payload);
|
|
656
|
-
return this.arrayBufferToBase64(signatureBuffer);
|
|
657
|
-
}
|
|
658
|
-
async handleRotateResponse(response) {
|
|
659
|
-
if (!VALID_ROTATION_RESPONSES.includes(response)) {
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
if (response === INIT_ROTATION_RESPONSE) {
|
|
663
|
-
const pendingKey = await this.extractPendingRotatedKeysData();
|
|
664
|
-
if (pendingKey) {
|
|
665
|
-
// Remove the keys from the rotated slot
|
|
666
|
-
await this.indexedDBClient.delete(this.keysStoreName, this.getRotatedKeysRecordKey());
|
|
667
|
-
// Move the keys from the rotated pending slot to the rotated slot with confirmed: false
|
|
668
|
-
const pendingKeyToStore = { ...pendingKey, confirmed: false };
|
|
669
|
-
await this.indexedDBClient.put(this.keysStoreName, this.getRotatedKeysRecordKey(), pendingKeyToStore);
|
|
670
|
-
await this.indexedDBClient.delete(this.keysStoreName, this.getRotatedKeysRecordKeyPending());
|
|
671
|
-
// Refresh cache
|
|
672
|
-
const { base64Key } = await this.getPKRepresentations(pendingKey.publicKey);
|
|
673
|
-
this.publicKeyBase64 = base64Key;
|
|
674
|
-
this.keyIdentifier = pendingKey.keyIdentifier;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
else if (response === COMPLETED_ROTATION_RESPONSE) {
|
|
678
|
-
const rotatedKey = await this.extractRotatedKeysData();
|
|
679
|
-
if (rotatedKey && rotatedKey.confirmed === false) {
|
|
680
|
-
await this.indexedDBClient.put(this.keysStoreName, this.getRotatedKeysRecordKey(), {
|
|
681
|
-
...rotatedKey,
|
|
682
|
-
confirmed: true,
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
function createCryptoBinding(keysType = 'sign', options) {
|
|
689
|
-
return new CryptoBinding(this, keysType, options);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
var crypto$1 = /*#__PURE__*/Object.freeze({
|
|
693
|
-
__proto__: null,
|
|
694
|
-
createCryptoBinding: createCryptoBinding,
|
|
695
|
-
generateRSAKeyPair: generateRSAKeyPair,
|
|
696
|
-
generateRSASignKeyPair: generateRSASignKeyPair,
|
|
697
|
-
signAssymetric: signAssymetric,
|
|
698
|
-
verifyAssymetric: verifyAssymetric
|
|
699
|
-
});
|
|
700
|
-
|
|
701
|
-
var indexedDB = /*#__PURE__*/Object.freeze({
|
|
702
|
-
__proto__: null
|
|
703
|
-
});
|
|
704
|
-
|
|
705
|
-
const agent = Agent.create((agent) => {
|
|
706
|
-
class TsError extends Error {
|
|
707
|
-
constructor(errorCode, message) {
|
|
708
|
-
super(`${agent.slug}-${errorCode} ${message}`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
class TsInternalError extends TsError {
|
|
712
|
-
constructor(errorCode) {
|
|
713
|
-
super(errorCode, 'Internal error');
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
return { TsError, TsInternalError };
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
var utils = Agent.create(() => ({
|
|
720
|
-
exceptions: agent,
|
|
721
|
-
...agent$1,
|
|
722
|
-
}));
|
|
723
|
-
|
|
724
|
-
class SdkLogger {
|
|
725
|
-
constructor(agent, middlewares = []) {
|
|
726
|
-
this.agent = agent;
|
|
727
|
-
this.middlewares = middlewares;
|
|
728
|
-
this.logs = [];
|
|
729
|
-
}
|
|
730
|
-
info(message, fields) {
|
|
731
|
-
this.pushLog(3, message, fields);
|
|
732
|
-
}
|
|
733
|
-
warn(message, fields) {
|
|
734
|
-
this.pushLog(4, message, fields);
|
|
735
|
-
}
|
|
736
|
-
error(message, fields) {
|
|
737
|
-
this.pushLog(5, message, fields);
|
|
738
|
-
}
|
|
739
|
-
pushLog(severity, message, fields = {}) {
|
|
740
|
-
this.logs.push({
|
|
741
|
-
timestamp: Date.now(),
|
|
742
|
-
module: this.agent.slug,
|
|
743
|
-
severity,
|
|
744
|
-
fields,
|
|
745
|
-
message,
|
|
746
|
-
});
|
|
747
|
-
const middlewareReturnValues = this.middlewares.map((middleware) => middleware(this));
|
|
748
|
-
Promise.all(middlewareReturnValues).catch(() => {
|
|
749
|
-
/* ignore */
|
|
750
|
-
});
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
function createSdkLogger(middlewares = []) {
|
|
754
|
-
return new SdkLogger(this, middlewares);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function consoleMiddleware(logger) {
|
|
758
|
-
const log = logger.logs[logger.logs.length - 1];
|
|
759
|
-
console.log(`${log.severity} ${log.message}`, log.fields);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
var logger$1 = /*#__PURE__*/Object.freeze({
|
|
763
|
-
__proto__: null,
|
|
764
|
-
consoleMiddleware: consoleMiddleware,
|
|
765
|
-
createSdkLogger: createSdkLogger
|
|
766
|
-
});
|
|
767
|
-
|
|
768
|
-
function calculateJsonSize(json) {
|
|
769
|
-
return encodeURI(JSON.stringify(json)).split(/%..|./).length - 1;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
function isValidUrl(url) {
|
|
773
|
-
try {
|
|
774
|
-
new URL(url);
|
|
775
|
-
return true;
|
|
776
|
-
}
|
|
777
|
-
catch (e) {
|
|
778
|
-
return false;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* format URL fragments to ensure correct path, so when both `api/action/something` and `/api/action/something` are valid inputs
|
|
783
|
-
* @param path partial URL
|
|
784
|
-
* @param search optional search params
|
|
785
|
-
* @returns string
|
|
786
|
-
*/
|
|
787
|
-
function urlFragmentFormat(path, search) {
|
|
788
|
-
if (!(path === null || path === void 0 ? void 0 : path.trim()))
|
|
789
|
-
return '';
|
|
790
|
-
if (isValidUrl(path))
|
|
791
|
-
return path;
|
|
792
|
-
const mockDom = 'http://mock.com';
|
|
793
|
-
const mockURL = new URL(mockDom);
|
|
794
|
-
mockURL.search = (search === null || search === void 0 ? void 0 : search.toString()) || '';
|
|
795
|
-
mockURL.pathname = path;
|
|
796
|
-
const res = mockURL.href.replace(mockDom, '');
|
|
797
|
-
return res;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
const REQUEST_HEADERS = {
|
|
801
|
-
'Content-Type': 'application/json',
|
|
802
|
-
'X-TS-client-time': new Date().toUTCString(),
|
|
803
|
-
'X-TS-ua': navigator.userAgent,
|
|
804
|
-
};
|
|
805
|
-
function init(method, body, headers) {
|
|
806
|
-
var _a;
|
|
807
|
-
const size = calculateJsonSize(body || {});
|
|
808
|
-
const sizeHeader = {
|
|
809
|
-
'X-TS-body-size': String(size),
|
|
810
|
-
};
|
|
811
|
-
return {
|
|
812
|
-
method,
|
|
813
|
-
headers: {
|
|
814
|
-
...sizeHeader,
|
|
815
|
-
...REQUEST_HEADERS,
|
|
816
|
-
...(headers || {}),
|
|
817
|
-
},
|
|
818
|
-
body: (_a = (body && JSON.stringify(body || {}))) !== null && _a !== void 0 ? _a : undefined,
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
// implementation of HTTP fetch API. supports POST/GET and returns a promise of a result/failure.
|
|
822
|
-
// full structure for response/failure TBD.
|
|
823
|
-
function httpFetchBuilder(path, httpMethod, body, params, headers) {
|
|
824
|
-
const reqUrl = urlFragmentFormat(path, params);
|
|
825
|
-
const requestInit = init(httpMethod, body, headers);
|
|
826
|
-
return fetch(reqUrl, requestInit);
|
|
827
|
-
}
|
|
828
|
-
async function fetchRequest(path, httpMethod, body, params, headers) {
|
|
829
|
-
let response;
|
|
830
|
-
if (httpMethod === 'GET' || httpMethod === 'DELETE') {
|
|
831
|
-
response = await httpFetchBuilder(path, httpMethod, body, params, headers);
|
|
832
|
-
}
|
|
833
|
-
else {
|
|
834
|
-
response = await httpFetchBuilder(path, httpMethod, body, params, headers);
|
|
835
|
-
}
|
|
836
|
-
// when debugging, check the response status code and response body
|
|
837
|
-
// console.log('response: ', response);
|
|
838
|
-
if (!response.ok) {
|
|
839
|
-
throw new Error('Request failed');
|
|
840
|
-
}
|
|
841
|
-
return response;
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* constructs a `GET` request
|
|
845
|
-
* @param path API path
|
|
846
|
-
* @param params request parameters
|
|
847
|
-
* @returns a promise of the response body if successful and throw an error if failed
|
|
848
|
-
*/
|
|
849
|
-
async function httpGet(path, params, headers) {
|
|
850
|
-
const httpRequest = await fetchRequest(path, 'GET', undefined, params, headers);
|
|
851
|
-
return { data: await httpRequest.json(), ...httpRequest, headers: httpRequest.headers };
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* constructs a `POST` request
|
|
855
|
-
* @param path API path
|
|
856
|
-
* @param data content of the request
|
|
857
|
-
* @param params request parameters
|
|
858
|
-
* @returns a promise of the response body if successful and throw an error if failed
|
|
859
|
-
*/
|
|
860
|
-
async function httpPost(path, data, params, headers) {
|
|
861
|
-
const httpRequest = await fetchRequest(path, 'POST', data, params, headers);
|
|
862
|
-
return { data: await httpRequest.json(), ...httpRequest, headers: httpRequest.headers };
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* constructs a `PUT` request
|
|
866
|
-
* @param path API path
|
|
867
|
-
* @param data content of the request
|
|
868
|
-
* @param params request parameters
|
|
869
|
-
* @returns a promise of the response body if successful and throw an error if failed
|
|
870
|
-
*/
|
|
871
|
-
async function httpPut(path, data, params, headers) {
|
|
872
|
-
const httpRequest = await fetchRequest(path, 'PUT', data, params, headers);
|
|
873
|
-
return { data: await httpRequest.json(), ...httpRequest, headers: httpRequest.headers };
|
|
874
|
-
}
|
|
875
|
-
/**
|
|
876
|
-
* constructs a `DELETE` request
|
|
877
|
-
* @param path API path
|
|
878
|
-
* @param body content of the request
|
|
879
|
-
* @param params request parameters
|
|
880
|
-
* @returns a promise of the response body if successful and throw an error if failed
|
|
881
|
-
*/
|
|
882
|
-
async function httpDelete(path, headers) {
|
|
883
|
-
const httpRequest = await fetchRequest(path, 'DELETE', undefined, undefined, headers);
|
|
884
|
-
return { data: await httpRequest.json(), ...httpRequest, headers: httpRequest.headers };
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
var http = /*#__PURE__*/Object.freeze({
|
|
888
|
-
__proto__: null,
|
|
889
|
-
httpDelete: httpDelete,
|
|
890
|
-
httpGet: httpGet,
|
|
891
|
-
httpPost: httpPost,
|
|
892
|
-
httpPut: httpPut,
|
|
893
|
-
init: init
|
|
894
|
-
});
|
|
895
|
-
|
|
896
|
-
var Common = Agent.create(() => ({
|
|
897
|
-
events,
|
|
898
|
-
moduleMetadata,
|
|
899
|
-
mainEntry,
|
|
900
|
-
utils,
|
|
901
|
-
storage,
|
|
902
|
-
crypto: crypto$1,
|
|
903
|
-
indexedDB,
|
|
904
|
-
logger: logger$1,
|
|
905
|
-
http,
|
|
906
|
-
}));
|
|
907
|
-
|
|
908
|
-
class EncodingUtils {
|
|
909
|
-
static arrayBufferToBase64(buffer) {
|
|
910
|
-
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
|
|
911
|
-
}
|
|
912
|
-
static base64ToArrayBuffer(base64) {
|
|
913
|
-
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
|
914
|
-
}
|
|
915
|
-
static stringToBase64(str) {
|
|
916
|
-
return btoa(str);
|
|
917
|
-
}
|
|
918
|
-
static jsonToBase64(json) {
|
|
919
|
-
const res = JSON.stringify(json);
|
|
920
|
-
return btoa(res);
|
|
921
|
-
}
|
|
922
|
-
static base64ToJson(str) {
|
|
923
|
-
const res = atob(str);
|
|
924
|
-
return JSON.parse(res);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
const logger = {
|
|
929
|
-
log: console.log,
|
|
930
|
-
error: console.error,
|
|
931
|
-
};
|
|
932
|
-
|
|
933
|
-
/**
|
|
934
|
-
* @enum
|
|
935
|
-
*/
|
|
936
|
-
var ErrorCode;
|
|
937
|
-
(function (ErrorCode) {
|
|
938
|
-
/**
|
|
939
|
-
* Either the SDK init call failed or another function was called before initializing the SDK
|
|
940
|
-
*/
|
|
941
|
-
ErrorCode["NotInitialized"] = "not_initialized";
|
|
942
|
-
/**
|
|
943
|
-
* When the call to {@link WebauthnApis.startAuthentication} failed
|
|
944
|
-
*/
|
|
945
|
-
ErrorCode["AuthenticationFailed"] = "authentication_failed";
|
|
946
|
-
/**
|
|
947
|
-
* When {@link WebauthnAuthenticationFlows.modal authenticate.modal} or {@link AutofillHandlers.activate authenticate.autofill.activate} is called and the modal is closed by the user
|
|
948
|
-
*/
|
|
949
|
-
ErrorCode["AuthenticationAbortedTimeout"] = "authentication_aborted_timeout";
|
|
950
|
-
/**
|
|
951
|
-
* When {@link register} is called and the modal is closed when reaching the timeout
|
|
952
|
-
*/
|
|
953
|
-
ErrorCode["AuthenticationCanceled"] = "webauthn_authentication_canceled";
|
|
954
|
-
/**
|
|
955
|
-
* When the call to {@link WebauthnApis.startRegistration} failed
|
|
956
|
-
*/
|
|
957
|
-
ErrorCode["RegistrationFailed"] = "registration_failed";
|
|
958
|
-
/**
|
|
959
|
-
/ When The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.
|
|
960
|
-
*/
|
|
961
|
-
ErrorCode["AlreadyRegistered"] = "username_already_registered";
|
|
962
|
-
/**
|
|
963
|
-
* When {@link register} is called and the modal is closed by the user
|
|
964
|
-
*/
|
|
965
|
-
ErrorCode["RegistrationAbortedTimeout"] = "registration_aborted_timeout";
|
|
966
|
-
/**
|
|
967
|
-
* When {@link register} is called and the modal is closed when reaching the timeout
|
|
968
|
-
*/
|
|
969
|
-
ErrorCode["RegistrationCanceled"] = "webauthn_registration_canceled";
|
|
970
|
-
/**
|
|
971
|
-
* Passkey autofill authentication was aborted by {@link AutofillHandlers.abort}
|
|
972
|
-
*/
|
|
973
|
-
ErrorCode["AutofillAuthenticationAborted"] = "autofill_authentication_aborted";
|
|
974
|
-
/**
|
|
975
|
-
* Passkey authentication is already active. To start a new authentication, abort the current one first by calling {@link AutofillHandlers.abort}
|
|
976
|
-
*/
|
|
977
|
-
ErrorCode["AuthenticationProcessAlreadyActive"] = "authentication_process_already_active";
|
|
978
|
-
/**
|
|
979
|
-
* The ApprovalData parameter was sent in the wrong format
|
|
980
|
-
*/
|
|
981
|
-
ErrorCode["InvalidApprovalData"] = "invalid_approval_data";
|
|
982
|
-
/**
|
|
983
|
-
* When the call to {@link WebauthnApis.initCrossDeviceAuthentication} failed */
|
|
984
|
-
ErrorCode["FailedToInitCrossDeviceSession"] = "cross_device_init_failed";
|
|
985
|
-
/**
|
|
986
|
-
* When the call to {@link WebauthnApis.getCrossDeviceTicketStatus} failed */
|
|
987
|
-
ErrorCode["FailedToGetCrossDeviceStatus"] = "cross_device_status_failed";
|
|
988
|
-
/**
|
|
989
|
-
* When the SDK operation fails on an unhandled error
|
|
990
|
-
*/
|
|
991
|
-
ErrorCode["Unknown"] = "unknown";
|
|
992
|
-
})(ErrorCode || (ErrorCode = {}));
|
|
993
|
-
|
|
994
|
-
class BaseSdkError extends Error {
|
|
995
|
-
constructor(message, additionalData) {
|
|
996
|
-
super(message);
|
|
997
|
-
this.errorCode = ErrorCode.NotInitialized;
|
|
998
|
-
this.data = additionalData;
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
class NotInitializedError extends BaseSdkError {
|
|
1002
|
-
constructor(message, additionalData) {
|
|
1003
|
-
super(message !== null && message !== void 0 ? message : 'WebAuthnSdk is not initialized', additionalData);
|
|
1004
|
-
this.errorCode = ErrorCode.NotInitialized;
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
class AuthenticationFailedError extends BaseSdkError {
|
|
1008
|
-
constructor(message, additionalData) {
|
|
1009
|
-
super(message !== null && message !== void 0 ? message : 'Authentication failed with an error', additionalData);
|
|
1010
|
-
this.errorCode = ErrorCode.AuthenticationFailed;
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
class AuthenticationCanceledError extends BaseSdkError {
|
|
1014
|
-
constructor(message, additionalData) {
|
|
1015
|
-
super(message !== null && message !== void 0 ? message : 'Authentication was canceled by the user or got timeout', additionalData);
|
|
1016
|
-
this.errorCode = ErrorCode.AuthenticationCanceled;
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
class RegistrationFailedError extends BaseSdkError {
|
|
1020
|
-
constructor(message, additionalData) {
|
|
1021
|
-
super(message !== null && message !== void 0 ? message : 'Registration failed with an error', additionalData);
|
|
1022
|
-
this.errorCode = ErrorCode.RegistrationFailed;
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
class RegistrationCanceledError extends BaseSdkError {
|
|
1026
|
-
constructor(message, additionalData) {
|
|
1027
|
-
super(message !== null && message !== void 0 ? message : 'Registration was canceled by the user or got timeout', additionalData);
|
|
1028
|
-
this.errorCode = ErrorCode.RegistrationCanceled;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
class AutofillAuthenticationAbortedError extends BaseSdkError {
|
|
1032
|
-
constructor(message) {
|
|
1033
|
-
super(message !== null && message !== void 0 ? message : 'Autofill flow was aborted');
|
|
1034
|
-
this.errorCode = ErrorCode.AutofillAuthenticationAborted;
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
class OperationTimeoutAbortedError extends BaseSdkError {
|
|
1038
|
-
constructor(message) {
|
|
1039
|
-
super(message !== null && message !== void 0 ? message : 'Operation was aborted by timeout');
|
|
1040
|
-
this.errorCode = ErrorCode.AutofillAuthenticationAborted;
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
class AlreadyRegisteredError extends BaseSdkError {
|
|
1044
|
-
constructor(message) {
|
|
1045
|
-
super(message !== null && message !== void 0 ? message : 'Passkey with this username is already registered with the relying party.');
|
|
1046
|
-
this.errorCode = ErrorCode.AlreadyRegistered;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
class AuthenticationProcessAlreadyActiveError extends BaseSdkError {
|
|
1050
|
-
constructor(message, additionalData) {
|
|
1051
|
-
super(message !== null && message !== void 0 ? message : 'Authentication process is already active', additionalData);
|
|
1052
|
-
this.errorCode = ErrorCode.AuthenticationProcessAlreadyActive;
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
class InvalidApprovalDataError extends BaseSdkError {
|
|
1056
|
-
constructor(message, additionalData) {
|
|
1057
|
-
super(message !== null && message !== void 0 ? message : 'Invalid approval data', additionalData);
|
|
1058
|
-
this.errorCode = ErrorCode.InvalidApprovalData;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
class FailedToInitCrossDeviceAuthenticationError extends BaseSdkError {
|
|
1062
|
-
constructor(message, additionalData) {
|
|
1063
|
-
super(message !== null && message !== void 0 ? message : 'Failed to init cross device authentication', additionalData);
|
|
1064
|
-
this.errorCode = ErrorCode.FailedToInitCrossDeviceSession;
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
class FailedToGetCrossDeviceStatusError extends BaseSdkError {
|
|
1068
|
-
constructor(message, additionalData) {
|
|
1069
|
-
super(message !== null && message !== void 0 ? message : 'Failed to get cross device status', additionalData);
|
|
1070
|
-
this.errorCode = ErrorCode.FailedToGetCrossDeviceStatus;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
function isBaseSdkError(error) {
|
|
1074
|
-
return error.errorCode && Object.values(ErrorCode).includes(error.errorCode);
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
/**
|
|
1078
|
-
* This is a typed (Storage)[https://developer.mozilla.org/en-US/docs/Web/API/Storage] wrapper.
|
|
1079
|
-
* Items are saved in either localstorage or sessionstorage depending on their key. when retrieving an
|
|
1080
|
-
* item, it is automatically fetched from the correct storage solution.
|
|
1081
|
-
*/
|
|
1082
|
-
var KeyPersistance;
|
|
1083
|
-
(function (KeyPersistance) {
|
|
1084
|
-
KeyPersistance[KeyPersistance["persistent"] = 0] = "persistent";
|
|
1085
|
-
KeyPersistance[KeyPersistance["session"] = 1] = "session";
|
|
1086
|
-
})(KeyPersistance || (KeyPersistance = {}));
|
|
1087
|
-
class SdkStorage {
|
|
1088
|
-
static get(keyName) {
|
|
1089
|
-
return (SdkStorage.getStorageMedium(SdkStorage.allowedKeys[keyName]).getItem(SdkStorage.getStorageKey(keyName)) ||
|
|
1090
|
-
undefined);
|
|
1091
|
-
}
|
|
1092
|
-
static set(keyName, value) {
|
|
1093
|
-
return SdkStorage.getStorageMedium(SdkStorage.allowedKeys[keyName]).setItem(SdkStorage.getStorageKey(keyName), value);
|
|
1094
|
-
}
|
|
1095
|
-
static remove(keyName) {
|
|
1096
|
-
SdkStorage.getStorageMedium(SdkStorage.allowedKeys[keyName]).removeItem(SdkStorage.getStorageKey(keyName));
|
|
1097
|
-
}
|
|
1098
|
-
static clear(retainConfiguration) {
|
|
1099
|
-
// We don't want to actually call Storage.clear, since that may clear some of the RP's data as well.
|
|
1100
|
-
for (const [key, keyPersistence] of Object.entries(SdkStorage.allowedKeys)) {
|
|
1101
|
-
const typedKey = key;
|
|
1102
|
-
if (retainConfiguration && this.configurationKeys.includes(typedKey)) {
|
|
1103
|
-
continue;
|
|
1104
|
-
}
|
|
1105
|
-
SdkStorage.getStorageMedium(keyPersistence).removeItem(SdkStorage.getStorageKey(typedKey));
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
static getStorageKey(keyName) {
|
|
1109
|
-
return `WebAuthnSdk:${keyName}`;
|
|
1110
|
-
}
|
|
1111
|
-
static getStorageMedium(keyPersistance) {
|
|
1112
|
-
switch (keyPersistance) {
|
|
1113
|
-
case KeyPersistance.session:
|
|
1114
|
-
return sessionStorage;
|
|
1115
|
-
default:
|
|
1116
|
-
return localStorage;
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
SdkStorage.allowedKeys = {
|
|
1121
|
-
// please please please keep this in alphabetical order
|
|
1122
|
-
clientId: KeyPersistance.session,
|
|
1123
|
-
};
|
|
1124
|
-
SdkStorage.configurationKeys = [
|
|
1125
|
-
// please please please keep this in alphabetical order
|
|
1126
|
-
'clientId',
|
|
1127
|
-
];
|
|
1128
|
-
|
|
1129
|
-
const NEW_API_PATH_PREFIX = '/cis';
|
|
1130
|
-
class SdkApiClient {
|
|
1131
|
-
static isNewApiDomain(hostname) {
|
|
1132
|
-
return (hostname &&
|
|
1133
|
-
(this.newApiDomains.includes(hostname) ||
|
|
1134
|
-
(hostname.startsWith('api.') && hostname.endsWith('.transmitsecurity.io'))));
|
|
1135
|
-
}
|
|
1136
|
-
static dnsPrefetch(serverPath) {
|
|
1137
|
-
const hint = document.createElement('link');
|
|
1138
|
-
hint.rel = 'dns-prefetch';
|
|
1139
|
-
hint.href = serverPath;
|
|
1140
|
-
document.head.appendChild(hint);
|
|
1141
|
-
}
|
|
1142
|
-
static preconnect(serverPath, crossOrigin) {
|
|
1143
|
-
const hint = document.createElement('link');
|
|
1144
|
-
hint.rel = 'preconnect';
|
|
1145
|
-
hint.href = serverPath;
|
|
1146
|
-
if (crossOrigin) {
|
|
1147
|
-
hint.crossOrigin = 'anonymous';
|
|
1148
|
-
}
|
|
1149
|
-
document.head.appendChild(hint);
|
|
1150
|
-
}
|
|
1151
|
-
static warmupConnection(serverPath) {
|
|
1152
|
-
// following https://dev.to/crenshaw_dev/when-the-browser-can-t-take-a-preconnect-hint-6dn
|
|
1153
|
-
this.dnsPrefetch(serverPath);
|
|
1154
|
-
this.preconnect(serverPath, false);
|
|
1155
|
-
this.preconnect(serverPath, true);
|
|
1156
|
-
}
|
|
1157
|
-
static init(clientId, options) {
|
|
1158
|
-
var _a, _b;
|
|
1159
|
-
try {
|
|
1160
|
-
this._serverPath = new URL(options.serverPath);
|
|
1161
|
-
if (this.isNewApiDomain((_a = this._serverPath) === null || _a === void 0 ? void 0 : _a.hostname)) {
|
|
1162
|
-
this.warmupConnection(this._serverPath.origin);
|
|
1163
|
-
}
|
|
1164
|
-
this._apiPaths = (_b = options.webauthnApiPaths) !== null && _b !== void 0 ? _b : this.getDefaultPaths();
|
|
1165
|
-
this._clientId = clientId;
|
|
1166
|
-
SdkStorage.set('clientId', clientId);
|
|
1167
|
-
}
|
|
1168
|
-
catch (error) {
|
|
1169
|
-
throw new NotInitializedError('Invalid options.serverPath', { error });
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
static getDefaultPaths() {
|
|
1173
|
-
var _a;
|
|
1174
|
-
// In case the user initializes the SDK with the new API gateway, we need to use the new API paths
|
|
1175
|
-
const pathPrefix = this.isNewApiDomain((_a = this._serverPath) === null || _a === void 0 ? void 0 : _a.hostname) ? NEW_API_PATH_PREFIX : '';
|
|
1176
|
-
return {
|
|
1177
|
-
startAuthentication: `${pathPrefix}/v1/auth/webauthn/authenticate/start`,
|
|
1178
|
-
startRegistration: `${pathPrefix}/v1/auth/webauthn/register/start`,
|
|
1179
|
-
initCrossDeviceAuthentication: `${pathPrefix}/v1/auth/webauthn/cross-device/authenticate/init`,
|
|
1180
|
-
startCrossDeviceAuthentication: `${pathPrefix}/v1/auth/webauthn/cross-device/authenticate/start`,
|
|
1181
|
-
startCrossDeviceRegistration: `${pathPrefix}/v1/auth/webauthn/cross-device/register/start`,
|
|
1182
|
-
getCrossDeviceTicketStatus: `${pathPrefix}/v1/auth/webauthn/cross-device/status`,
|
|
1183
|
-
attachDeviceToCrossDeviceSession: `${pathPrefix}/v1/auth/webauthn/cross-device/attach-device`,
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
|
-
static getApiPaths() {
|
|
1187
|
-
return this._apiPaths;
|
|
1188
|
-
}
|
|
1189
|
-
static async sendRequest(path, request, query) {
|
|
1190
|
-
logger.log(`[WebAuthn SDK] Calling ${request.method} ${path}...`);
|
|
1191
|
-
const requestUrl = new URL(this._serverPath);
|
|
1192
|
-
requestUrl.pathname = path;
|
|
1193
|
-
if (query) {
|
|
1194
|
-
requestUrl.search = query;
|
|
1195
|
-
}
|
|
1196
|
-
return fetch(requestUrl.toString(), request);
|
|
1197
|
-
}
|
|
1198
|
-
static async startRegistration(params) {
|
|
1199
|
-
const response = await this.sendRequest(this._apiPaths.startRegistration, {
|
|
1200
|
-
method: 'POST',
|
|
1201
|
-
headers: {
|
|
1202
|
-
'Content-Type': 'application/json',
|
|
1203
|
-
},
|
|
1204
|
-
body: JSON.stringify({
|
|
1205
|
-
client_id: this.getValidatedClientId(),
|
|
1206
|
-
username: params.username,
|
|
1207
|
-
display_name: params.displayName,
|
|
1208
|
-
...(params.timeout && { timeout: params.timeout }),
|
|
1209
|
-
...(params.limitSingleCredentialToDevice && {
|
|
1210
|
-
limit_single_credential_to_device: params.limitSingleCredentialToDevice,
|
|
1211
|
-
}),
|
|
1212
|
-
}),
|
|
1213
|
-
});
|
|
1214
|
-
if (!(response === null || response === void 0 ? void 0 : response.ok)) {
|
|
1215
|
-
throw new AuthenticationFailedError('Failed to start registration', response === null || response === void 0 ? void 0 : response.body);
|
|
1216
|
-
}
|
|
1217
|
-
return (await response.json());
|
|
1218
|
-
}
|
|
1219
|
-
static async startAuthentication(params) {
|
|
1220
|
-
const response = await this.sendRequest(this._apiPaths.startAuthentication, {
|
|
1221
|
-
method: 'POST',
|
|
1222
|
-
headers: {
|
|
1223
|
-
'Content-Type': 'application/json',
|
|
1224
|
-
},
|
|
1225
|
-
body: JSON.stringify({
|
|
1226
|
-
client_id: this.getValidatedClientId(),
|
|
1227
|
-
...(params.username && { username: params.username }),
|
|
1228
|
-
...(params.approvalData && { approval_data: params.approvalData }),
|
|
1229
|
-
...(params.timeout && { timeout: params.timeout }),
|
|
1230
|
-
}),
|
|
1231
|
-
});
|
|
1232
|
-
if (!(response === null || response === void 0 ? void 0 : response.ok)) {
|
|
1233
|
-
throw new AuthenticationFailedError('Failed to start authentication', response === null || response === void 0 ? void 0 : response.body);
|
|
1234
|
-
}
|
|
1235
|
-
return (await response.json());
|
|
1236
|
-
}
|
|
1237
|
-
static async initCrossDeviceAuthentication(params) {
|
|
1238
|
-
const response = await this.sendRequest(this._apiPaths.initCrossDeviceAuthentication, {
|
|
1239
|
-
method: 'POST',
|
|
1240
|
-
headers: {
|
|
1241
|
-
'Content-Type': 'application/json',
|
|
1242
|
-
},
|
|
1243
|
-
body: JSON.stringify({
|
|
1244
|
-
client_id: this.getValidatedClientId(),
|
|
1245
|
-
...(params.username && { username: params.username }),
|
|
1246
|
-
...(params.approvalData && { approval_data: params.approvalData }),
|
|
1247
|
-
}),
|
|
1248
|
-
});
|
|
1249
|
-
if (!(response === null || response === void 0 ? void 0 : response.ok)) {
|
|
1250
|
-
throw new FailedToInitCrossDeviceAuthenticationError(undefined, response === null || response === void 0 ? void 0 : response.body);
|
|
1251
|
-
}
|
|
1252
|
-
return (await response.json());
|
|
1253
|
-
}
|
|
1254
|
-
static async getCrossDeviceTicketStatus(params) {
|
|
1255
|
-
const response = await this.sendRequest(this._apiPaths.getCrossDeviceTicketStatus, {
|
|
1256
|
-
method: 'GET',
|
|
1257
|
-
}, `cross_device_ticket_id=${params.ticketId}`);
|
|
1258
|
-
if (!(response === null || response === void 0 ? void 0 : response.ok)) {
|
|
1259
|
-
throw new FailedToGetCrossDeviceStatusError(undefined, response === null || response === void 0 ? void 0 : response.body);
|
|
1260
|
-
}
|
|
1261
|
-
return (await response.json());
|
|
1262
|
-
}
|
|
1263
|
-
static async startCrossDeviceAuthentication(params) {
|
|
1264
|
-
const response = await this.sendRequest(this._apiPaths.startCrossDeviceAuthentication, {
|
|
1265
|
-
method: 'POST',
|
|
1266
|
-
headers: {
|
|
1267
|
-
'Content-Type': 'application/json',
|
|
1268
|
-
},
|
|
1269
|
-
body: JSON.stringify({
|
|
1270
|
-
cross_device_ticket_id: params.ticketId,
|
|
1271
|
-
}),
|
|
1272
|
-
});
|
|
1273
|
-
if (!(response === null || response === void 0 ? void 0 : response.ok)) {
|
|
1274
|
-
throw new AuthenticationFailedError('Failed to start cross device authentication', response === null || response === void 0 ? void 0 : response.body);
|
|
1275
|
-
}
|
|
1276
|
-
return (await response.json());
|
|
1277
|
-
}
|
|
1278
|
-
static async startCrossDeviceRegistration(params) {
|
|
1279
|
-
const response = await this.sendRequest(this._apiPaths.startCrossDeviceRegistration, {
|
|
1280
|
-
method: 'POST',
|
|
1281
|
-
headers: {
|
|
1282
|
-
'Content-Type': 'application/json',
|
|
1283
|
-
},
|
|
1284
|
-
body: JSON.stringify({
|
|
1285
|
-
cross_device_ticket_id: params.ticketId,
|
|
1286
|
-
}),
|
|
1287
|
-
});
|
|
1288
|
-
if (!(response === null || response === void 0 ? void 0 : response.ok)) {
|
|
1289
|
-
throw new RegistrationFailedError('Failed to start cross device registration', response === null || response === void 0 ? void 0 : response.body);
|
|
1290
|
-
}
|
|
1291
|
-
return (await response.json());
|
|
1292
|
-
}
|
|
1293
|
-
static async attachDeviceToCrossDeviceSession(params) {
|
|
1294
|
-
const response = await this.sendRequest(this._apiPaths.attachDeviceToCrossDeviceSession, {
|
|
1295
|
-
method: 'POST',
|
|
1296
|
-
headers: {
|
|
1297
|
-
'Content-Type': 'application/json',
|
|
1298
|
-
},
|
|
1299
|
-
body: JSON.stringify({
|
|
1300
|
-
cross_device_ticket_id: params.ticketId,
|
|
1301
|
-
}),
|
|
1302
|
-
});
|
|
1303
|
-
if (!(response === null || response === void 0 ? void 0 : response.ok)) {
|
|
1304
|
-
throw new RegistrationFailedError('Failed to attach device to cross device session', response === null || response === void 0 ? void 0 : response.body);
|
|
1305
|
-
}
|
|
1306
|
-
return (await response.json());
|
|
1307
|
-
}
|
|
1308
|
-
static getValidatedClientId() {
|
|
1309
|
-
var _a;
|
|
1310
|
-
const clientId = (_a = this._clientId) !== null && _a !== void 0 ? _a : SdkStorage.get('clientId');
|
|
1311
|
-
if (!clientId) {
|
|
1312
|
-
throw new NotInitializedError('Missing clientId');
|
|
1313
|
-
}
|
|
1314
|
-
return clientId;
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
SdkApiClient.newApiDomains = ['api.idsec-dev.com', 'api.idsec-stg.com'];
|
|
1318
|
-
|
|
1319
|
-
/**
|
|
1320
|
-
* WebAuthn registration response interfaces
|
|
1321
|
-
*/
|
|
1322
|
-
/**
|
|
1323
|
-
* WebAuthn authentication mediation type
|
|
1324
|
-
*/
|
|
1325
|
-
var AuthenticationMediationType;
|
|
1326
|
-
(function (AuthenticationMediationType) {
|
|
1327
|
-
AuthenticationMediationType["InputAutofill"] = "input-autofill";
|
|
1328
|
-
AuthenticationMediationType["Modal"] = "modal";
|
|
1329
|
-
})(AuthenticationMediationType || (AuthenticationMediationType = {}));
|
|
1330
|
-
/**
|
|
1331
|
-
* WebAuthn cross device interfaces
|
|
1332
|
-
*/
|
|
1333
|
-
exports.WebauthnCrossDeviceStatus = void 0;
|
|
1334
|
-
(function (WebauthnCrossDeviceStatus) {
|
|
1335
|
-
WebauthnCrossDeviceStatus["Pending"] = "pending";
|
|
1336
|
-
WebauthnCrossDeviceStatus["Scanned"] = "scanned";
|
|
1337
|
-
WebauthnCrossDeviceStatus["Success"] = "success";
|
|
1338
|
-
WebauthnCrossDeviceStatus["Error"] = "error";
|
|
1339
|
-
WebauthnCrossDeviceStatus["Timeout"] = "timeout";
|
|
1340
|
-
WebauthnCrossDeviceStatus["Aborted"] = "aborted";
|
|
1341
|
-
})(exports.WebauthnCrossDeviceStatus || (exports.WebauthnCrossDeviceStatus = {}));
|
|
1342
|
-
|
|
1343
|
-
var SdkErrorParser;
|
|
1344
|
-
(function (SdkErrorParser) {
|
|
1345
|
-
SdkErrorParser.toAuthenticationError = (error) => {
|
|
1346
|
-
if (isBaseSdkError(error)) {
|
|
1347
|
-
return error;
|
|
1348
|
-
}
|
|
1349
|
-
if (error.name === 'NotAllowedError') {
|
|
1350
|
-
return new AuthenticationCanceledError();
|
|
1351
|
-
}
|
|
1352
|
-
if (error.name === 'OperationError') {
|
|
1353
|
-
return new AuthenticationProcessAlreadyActiveError(error.message);
|
|
1354
|
-
}
|
|
1355
|
-
if (error.name === 'SecurityError') {
|
|
1356
|
-
return new AuthenticationFailedError(error.message);
|
|
1357
|
-
}
|
|
1358
|
-
if (error === ErrorCode.AuthenticationAbortedTimeout) {
|
|
1359
|
-
return new OperationTimeoutAbortedError();
|
|
1360
|
-
}
|
|
1361
|
-
if (error.name === 'AbortError' || error === ErrorCode.AutofillAuthenticationAborted) {
|
|
1362
|
-
return new AutofillAuthenticationAbortedError();
|
|
1363
|
-
}
|
|
1364
|
-
return new AuthenticationFailedError('Something went wrong during authentication', { error });
|
|
1365
|
-
};
|
|
1366
|
-
SdkErrorParser.toRegistrationError = (error) => {
|
|
1367
|
-
if (isBaseSdkError(error)) {
|
|
1368
|
-
return error;
|
|
1369
|
-
}
|
|
1370
|
-
if (error.name === 'NotAllowedError') {
|
|
1371
|
-
return new RegistrationCanceledError();
|
|
1372
|
-
}
|
|
1373
|
-
if (error.name === 'SecurityError') {
|
|
1374
|
-
return new RegistrationFailedError(error.message);
|
|
1375
|
-
}
|
|
1376
|
-
if (error.name === 'InvalidStateError') {
|
|
1377
|
-
return new AlreadyRegisteredError();
|
|
1378
|
-
}
|
|
1379
|
-
if (error === ErrorCode.RegistrationAbortedTimeout) {
|
|
1380
|
-
return new OperationTimeoutAbortedError();
|
|
1381
|
-
}
|
|
1382
|
-
return new RegistrationFailedError('Something went wrong during registration', { error });
|
|
1383
|
-
};
|
|
1384
|
-
})(SdkErrorParser || (SdkErrorParser = {}));
|
|
1385
|
-
|
|
1386
|
-
var WebauthnParser;
|
|
1387
|
-
(function (WebauthnParser) {
|
|
1388
|
-
WebauthnParser.processCredentialRequestOptions = (credentialRequestOptions) => {
|
|
1389
|
-
return {
|
|
1390
|
-
...credentialRequestOptions,
|
|
1391
|
-
challenge: EncodingUtils.base64ToArrayBuffer(credentialRequestOptions.challenge),
|
|
1392
|
-
allowCredentials: credentialRequestOptions.allowCredentials.map((ac) => ({
|
|
1393
|
-
...ac,
|
|
1394
|
-
id: EncodingUtils.base64ToArrayBuffer(ac.id),
|
|
1395
|
-
})),
|
|
1396
|
-
};
|
|
1397
|
-
};
|
|
1398
|
-
WebauthnParser.processCredentialCreationOptions = (rawCredentialCreationOptions, options) => {
|
|
1399
|
-
var _a;
|
|
1400
|
-
const credentialCreationOptions = JSON.parse(JSON.stringify(rawCredentialCreationOptions));
|
|
1401
|
-
credentialCreationOptions.challenge = EncodingUtils.base64ToArrayBuffer(rawCredentialCreationOptions.challenge);
|
|
1402
|
-
credentialCreationOptions.user.id = EncodingUtils.base64ToArrayBuffer(rawCredentialCreationOptions.user.id);
|
|
1403
|
-
if (options === null || options === void 0 ? void 0 : options.limitSingleCredentialToDevice) {
|
|
1404
|
-
credentialCreationOptions.excludeCredentials = (_a = rawCredentialCreationOptions.excludeCredentials) === null || _a === void 0 ? void 0 : _a.map((ac) => ({
|
|
1405
|
-
...ac,
|
|
1406
|
-
id: EncodingUtils.base64ToArrayBuffer(ac.id),
|
|
1407
|
-
}));
|
|
1408
|
-
}
|
|
1409
|
-
if (options === null || options === void 0 ? void 0 : options.registerAsDiscoverable) {
|
|
1410
|
-
credentialCreationOptions.authenticatorSelection.residentKey = 'preferred';
|
|
1411
|
-
credentialCreationOptions.authenticatorSelection.requireResidentKey = true;
|
|
1412
|
-
}
|
|
1413
|
-
else {
|
|
1414
|
-
credentialCreationOptions.authenticatorSelection.residentKey = 'discouraged';
|
|
1415
|
-
credentialCreationOptions.authenticatorSelection.requireResidentKey = false;
|
|
1416
|
-
}
|
|
1417
|
-
credentialCreationOptions.authenticatorSelection.authenticatorAttachment = (options === null || options === void 0 ? void 0 : options.allowCrossPlatformAuthenticators)
|
|
1418
|
-
? undefined
|
|
1419
|
-
: 'platform';
|
|
1420
|
-
return credentialCreationOptions;
|
|
1421
|
-
};
|
|
1422
|
-
WebauthnParser.encodeAuthenticationResult = (credential) => {
|
|
1423
|
-
const { authenticatorAttachment } = credential;
|
|
1424
|
-
const credentialResponse = credential.response;
|
|
1425
|
-
return {
|
|
1426
|
-
id: credential.id,
|
|
1427
|
-
rawId: EncodingUtils.arrayBufferToBase64(credential.rawId),
|
|
1428
|
-
response: {
|
|
1429
|
-
authenticatorData: EncodingUtils.arrayBufferToBase64(credentialResponse.authenticatorData),
|
|
1430
|
-
clientDataJSON: EncodingUtils.arrayBufferToBase64(credentialResponse.clientDataJSON),
|
|
1431
|
-
signature: EncodingUtils.arrayBufferToBase64(credentialResponse.signature),
|
|
1432
|
-
userHandle: EncodingUtils.arrayBufferToBase64(credentialResponse.userHandle),
|
|
1433
|
-
},
|
|
1434
|
-
authenticatorAttachment,
|
|
1435
|
-
type: credential.type,
|
|
1436
|
-
};
|
|
1437
|
-
};
|
|
1438
|
-
WebauthnParser.encodeRegistrationResult = (credential) => {
|
|
1439
|
-
const { authenticatorAttachment } = credential;
|
|
1440
|
-
const credentialResponse = credential.response;
|
|
1441
|
-
return {
|
|
1442
|
-
id: credential.id,
|
|
1443
|
-
rawId: EncodingUtils.arrayBufferToBase64(credential.rawId),
|
|
1444
|
-
response: {
|
|
1445
|
-
attestationObject: EncodingUtils.arrayBufferToBase64(credentialResponse.attestationObject),
|
|
1446
|
-
clientDataJSON: EncodingUtils.arrayBufferToBase64(credentialResponse.clientDataJSON),
|
|
1447
|
-
},
|
|
1448
|
-
authenticatorAttachment,
|
|
1449
|
-
type: credential.type,
|
|
1450
|
-
};
|
|
1451
|
-
};
|
|
1452
|
-
})(WebauthnParser || (WebauthnParser = {}));
|
|
1453
|
-
|
|
1454
|
-
class AuthenticationHandler {
|
|
1455
|
-
async modal(params) {
|
|
1456
|
-
try {
|
|
1457
|
-
const result = await this.performAuthentication({
|
|
1458
|
-
...params,
|
|
1459
|
-
mediationType: AuthenticationMediationType.Modal,
|
|
1460
|
-
});
|
|
1461
|
-
return EncodingUtils.jsonToBase64(result);
|
|
1462
|
-
}
|
|
1463
|
-
catch (err) {
|
|
1464
|
-
throw SdkErrorParser.toAuthenticationError(err);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
activateAutofill(handlers, username) {
|
|
1468
|
-
const { onSuccess, onError, onReady } = handlers;
|
|
1469
|
-
this.performAuthentication({
|
|
1470
|
-
username,
|
|
1471
|
-
mediationType: AuthenticationMediationType.InputAutofill,
|
|
1472
|
-
onReady,
|
|
1473
|
-
})
|
|
1474
|
-
.then((result) => {
|
|
1475
|
-
onSuccess(EncodingUtils.jsonToBase64(result));
|
|
1476
|
-
})
|
|
1477
|
-
.catch((err) => {
|
|
1478
|
-
const error = SdkErrorParser.toAuthenticationError(err);
|
|
1479
|
-
if (!onError)
|
|
1480
|
-
throw error;
|
|
1481
|
-
onError(error);
|
|
1482
|
-
});
|
|
1483
|
-
}
|
|
1484
|
-
abortAutofill() {
|
|
1485
|
-
if (this.abortController)
|
|
1486
|
-
this.abortController.abort(ErrorCode.AutofillAuthenticationAborted);
|
|
1487
|
-
}
|
|
1488
|
-
abortAuthentication() {
|
|
1489
|
-
if (this.abortController)
|
|
1490
|
-
this.abortController.abort(ErrorCode.AuthenticationAbortedTimeout);
|
|
1491
|
-
}
|
|
1492
|
-
async performAuthentication(params) {
|
|
1493
|
-
var _a, _b;
|
|
1494
|
-
const startAuthenticationResponse = 'crossDeviceTicketId' in params
|
|
1495
|
-
? await SdkApiClient.startCrossDeviceAuthentication({ ticketId: params.crossDeviceTicketId })
|
|
1496
|
-
: await SdkApiClient.startAuthentication({ username: params.username, timeout: (_a = params.options) === null || _a === void 0 ? void 0 : _a.timeout });
|
|
1497
|
-
const credentialRequestOptions = startAuthenticationResponse.credential_request_options;
|
|
1498
|
-
const processedOptions = WebauthnParser.processCredentialRequestOptions(credentialRequestOptions);
|
|
1499
|
-
const mediatedCredentialRequestOptions = this.getMediatedCredentialRequest(processedOptions, params.mediationType);
|
|
1500
|
-
if (params.mediationType === AuthenticationMediationType.InputAutofill) {
|
|
1501
|
-
(_b = params.onReady) === null || _b === void 0 ? void 0 : _b.call(params);
|
|
1502
|
-
}
|
|
1503
|
-
const credential = (await navigator.credentials.get(mediatedCredentialRequestOptions).catch((error) => {
|
|
1504
|
-
throw SdkErrorParser.toAuthenticationError(error);
|
|
1505
|
-
}));
|
|
1506
|
-
return {
|
|
1507
|
-
webauthnSessionId: startAuthenticationResponse.webauthn_session_id,
|
|
1508
|
-
publicKeyCredential: WebauthnParser.encodeAuthenticationResult(credential),
|
|
1509
|
-
userAgent: navigator.userAgent,
|
|
1510
|
-
};
|
|
1511
|
-
}
|
|
1512
|
-
getMediatedCredentialRequest(credentialRequestOptions, passkeysMediationType) {
|
|
1513
|
-
const mediatedCredentialRequestOptions = { publicKey: credentialRequestOptions };
|
|
1514
|
-
this.abortController = new AbortController();
|
|
1515
|
-
mediatedCredentialRequestOptions.signal = this.abortController && this.abortController.signal;
|
|
1516
|
-
if (passkeysMediationType === AuthenticationMediationType.InputAutofill) {
|
|
1517
|
-
mediatedCredentialRequestOptions.mediation = 'conditional';
|
|
1518
|
-
}
|
|
1519
|
-
else if (credentialRequestOptions.timeout) {
|
|
1520
|
-
setTimeout(() => {
|
|
1521
|
-
this.abortAuthentication();
|
|
1522
|
-
}, credentialRequestOptions.timeout);
|
|
1523
|
-
}
|
|
1524
|
-
return mediatedCredentialRequestOptions;
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
function setIntervalWithPromise(target) {
|
|
1529
|
-
return async (...args) => {
|
|
1530
|
-
if (target.isRunning) {
|
|
1531
|
-
return;
|
|
1532
|
-
}
|
|
1533
|
-
target.isRunning = true;
|
|
1534
|
-
await target(...args);
|
|
1535
|
-
target.isRunning = false;
|
|
1536
|
-
};
|
|
1537
|
-
}
|
|
1538
|
-
class PollingHandler {
|
|
1539
|
-
constructor(handler, intervalInMs) {
|
|
1540
|
-
this.handler = handler;
|
|
1541
|
-
this.intervalInMs = intervalInMs;
|
|
1542
|
-
}
|
|
1543
|
-
begin() {
|
|
1544
|
-
this.intervalId = window.setInterval(setIntervalWithPromise(this.handler), this.intervalInMs);
|
|
1545
|
-
}
|
|
1546
|
-
stop() {
|
|
1547
|
-
clearInterval(this.intervalId);
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
const approvalDataMaxLength = 10;
|
|
1552
|
-
const approvalDataAllowedChars = /^[A-Za-z0-9\-_.: ]*$/;
|
|
1553
|
-
function validateApprovalMaxLength(approvalData) {
|
|
1554
|
-
return Object.keys(approvalData).length <= approvalDataMaxLength;
|
|
1555
|
-
}
|
|
1556
|
-
function validateApprovalAllowedContent(approvalData) {
|
|
1557
|
-
const isString = (val) => typeof val === 'string';
|
|
1558
|
-
const isContentAllowed = (val) => approvalDataAllowedChars.test(val);
|
|
1559
|
-
return Object.keys(approvalData).every((key) => isString(key) && isString(approvalData[key]) && isContentAllowed(key) && isContentAllowed(approvalData[key]));
|
|
1560
|
-
}
|
|
1561
|
-
function validateApprovalData(approvalData) {
|
|
1562
|
-
if (!approvalData) {
|
|
1563
|
-
return;
|
|
1564
|
-
}
|
|
1565
|
-
if (!validateApprovalMaxLength(approvalData) || !validateApprovalAllowedContent(approvalData)) {
|
|
1566
|
-
logger.error(`Failed validating approval data`);
|
|
1567
|
-
throw new InvalidApprovalDataError(`Provided approval data should have ${approvalDataMaxLength} properties max. Also, it should contain only
|
|
1568
|
-
alphanumeric characters, numbers, and the special characters: '-', '_', '.'`);
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
class WebauthnCrossDeviceHandler {
|
|
1573
|
-
constructor(authenticationHandler, registrationHandler, approvalHandler) {
|
|
1574
|
-
this.authenticationHandler = authenticationHandler;
|
|
1575
|
-
this.registrationHandler = registrationHandler;
|
|
1576
|
-
this.approvalHandler = approvalHandler;
|
|
1577
|
-
this.init = {
|
|
1578
|
-
registration: async (params) => {
|
|
1579
|
-
this.ticketStatus = exports.WebauthnCrossDeviceStatus.Pending;
|
|
1580
|
-
return this.pollCrossDeviceSession(params.crossDeviceTicketId, params.handlers);
|
|
1581
|
-
},
|
|
1582
|
-
authentication: async (params) => {
|
|
1583
|
-
const { username } = params;
|
|
1584
|
-
const initAuthenticationResponse = await SdkApiClient.initCrossDeviceAuthentication({
|
|
1585
|
-
...(username && { username }),
|
|
1586
|
-
});
|
|
1587
|
-
const ticketId = initAuthenticationResponse.cross_device_ticket_id;
|
|
1588
|
-
this.ticketStatus = exports.WebauthnCrossDeviceStatus.Pending;
|
|
1589
|
-
return this.pollCrossDeviceSession(ticketId, params.handlers);
|
|
1590
|
-
},
|
|
1591
|
-
approval: async (params) => {
|
|
1592
|
-
const { username, approvalData } = params;
|
|
1593
|
-
validateApprovalData(approvalData);
|
|
1594
|
-
const initAuthenticationResponse = await SdkApiClient.initCrossDeviceAuthentication({
|
|
1595
|
-
username,
|
|
1596
|
-
approvalData,
|
|
1597
|
-
});
|
|
1598
|
-
const ticketId = initAuthenticationResponse.cross_device_ticket_id;
|
|
1599
|
-
this.ticketStatus = exports.WebauthnCrossDeviceStatus.Pending;
|
|
1600
|
-
return this.pollCrossDeviceSession(ticketId, params.handlers);
|
|
1601
|
-
},
|
|
1602
|
-
};
|
|
1603
|
-
this.authenticate = {
|
|
1604
|
-
modal: async (crossDeviceTicketId) => this.authenticationHandler.modal({ crossDeviceTicketId }),
|
|
1605
|
-
};
|
|
1606
|
-
this.approve = {
|
|
1607
|
-
modal: async (crossDeviceTicketId) => this.approvalHandler.modal({ crossDeviceTicketId }),
|
|
1608
|
-
};
|
|
1609
|
-
}
|
|
1610
|
-
async register(crossDeviceTicketId, options) {
|
|
1611
|
-
return this.registrationHandler.register({ crossDeviceTicketId }, options);
|
|
1612
|
-
}
|
|
1613
|
-
async attachDevice(crossDeviceTicketId) {
|
|
1614
|
-
const response = await SdkApiClient.attachDeviceToCrossDeviceSession({
|
|
1615
|
-
ticketId: crossDeviceTicketId,
|
|
1616
|
-
});
|
|
1617
|
-
return {
|
|
1618
|
-
status: response.status,
|
|
1619
|
-
startedAt: response.started_at,
|
|
1620
|
-
...(response.approval_data && { approvalData: response.approval_data }),
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
async pollCrossDeviceSession(ticketId, handlers) {
|
|
1624
|
-
const pollFunction = async () => {
|
|
1625
|
-
var _a, _b;
|
|
1626
|
-
const statusResponse = await SdkApiClient.getCrossDeviceTicketStatus({
|
|
1627
|
-
ticketId,
|
|
1628
|
-
});
|
|
1629
|
-
const newStatus = statusResponse.status;
|
|
1630
|
-
if (newStatus === this.ticketStatus) {
|
|
1631
|
-
return;
|
|
1632
|
-
}
|
|
1633
|
-
this.ticketStatus = newStatus;
|
|
1634
|
-
switch (newStatus) {
|
|
1635
|
-
case exports.WebauthnCrossDeviceStatus.Scanned:
|
|
1636
|
-
await handlers.onDeviceAttach();
|
|
1637
|
-
break;
|
|
1638
|
-
case exports.WebauthnCrossDeviceStatus.Error:
|
|
1639
|
-
case exports.WebauthnCrossDeviceStatus.Timeout:
|
|
1640
|
-
case exports.WebauthnCrossDeviceStatus.Aborted:
|
|
1641
|
-
await handlers.onFailure(statusResponse);
|
|
1642
|
-
(_a = this.poller) === null || _a === void 0 ? void 0 : _a.stop();
|
|
1643
|
-
break;
|
|
1644
|
-
case exports.WebauthnCrossDeviceStatus.Success:
|
|
1645
|
-
if ('onCredentialRegister' in handlers) {
|
|
1646
|
-
await handlers.onCredentialRegister();
|
|
1647
|
-
}
|
|
1648
|
-
else {
|
|
1649
|
-
if (!statusResponse.session_id) {
|
|
1650
|
-
throw new FailedToGetCrossDeviceStatusError('Cross device session is complete without returning session_id', statusResponse);
|
|
1651
|
-
}
|
|
1652
|
-
await handlers.onCredentialAuthenticate(statusResponse.session_id);
|
|
1653
|
-
}
|
|
1654
|
-
(_b = this.poller) === null || _b === void 0 ? void 0 : _b.stop();
|
|
1655
|
-
break;
|
|
1656
|
-
}
|
|
1657
|
-
};
|
|
1658
|
-
this.poller = new PollingHandler(pollFunction, 1000);
|
|
1659
|
-
this.poller.begin();
|
|
1660
|
-
setTimeout(() => {
|
|
1661
|
-
var _a;
|
|
1662
|
-
(_a = this.poller) === null || _a === void 0 ? void 0 : _a.stop();
|
|
1663
|
-
handlers.onFailure({ status: exports.WebauthnCrossDeviceStatus.Timeout });
|
|
1664
|
-
}, 5 * 60 * 1000);
|
|
1665
|
-
return {
|
|
1666
|
-
crossDeviceTicketId: ticketId,
|
|
1667
|
-
stop: () => {
|
|
1668
|
-
var _a;
|
|
1669
|
-
(_a = this.poller) === null || _a === void 0 ? void 0 : _a.stop();
|
|
1670
|
-
},
|
|
1671
|
-
};
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
class RegistrationHandler {
|
|
1676
|
-
async register(params, options) {
|
|
1677
|
-
this.abortController = new AbortController();
|
|
1678
|
-
const isCrossDevice = 'crossDeviceTicketId' in params;
|
|
1679
|
-
const registrationOptions = {
|
|
1680
|
-
allowCrossPlatformAuthenticators: !isCrossDevice,
|
|
1681
|
-
registerAsDiscoverable: true,
|
|
1682
|
-
...options,
|
|
1683
|
-
};
|
|
1684
|
-
try {
|
|
1685
|
-
const startRegistrationResponse = 'crossDeviceTicketId' in params
|
|
1686
|
-
? await SdkApiClient.startCrossDeviceRegistration({ ticketId: params.crossDeviceTicketId })
|
|
1687
|
-
: await SdkApiClient.startRegistration({
|
|
1688
|
-
username: params.username,
|
|
1689
|
-
displayName: (options === null || options === void 0 ? void 0 : options.displayName) || params.username,
|
|
1690
|
-
timeout: options === null || options === void 0 ? void 0 : options.timeout,
|
|
1691
|
-
limitSingleCredentialToDevice: options === null || options === void 0 ? void 0 : options.limitSingleCredentialToDevice,
|
|
1692
|
-
});
|
|
1693
|
-
const credentialCreationOptions = WebauthnParser.processCredentialCreationOptions(startRegistrationResponse.credential_creation_options, registrationOptions);
|
|
1694
|
-
setTimeout(() => {
|
|
1695
|
-
this.abortRegistration();
|
|
1696
|
-
}, credentialCreationOptions.timeout);
|
|
1697
|
-
const publicKeyCredential = await this.registerCredential(credentialCreationOptions);
|
|
1698
|
-
const result = {
|
|
1699
|
-
webauthnSessionId: startRegistrationResponse.webauthn_session_id,
|
|
1700
|
-
publicKeyCredential,
|
|
1701
|
-
userAgent: navigator.userAgent,
|
|
1702
|
-
};
|
|
1703
|
-
return EncodingUtils.jsonToBase64(result);
|
|
1704
|
-
}
|
|
1705
|
-
catch (error) {
|
|
1706
|
-
throw SdkErrorParser.toRegistrationError(error);
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
abortRegistration() {
|
|
1710
|
-
if (this.abortController)
|
|
1711
|
-
this.abortController.abort(ErrorCode.RegistrationAbortedTimeout);
|
|
1712
|
-
}
|
|
1713
|
-
async registerCredential(credentialCreationOptions) {
|
|
1714
|
-
const credential = (await navigator.credentials
|
|
1715
|
-
.create({
|
|
1716
|
-
publicKey: credentialCreationOptions,
|
|
1717
|
-
signal: this.abortController && this.abortController.signal,
|
|
1718
|
-
})
|
|
1719
|
-
.catch((error) => {
|
|
1720
|
-
throw SdkErrorParser.toRegistrationError(error);
|
|
1721
|
-
}));
|
|
1722
|
-
return WebauthnParser.encodeRegistrationResult(credential);
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
function symmetricDifference(arr1, arr2) {
|
|
1727
|
-
const set1 = new Set(arr1);
|
|
1728
|
-
const set2 = new Set(arr2);
|
|
1729
|
-
return [...arr1.filter((x) => !set2.has(x)), ...arr2.filter((x) => !set1.has(x))];
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
class ApprovalHandler {
|
|
1733
|
-
async modal(params) {
|
|
1734
|
-
try {
|
|
1735
|
-
const result = await this.performApproval(params);
|
|
1736
|
-
return EncodingUtils.jsonToBase64(result);
|
|
1737
|
-
}
|
|
1738
|
-
catch (err) {
|
|
1739
|
-
throw SdkErrorParser.toAuthenticationError(err);
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
async performApproval(params) {
|
|
1743
|
-
if ('approvalData' in params) {
|
|
1744
|
-
validateApprovalData(params.approvalData);
|
|
1745
|
-
}
|
|
1746
|
-
const startAuthenticationResponse = 'crossDeviceTicketId' in params
|
|
1747
|
-
? await SdkApiClient.startCrossDeviceAuthentication({ ticketId: params.crossDeviceTicketId })
|
|
1748
|
-
: await SdkApiClient.startAuthentication({
|
|
1749
|
-
username: params.username,
|
|
1750
|
-
approvalData: params.approvalData,
|
|
1751
|
-
});
|
|
1752
|
-
const credentialRequestOptions = startAuthenticationResponse.credential_request_options;
|
|
1753
|
-
const processedOptions = WebauthnParser.processCredentialRequestOptions(credentialRequestOptions);
|
|
1754
|
-
const credential = (await navigator.credentials.get({ publicKey: processedOptions }).catch((error) => {
|
|
1755
|
-
throw SdkErrorParser.toAuthenticationError(error);
|
|
1756
|
-
}));
|
|
1757
|
-
return {
|
|
1758
|
-
webauthnSessionId: startAuthenticationResponse.webauthn_session_id,
|
|
1759
|
-
publicKeyCredential: WebauthnParser.encodeAuthenticationResult(credential),
|
|
1760
|
-
userAgent: navigator.userAgent,
|
|
1761
|
-
};
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
/**
|
|
1766
|
-
* WebAuthn SDK implementation
|
|
1767
|
-
*/
|
|
1768
|
-
class WebAuthnSdk {
|
|
1769
|
-
constructor() {
|
|
1770
|
-
this._initialized = false;
|
|
1771
|
-
this._authenticationHandler = new AuthenticationHandler();
|
|
1772
|
-
this._registrationHandler = new RegistrationHandler();
|
|
1773
|
-
this._approvalHandler = new ApprovalHandler();
|
|
1774
|
-
this._crossDeviceHandler = new WebauthnCrossDeviceHandler(this._authenticationHandler, this._registrationHandler, this._approvalHandler);
|
|
1775
|
-
this.authenticate = {
|
|
1776
|
-
modal: async (username, options) => {
|
|
1777
|
-
this.initCheck();
|
|
1778
|
-
return this._authenticationHandler.modal({ username, options });
|
|
1779
|
-
},
|
|
1780
|
-
autofill: {
|
|
1781
|
-
activate: (handlers, username) => {
|
|
1782
|
-
this.initCheck();
|
|
1783
|
-
return this._authenticationHandler.activateAutofill(handlers, username);
|
|
1784
|
-
},
|
|
1785
|
-
abort: () => {
|
|
1786
|
-
return this._authenticationHandler.abortAutofill();
|
|
1787
|
-
},
|
|
1788
|
-
},
|
|
1789
|
-
};
|
|
1790
|
-
this.approve = {
|
|
1791
|
-
modal: async (username, approvalData) => {
|
|
1792
|
-
this.initCheck();
|
|
1793
|
-
return this._approvalHandler.modal({ username, approvalData });
|
|
1794
|
-
},
|
|
1795
|
-
};
|
|
1796
|
-
this.register = async (username, options) => {
|
|
1797
|
-
this.initCheck();
|
|
1798
|
-
return this._registrationHandler.register({ username }, options);
|
|
1799
|
-
};
|
|
1800
|
-
this.crossDevice = {
|
|
1801
|
-
init: {
|
|
1802
|
-
registration: async (params) => {
|
|
1803
|
-
this.initCheck();
|
|
1804
|
-
return this._crossDeviceHandler.init.registration(params);
|
|
1805
|
-
},
|
|
1806
|
-
authentication: async (params) => {
|
|
1807
|
-
this.initCheck();
|
|
1808
|
-
return this._crossDeviceHandler.init.authentication(params);
|
|
1809
|
-
},
|
|
1810
|
-
approval: async (params) => {
|
|
1811
|
-
this.initCheck();
|
|
1812
|
-
return this._crossDeviceHandler.init.approval(params);
|
|
1813
|
-
},
|
|
1814
|
-
},
|
|
1815
|
-
authenticate: {
|
|
1816
|
-
modal: async (crossDeviceTicketId) => {
|
|
1817
|
-
this.initCheck();
|
|
1818
|
-
return this._crossDeviceHandler.authenticate.modal(crossDeviceTicketId);
|
|
1819
|
-
},
|
|
1820
|
-
},
|
|
1821
|
-
approve: {
|
|
1822
|
-
modal: async (crossDeviceTicketId) => {
|
|
1823
|
-
this.initCheck();
|
|
1824
|
-
return this._crossDeviceHandler.approve.modal(crossDeviceTicketId);
|
|
1825
|
-
},
|
|
1826
|
-
},
|
|
1827
|
-
register: async (crossDeviceTicketId, options) => {
|
|
1828
|
-
this.initCheck();
|
|
1829
|
-
return this._crossDeviceHandler.register(crossDeviceTicketId, options);
|
|
1830
|
-
},
|
|
1831
|
-
attachDevice: async (crossDeviceTicketId) => {
|
|
1832
|
-
this.initCheck();
|
|
1833
|
-
return this._crossDeviceHandler.attachDevice(crossDeviceTicketId);
|
|
1834
|
-
},
|
|
1835
|
-
};
|
|
1836
|
-
this.isPlatformAuthenticatorSupported = async () => {
|
|
1837
|
-
var _a;
|
|
1838
|
-
try {
|
|
1839
|
-
return await ((_a = WebAuthnSdk.StaticPublicKeyCredential) === null || _a === void 0 ? void 0 : _a.isUserVerifyingPlatformAuthenticatorAvailable());
|
|
1840
|
-
}
|
|
1841
|
-
catch (error) {
|
|
1842
|
-
return false;
|
|
1843
|
-
}
|
|
1844
|
-
};
|
|
1845
|
-
this.isAutofillSupported = async () => {
|
|
1846
|
-
var _a, _b;
|
|
1847
|
-
return !!(((_a = WebAuthnSdk.StaticPublicKeyCredential) === null || _a === void 0 ? void 0 : _a.isConditionalMediationAvailable) &&
|
|
1848
|
-
(await ((_b = WebAuthnSdk.StaticPublicKeyCredential) === null || _b === void 0 ? void 0 : _b.isConditionalMediationAvailable())));
|
|
1849
|
-
};
|
|
1850
|
-
}
|
|
1851
|
-
async init(clientId, options) {
|
|
1852
|
-
try {
|
|
1853
|
-
if (!clientId) {
|
|
1854
|
-
throw new NotInitializedError('Invalid clientId', { clientId });
|
|
1855
|
-
}
|
|
1856
|
-
if (options.webauthnApiPaths) {
|
|
1857
|
-
const defaultPaths = SdkApiClient.getDefaultPaths();
|
|
1858
|
-
if (symmetricDifference(Object.keys(options.webauthnApiPaths), Object.keys(defaultPaths)).length) {
|
|
1859
|
-
throw new NotInitializedError('Invalid custom paths', {
|
|
1860
|
-
customApiPaths: options.webauthnApiPaths,
|
|
1861
|
-
});
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
SdkApiClient.init(clientId, options);
|
|
1865
|
-
this._initialized = true;
|
|
1866
|
-
}
|
|
1867
|
-
catch (error) {
|
|
1868
|
-
if (isBaseSdkError(error)) {
|
|
1869
|
-
throw error;
|
|
1870
|
-
}
|
|
1871
|
-
else {
|
|
1872
|
-
throw new NotInitializedError('Failed to initialize SDK');
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
}
|
|
1876
|
-
getDefaultPaths() {
|
|
1877
|
-
this.initCheck();
|
|
1878
|
-
return SdkApiClient.getDefaultPaths();
|
|
1879
|
-
}
|
|
1880
|
-
getApiPaths() {
|
|
1881
|
-
this.initCheck();
|
|
1882
|
-
return SdkApiClient.getApiPaths();
|
|
1883
|
-
}
|
|
1884
|
-
initCheck() {
|
|
1885
|
-
if (!this._initialized) {
|
|
1886
|
-
throw new NotInitializedError();
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
// We are accessing the PublicKeyCredential from the global scope here in order to make it possibly undefined - for browsers which do not support WebAuthn
|
|
1891
|
-
WebAuthnSdk.StaticPublicKeyCredential = window.PublicKeyCredential;
|
|
1892
|
-
|
|
1893
|
-
const common = new Common('webauthn');
|
|
1894
|
-
const webauthn$1 = new WebAuthnSdk();
|
|
1895
|
-
common.events.on(common.events.MODULE_INITIALIZED, () => {
|
|
1896
|
-
var _a;
|
|
1897
|
-
const config = common.moduleMetadata.getInitConfig();
|
|
1898
|
-
if (!((_a = config === null || config === void 0 ? void 0 : config.webauthn) === null || _a === void 0 ? void 0 : _a.serverPath)) {
|
|
1899
|
-
return;
|
|
1900
|
-
}
|
|
1901
|
-
const { clientId, webauthn: webauthnConfig } = config;
|
|
1902
|
-
webauthn$1.init(clientId, { ...webauthnConfig });
|
|
1903
|
-
});
|
|
1904
|
-
/**
|
|
1905
|
-
* Returns the authentication flows for webauthn
|
|
1906
|
-
*/
|
|
1907
|
-
const authenticate = {
|
|
1908
|
-
modal: async (username, options) => {
|
|
1909
|
-
webauthn$1.initCheck();
|
|
1910
|
-
return webauthn$1.authenticate.modal(username, options);
|
|
1911
|
-
},
|
|
1912
|
-
autofill: {
|
|
1913
|
-
activate: (handlers, username) => {
|
|
1914
|
-
webauthn$1.initCheck();
|
|
1915
|
-
webauthn$1.authenticate.autofill.activate(handlers, username);
|
|
1916
|
-
},
|
|
1917
|
-
abort: () => {
|
|
1918
|
-
webauthn$1.initCheck();
|
|
1919
|
-
webauthn$1.authenticate.autofill.abort();
|
|
1920
|
-
},
|
|
1921
|
-
},
|
|
1922
|
-
};
|
|
1923
|
-
const approve = {
|
|
1924
|
-
modal: async (username, approvalData) => {
|
|
1925
|
-
webauthn$1.initCheck();
|
|
1926
|
-
return webauthn$1.approve.modal(username, approvalData);
|
|
1927
|
-
},
|
|
1928
|
-
};
|
|
1929
|
-
/**
|
|
1930
|
-
* Invokes a WebAuthn credential registration for the specified user, including prompting the user for biometrics.
|
|
1931
|
-
* If registration is completed successfully, this call will return a promise that resolves to the credential result, which is an object encoded as a base64 string. This encoded result should then be passed to the relevant backend registration endpoint to complete the registration for either a [logged-in user](/openapi/user/backend-webauthn/#operation/webauthn-registration) or [logged-out user](/openapi/user/backend-webauthn/#operation/webauthn-registration-external).
|
|
1932
|
-
*
|
|
1933
|
-
* If registration fails, an SdkError will be thrown.
|
|
1934
|
-
*
|
|
1935
|
-
* @param username WebAuthn username to register
|
|
1936
|
-
* @param options Additional configuration for registration flow
|
|
1937
|
-
* @throws {@link ErrorCode.NotInitialized}
|
|
1938
|
-
* @throws {@link ErrorCode.RegistrationFailed}
|
|
1939
|
-
* @throws {@link ErrorCode.RegistrationCanceled}
|
|
1940
|
-
*/
|
|
1941
|
-
async function register(username, options) {
|
|
1942
|
-
webauthn$1.initCheck();
|
|
1943
|
-
return webauthn$1.register(username, options);
|
|
1944
|
-
}
|
|
1945
|
-
/**
|
|
1946
|
-
* Returns webauthn cross device flows
|
|
1947
|
-
* @type WebauthnCrossDeviceFlows
|
|
1948
|
-
*/
|
|
1949
|
-
const { crossDevice } = webauthn$1;
|
|
1950
|
-
/**
|
|
1951
|
-
* Indicates whether this browser supports WebAuthn, and has a platform authenticator
|
|
1952
|
-
*/
|
|
1953
|
-
const { isPlatformAuthenticatorSupported } = webauthn$1;
|
|
1954
|
-
/**
|
|
1955
|
-
* Indicates whether this browser supports Passkey Autofill
|
|
1956
|
-
*/
|
|
1957
|
-
const { isAutofillSupported } = webauthn$1;
|
|
1958
|
-
/**
|
|
1959
|
-
* Returns the default API paths for webauthn
|
|
1960
|
-
*/
|
|
1961
|
-
const { getDefaultPaths } = webauthn$1;
|
|
1962
|
-
// @ts-ignore
|
|
1963
|
-
window.localWebAuthnSDK = webauthn$1;
|
|
1964
|
-
|
|
1965
|
-
var moduleExports = /*#__PURE__*/Object.freeze({
|
|
1966
|
-
__proto__: null,
|
|
1967
|
-
get WebauthnCrossDeviceStatus () { return exports.WebauthnCrossDeviceStatus; },
|
|
1968
|
-
approve: approve,
|
|
1969
|
-
authenticate: authenticate,
|
|
1970
|
-
crossDevice: crossDevice,
|
|
1971
|
-
getDefaultPaths: getDefaultPaths,
|
|
1972
|
-
isAutofillSupported: isAutofillSupported,
|
|
1973
|
-
isPlatformAuthenticatorSupported: isPlatformAuthenticatorSupported,
|
|
1974
|
-
register: register
|
|
1975
|
-
});
|
|
1976
|
-
|
|
1977
|
-
const PACKAGE_VERSION = "1.17.2";
|
|
1978
|
-
|
|
1979
|
-
// Create namespace object for tree-shakable import
|
|
1980
|
-
const webauthn = {
|
|
1981
|
-
initialize: initialize,
|
|
1982
|
-
...moduleExports
|
|
1983
|
-
};
|
|
1984
|
-
|
|
1985
|
-
exports.PACKAGE_VERSION = PACKAGE_VERSION;
|
|
1986
|
-
exports.approve = approve;
|
|
1987
|
-
exports.authenticate = authenticate;
|
|
1988
|
-
exports.crossDevice = crossDevice;
|
|
1989
|
-
exports.getDefaultPaths = getDefaultPaths;
|
|
1990
|
-
exports.initialize = initialize;
|
|
1991
|
-
exports.isAutofillSupported = isAutofillSupported;
|
|
1992
|
-
exports.isPlatformAuthenticatorSupported = isPlatformAuthenticatorSupported;
|
|
1993
|
-
exports.register = register;
|
|
1994
|
-
exports.webauthn = webauthn;
|
|
1
|
+
"undefined"==typeof globalThis&&("undefined"!=typeof window?(window.globalThis=window,window.global=window):"undefined"!=typeof self&&(self.globalThis=self,self.global=self));const t=Symbol("MODULE_INITIALIZED"),e=new Map;function i(t,i){var a,n;null===(a=e.get(t))||void 0===a||a.forEach((n=t=>t(i),function(){try{return n(...arguments)}catch(t){console.log(t)}}))}let a=null;function n(t){a=t}var s=Object.freeze({__proto__:null,getInitConfig:function(){return a},get initConfig(){return a},setInitConfig:n});function r(e){n(e),i(t,void 0)}var o=Object.freeze({__proto__:null,initialize:r});function c(t,e,i){return(e=function(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var i=t[Symbol.toPrimitive];if(void 0!==i){var a=i.call(t,e||"default");if("object"!=typeof a)return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}(e))in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function l(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(t);e&&(a=a.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,a)}return i}function u(t){for(var e=1;e<arguments.length;e++){var i=null!=arguments[e]?arguments[e]:{};e%2?l(Object(i),!0).forEach((function(e){c(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):l(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function d(t,e){return Object.entries(e).reduce(((e,i)=>{let[a,n]=i;return u(u({},e),{},{[a]:h.isPrototypeOf(n)?new n(t.slug):"function"==typeof n?n.bind(t):"object"==typeof n&&!Array.isArray(n)&&n?d(t,n):n})}),{})}class h{constructor(t){this.slug=t}static create(t){return class extends h{constructor(e){super(e),Object.assign(this,d(this,t(this)))}}}}var y=Object.freeze({__proto__:null,Agent:h}),p=Object.freeze({__proto__:null,MODULE_INITIALIZED:t,emit:i,off:function(t,i){const a=e.get(t);if(!a)return;const n=a.indexOf(i);-1!==n&&a.splice(n,1)},on:function(t,i){var a;e.has(t)?null===(a=e.get(t))||void 0===a||a.push(i):e.set(t,[i])}});function g(t,e){const i=!t||"object"!=typeof t||Array.isArray(t)?{}:t;return[e.reduce(((t,e)=>{if(e in t){const i=t[e];if(null!==i&&"object"==typeof i&&!Array.isArray(i))return i}const i={};return t[e]=i,i}),i),i]}const v="tsec",f="general";function w(t){return t?f:a.clientId}function m(t){return function(t){if(!t)return{};try{return JSON.parse(t)}catch(t){return{}}}((t?sessionStorage:localStorage).getItem(v))}function b(t,e){const i=t?sessionStorage:localStorage,a=e(m(t));i.setItem(v,JSON.stringify(a))}var D=Object.freeze({__proto__:null,COMMON_STORAGE_KEY:v,GENERAL_ID_KEY:f,getValue:function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const i=w(!!e.isGeneral),a=m(!!e.sessionOnly),[n]=g(a,[this.slug.toString(),i]);return n[t]},hasValue:function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const i=w(!!e.isGeneral);return function(t,e){let i=t;return e.every((t=>!(!i||"object"!=typeof i||Array.isArray(i)||!(t in i)||(i=i[t],0))),t)}(m(!!e.sessionOnly),[this.slug.toString(),i,t])},removeValue:function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const i=w(!!e.isGeneral);b(!!e.sessionOnly,(e=>{const[a,n]=g(e,[this.slug.toString(),i]);return delete a[t],n}))},setValue:function(t,e){let i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const a=w(!!i.isGeneral);b(!!i.sessionOnly,(i=>{const[n,s]=g(i,[this.slug.toString(),a]);return n[t]=e,s}))}});const S="RSA-PSS",A=async(t,e)=>await window.crypto.subtle.generateKey({name:t,modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:"SHA-256"},!1,e),C=async()=>await A("RSA-OAEP",["encrypt","decrypt"]),_=async()=>await A(S,["sign"]),K=async(t,e)=>{const i=(new TextEncoder).encode(e);return await window.crypto.subtle.sign({name:S,saltLength:32},t,i)};class k{constructor(t,e,i){this.slug=t,this.dbName=e,this.dbVersion=i}queryObjectStore(t,e){let i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const{attemptToRecoverDB:a=!0}=i,n=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB||window.shimIndexedDB,s=`${this.slug}:${this.dbName}`,r=n.open(s,this.dbVersion||1);r.onupgradeneeded=()=>{var e;const i=r.result;(null===(e=null==i?void 0:i.objectStoreNames)||void 0===e?void 0:e.contains)&&!i.objectStoreNames.contains(t)&&i.createObjectStore(t,{keyPath:"key"})},r.onsuccess=()=>{const o=r.result;let c;try{c=o.transaction(t,(null==i?void 0:i.operation)||"readwrite")}catch(r){if(a&&r instanceof DOMException&&"NotFoundError"===r.name){o.close();return void(n.deleteDatabase(s).onsuccess=()=>{this.queryObjectStore(t,e,u(u({},i),{},{attemptToRecoverDB:!1}))})}throw r}const l=c.objectStore(t);e(l,o),c.oncomplete||(c.oncomplete=()=>{o.close()})}}put(t,e,i){return new Promise(((a,n)=>{this.queryObjectStore(t,(t=>{const s=t.put({key:e,value:i});s.onsuccess=()=>{a(s.result)},s.onerror=t=>{n("Failed adding item to objectStore, err: "+t)}}))}))}add(t,e,i){return new Promise(((a,n)=>{this.queryObjectStore(t,(t=>{const s=t.add({key:e,value:i});s.onsuccess=()=>{a(s.result)},s.onerror=t=>{const e=t.target.error;n(e)}}))}))}get(t,e){return new Promise(((i,a)=>{this.queryObjectStore(t,(t=>{const n=t.get(e);n.onsuccess=()=>{var t;n.result?i(null===(t=n.result)||void 0===t?void 0:t.value):i(void 0)},n.onerror=t=>{a("Failed adding item to objectStore, err: "+t)}}))}))}getAll(t,e){return new Promise(((i,a)=>{this.queryObjectStore(t,(t=>{const n=t.getAll(null,e);n.onsuccess=()=>{if(n.result){const t=n.result;(null==t?void 0:t.length)?i(t.map((t=>null==t?void 0:t.value))):i(t)}else i([])},n.onerror=t=>{a("Failed getting items, err: "+t)}}))}))}delete(t,e){return new Promise(((i,a)=>{this.queryObjectStore(t,(t=>{const n=t.delete(e);n.onsuccess=()=>{i()},n.onerror=t=>{a(`Failed deleting key: '${e}' from objectStore, err: `+t)}}))}))}clear(t){return new Promise(((e,i)=>{this.queryObjectStore(t,(t=>{const a=t.clear();a.onsuccess=()=>{e()},a.onerror=t=>{i("Failed clearing objectStore, err: "+t)}}))}))}executeTransaction(t,e){return new Promise(((i,a)=>{this.queryObjectStore(t,((t,n)=>{const s=t.transaction;s.onerror=()=>{a(`Transaction failed: ${s.error}`)},s.onabort=()=>{a("Transaction aborted")},s.oncomplete=()=>{n.close(),i()};for(const i of e){let e;if("delete"===i.type)e=t.delete(i.key);else{if("put"!==i.type)return s.abort(),void a("Unknown operation type");e=t.put({key:i.key,value:i.value})}e.onerror=()=>{s.abort(),a(`Operation failed: ${e.error}`)}}}))}))}}const R="init",P="completed",T="RSA2048",I=[R,P];class O{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sign",i=arguments.length>2?arguments[2]:void 0;var a,n,s,r,o;this.agent=t,this.keysType=e,this.options=i,this._extractingKeysPromise=null;const c=!(null===(a=this.options)||void 0===a?void 0:a.productScope),l=null===(n=this.options)||void 0===n?void 0:n.fallbackClientId;this.keysDatabaseName=c||!(null===(s=this.options)||void 0===s?void 0:s.indexedDBName)?"ts_crypto_binding":this.options.indexedDBName,this.dbVersion=c?1:(null===(r=this.options)||void 0===r?void 0:r.dbVersion)||1,this.keysStoreName=c||!(null===(o=this.options)||void 0===o?void 0:o.keysStoreName)?"identifiers_store":this.options.keysStoreName;const u=c?"platform":t.slug,d=this.getClientConfiguration(l,u);this.indexedDBClient=new k(d.main,this.keysDatabaseName,this.dbVersion),this.indexedDBClientFallback=new k(d.fallback,this.keysDatabaseName,this.dbVersion)}getClientConfiguration(t,e){return t?{main:e,fallback:`${e}:${t}`}:{main:e,fallback:`${e}:${a.clientId}`}}getKeysRecordKey(){return`${this.keysType}_keys`}getRotatedKeysRecordKey(){return`rotated_${this.keysType}_keys`}getRotatedKeysRecordKeyPending(){return`rotated_pending_${this.keysType}_keys`}arrayBufferToBase64(t){return window.btoa(String.fromCharCode(...new Uint8Array(t)))}async getPKRepresentations(t){const e=await crypto.subtle.exportKey("spki",t);return{arrayBufferKey:e,base64Key:this.arrayBufferToBase64(e)}}async generateKeyPair(){return"sign"==this.keysType?await _():await C()}async calcKeyIdentifier(t){const e=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(e)).map((t=>t.toString(16).padStart(2,"0"))).join("")}async extractKeysData(){if(this._extractingKeysPromise)return this._extractingKeysPromise;this._extractingKeysPromise=(async()=>{var t,e;const i=(null===(e=null===(t=this.options)||void 0===t?void 0:t.keyRotation)||void 0===e?void 0:e.isEnabled)?await this.getRotatedKeysData():await this.getKeysData(),{base64Key:a}=await this.getPKRepresentations(i.publicKey);return this.publicKeyBase64=a,this.keyIdentifier=i.keyIdentifier,i})();try{return await this._extractingKeysPromise}finally{this._extractingKeysPromise=null}}async generateKeyPairData(t){const e=await this.generateKeyPair(),{arrayBufferKey:i}=await this.getPKRepresentations(e.publicKey),a=t||await this.calcKeyIdentifier(i);return u(u({},e),{},{keyIdentifier:a,createdDate:Date.now()})}shouldKeyBeRotated(t){var e;const i=null===(e=this.options)||void 0===e?void 0:e.keyRotation;if(!(null==i?void 0:i.isEnabled)||!i.expiryDays||void 0===i.startedAt)return!1;const a=24*i.expiryDays*60*60*1e3,n=t.createdDate&&t.createdDate>=i.startedAt?t.createdDate:i.startedAt;return Date.now()-n>a-2592e6}async extractMainKeysData(){return await this.indexedDBClient.get(this.keysStoreName,this.getKeysRecordKey())}async extractFallbackMainKeysData(){return await this.indexedDBClientFallback.get(this.keysStoreName,this.getKeysRecordKey())}async extractRotatedKeysData(){return await this.indexedDBClient.get(this.keysStoreName,this.getRotatedKeysRecordKey())}async extractPendingRotatedKeysData(){return await this.indexedDBClient.get(this.keysStoreName,this.getRotatedKeysRecordKeyPending())}async saveKeyData(t,e){try{return await this.indexedDBClient.add(this.keysStoreName,t,e),e}catch(e){if(e instanceof DOMException&&"ConstraintError"===e.name){const e=await this.indexedDBClient.get(this.keysStoreName,t);if(e)return e}throw e}}async getKeysData(){const t=this.getKeysRecordKey();let e=await this.extractMainKeysData();if(e)return e;if(e=await this.extractFallbackMainKeysData(),e)return this.saveKeyData(t,e);const i=await this.generateKeyPairData();return this.saveKeyData(t,i)}async getOrCreateRotatedKeys(){let t=await this.extractRotatedKeysData();if(!t){const e=this.getRotatedKeysRecordKey(),i=await this.getKeysData(),a=u(u({},i),{},{createdDate:i.createdDate||Date.now()});t=await this.saveKeyData(e,a)}return t}async getRotatedKeysData(){const t=await this.getOrCreateRotatedKeys();if(this.shouldKeyBeRotated(t)){if(!await this.extractPendingRotatedKeysData()){const e=this.getRotatedKeysRecordKeyPending(),i=await this.generateKeyPairData(t.keyIdentifier);await this.saveKeyData(e,i)}}return t}async getPublicData(){return this.publicKeyBase64&&this.keyIdentifier||await this.extractKeysData(),{publicKey:this.publicKeyBase64,keyIdentifier:this.keyIdentifier}}async sign(t){if("sign"==this.keysType){const{privateKey:e}=await this.extractKeysData(),i=await K(e,t);return this.arrayBufferToBase64(i)}throw new Error("keysType must be 'sign' in order to use sign keys")}async clearKeys(){const t=this.getKeysRecordKey();await this.indexedDBClient.delete(this.keysStoreName,t)}getBaseRotationPayload(){return{keyIdentifier:this.keyIdentifier,slot:this.getRotatedKeysRecordKey(),publicKey:this.publicKeyBase64,publicKeyType:T,tenantId:this.options.keyRotation.tenantId}}async getRotationData(){var t,e;if(!(null===(e=null===(t=this.options)||void 0===t?void 0:t.keyRotation)||void 0===e?void 0:e.isEnabled))return;this.publicKeyBase64&&this.keyIdentifier||await this.extractKeysData();const i=await this.extractPendingRotatedKeysData();if(i){const{base64Key:t}=await this.getPKRepresentations(i.publicKey),{privateKey:e}=await this.extractKeysData(),a=u(u({},this.getBaseRotationPayload()),{},{newPublicKey:t,createdDate:i.createdDate,newPublicKeyType:T}),n=JSON.stringify(a);return{data:n,signature:await this.signPayload(n,e)}}const a=await this.extractRotatedKeysData();if(a&&!1===a.confirmed){await this.extractKeysData();const t=JSON.stringify(this.getBaseRotationPayload());return{data:t,signature:await this.signPayload(t,a.privateKey)}}}async signPayload(t,e){const i=await K(e,t);return this.arrayBufferToBase64(i)}async handleRotateResponse(t){if(I.includes(t))if(t===R){const t=await this.extractPendingRotatedKeysData();if(t){const e=u(u({},t),{},{confirmed:!1});await this.indexedDBClient.executeTransaction(this.keysStoreName,[{type:"delete",key:this.getRotatedKeysRecordKey()},{type:"put",key:this.getRotatedKeysRecordKey(),value:e},{type:"delete",key:this.getRotatedKeysRecordKeyPending()}]);const{base64Key:i}=await this.getPKRepresentations(t.publicKey);this.publicKeyBase64=i,this.keyIdentifier=t.keyIdentifier}}else if(t===P){const t=await this.extractRotatedKeysData();t&&!1===t.confirmed&&await this.indexedDBClient.put(this.keysStoreName,this.getRotatedKeysRecordKey(),u(u({},t),{},{confirmed:!0}))}}}var x=Object.freeze({__proto__:null,createCryptoBinding:function(){return new O(this,arguments.length>0&&void 0!==arguments[0]?arguments[0]:"sign",arguments.length>1?arguments[1]:void 0)},generateRSAKeyPair:C,generateRSASignKeyPair:_,signAssymetric:K,verifyAssymetric:async(t,e,i)=>{const a=(new TextEncoder).encode(e);return await window.crypto.subtle.verify(S,t,i,a)}}),j=Object.freeze({__proto__:null});const B=h.create((t=>{class e extends Error{constructor(e,i){super(`${t.slug}-${e} ${i}`)}}return{TsError:e,TsInternalError:class extends e{constructor(t){super(t,"Internal error")}}}}));var E=h.create((()=>u({exceptions:B},y)));class N{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];this.agent=t,this.middlewares=e,this.logs=[]}info(t,e){this.pushLog(3,t,e)}warn(t,e){this.pushLog(4,t,e)}error(t,e){this.pushLog(5,t,e)}pushLog(t,e){let i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};this.logs.push({timestamp:Date.now(),module:this.agent.slug,severity:t,fields:i,message:e});const a=this.middlewares.map((t=>t(this)));Promise.all(a).catch((()=>{}))}}var F=Object.freeze({__proto__:null,consoleMiddleware:function(t){const e=t.logs[t.logs.length-1];console.log(`${e.severity} ${e.message}`,e.fields)},createSdkLogger:function(){return new N(this,arguments.length>0&&void 0!==arguments[0]?arguments[0]:[])}});function H(t,e){if(!(null==t?void 0:t.trim()))return"";if(function(t){try{return new URL(t),!0}catch(t){return!1}}(t))return t;const i="http://mock.com",a=new URL(i);a.search=(null==e?void 0:e.toString())||"",a.pathname=t;return a.href.replace(i,"")}const M={"Content-Type":"application/json","X-TS-client-time":(new Date).toUTCString(),"X-TS-ua":navigator.userAgent};function $(t,e,i){var a;const n=(s=e||{},encodeURI(JSON.stringify(s)).split(/%..|./).length-1);var s;return{method:t,headers:u(u(u({},{"X-TS-body-size":String(n)}),M),i||{}),body:null!==(a=e&&JSON.stringify(e||{}))&&void 0!==a?a:void 0}}function q(t,e,i,a,n){const s=H(t,a),r=$(e,i,n);return fetch(s,r)}async function z(t,e,i,a,n){let s;if(s=await q(t,e,i,a,n),!s.ok)throw new Error("Request failed");return s}var J=Object.freeze({__proto__:null,httpDelete:async function(t,e){const i=await z(t,"DELETE",void 0,void 0,e);return u(u({data:await i.json()},i),{},{headers:i.headers})},httpGet:async function(t,e,i){const a=await z(t,"GET",void 0,e,i);return u(u({data:await a.json()},a),{},{headers:a.headers})},httpPost:async function(t,e,i,a){const n=await z(t,"POST",e,i,a);return u(u({data:await n.json()},n),{},{headers:n.headers})},httpPut:async function(t,e,i,a){const n=await z(t,"PUT",e,i,a);return u(u({data:await n.json()},n),{},{headers:n.headers})},init:$}),W=h.create((()=>({events:p,moduleMetadata:s,mainEntry:o,utils:E,storage:D,crypto:x,indexedDB:j,logger:F,http:J})));class L{static arrayBufferToBase64(t){return btoa(String.fromCharCode(...new Uint8Array(t)))}static base64ToArrayBuffer(t){return Uint8Array.from(atob(t),(t=>t.charCodeAt(0)))}static stringToBase64(t){return btoa(t)}static jsonToBase64(t){const e=JSON.stringify(t);return btoa(e)}static base64ToJson(t){const e=atob(t);return JSON.parse(e)}}const U={log:console.log,error:console.error};var V,G;!function(t){t.NotInitialized="not_initialized",t.AuthenticationFailed="authentication_failed",t.AuthenticationAbortedTimeout="authentication_aborted_timeout",t.AuthenticationCanceled="webauthn_authentication_canceled",t.RegistrationFailed="registration_failed",t.AlreadyRegistered="username_already_registered",t.RegistrationAbortedTimeout="registration_aborted_timeout",t.RegistrationCanceled="webauthn_registration_canceled",t.AutofillAuthenticationAborted="autofill_authentication_aborted",t.AuthenticationProcessAlreadyActive="authentication_process_already_active",t.InvalidApprovalData="invalid_approval_data",t.FailedToInitCrossDeviceSession="cross_device_init_failed",t.FailedToGetCrossDeviceStatus="cross_device_status_failed",t.Unknown="unknown"}(V||(V={}));class Z extends Error{constructor(t,e){super(t),this.errorCode=V.NotInitialized,this.data=e}}class X extends Z{constructor(t,e){super(null!=t?t:"WebAuthnSdk is not initialized",e),this.errorCode=V.NotInitialized}}class Y extends Z{constructor(t,e){super(null!=t?t:"Authentication failed with an error",e),this.errorCode=V.AuthenticationFailed}}class Q extends Z{constructor(t,e){super(null!=t?t:"Authentication was canceled by the user or got timeout",e),this.errorCode=V.AuthenticationCanceled}}class tt extends Z{constructor(t,e){super(null!=t?t:"Registration failed with an error",e),this.errorCode=V.RegistrationFailed}}class et extends Z{constructor(t,e){super(null!=t?t:"Registration was canceled by the user or got timeout",e),this.errorCode=V.RegistrationCanceled}}class it extends Z{constructor(t){super(null!=t?t:"Autofill flow was aborted"),this.errorCode=V.AutofillAuthenticationAborted}}class at extends Z{constructor(t){super(null!=t?t:"Operation was aborted by timeout"),this.errorCode=V.AutofillAuthenticationAborted}}class nt extends Z{constructor(t){super(null!=t?t:"Passkey with this username is already registered with the relying party."),this.errorCode=V.AlreadyRegistered}}class st extends Z{constructor(t,e){super(null!=t?t:"Authentication process is already active",e),this.errorCode=V.AuthenticationProcessAlreadyActive}}class rt extends Z{constructor(t,e){super(null!=t?t:"Invalid approval data",e),this.errorCode=V.InvalidApprovalData}}class ot extends Z{constructor(t,e){super(null!=t?t:"Failed to init cross device authentication",e),this.errorCode=V.FailedToInitCrossDeviceSession}}class ct extends Z{constructor(t,e){super(null!=t?t:"Failed to get cross device status",e),this.errorCode=V.FailedToGetCrossDeviceStatus}}function lt(t){return t.errorCode&&Object.values(V).includes(t.errorCode)}!function(t){t[t.persistent=0]="persistent",t[t.session=1]="session"}(G||(G={}));class ut{static get(t){return ut.getStorageMedium(ut.allowedKeys[t]).getItem(ut.getStorageKey(t))||void 0}static set(t,e){return ut.getStorageMedium(ut.allowedKeys[t]).setItem(ut.getStorageKey(t),e)}static remove(t){ut.getStorageMedium(ut.allowedKeys[t]).removeItem(ut.getStorageKey(t))}static clear(t){for(const[e,i]of Object.entries(ut.allowedKeys)){const a=e;t&&this.configurationKeys.includes(a)||ut.getStorageMedium(i).removeItem(ut.getStorageKey(a))}}static getStorageKey(t){return`WebAuthnSdk:${t}`}static getStorageMedium(t){return t===G.session?sessionStorage:localStorage}}ut.allowedKeys={clientId:G.session},ut.configurationKeys=["clientId"];class dt{static isNewApiDomain(t){return t&&(this.newApiDomains.includes(t)||t.startsWith("api.")&&t.endsWith(".transmitsecurity.io"))}static dnsPrefetch(t){const e=document.createElement("link");e.rel="dns-prefetch",e.href=t,document.head.appendChild(e)}static preconnect(t,e){const i=document.createElement("link");i.rel="preconnect",i.href=t,e&&(i.crossOrigin="anonymous"),document.head.appendChild(i)}static warmupConnection(t){this.dnsPrefetch(t),this.preconnect(t,!1),this.preconnect(t,!0)}static init(t,e){var i,a;try{this._serverPath=new URL(e.serverPath),this.isNewApiDomain(null===(i=this._serverPath)||void 0===i?void 0:i.hostname)&&this.warmupConnection(this._serverPath.origin),this._apiPaths=null!==(a=e.webauthnApiPaths)&&void 0!==a?a:this.getDefaultPaths(),this._clientId=t,ut.set("clientId",t)}catch(t){throw new X("Invalid options.serverPath",{error:t})}}static getDefaultPaths(){var t;const e=this.isNewApiDomain(null===(t=this._serverPath)||void 0===t?void 0:t.hostname)?"/cis":"";return{startAuthentication:`${e}/v1/auth/webauthn/authenticate/start`,startRegistration:`${e}/v1/auth/webauthn/register/start`,initCrossDeviceAuthentication:`${e}/v1/auth/webauthn/cross-device/authenticate/init`,startCrossDeviceAuthentication:`${e}/v1/auth/webauthn/cross-device/authenticate/start`,startCrossDeviceRegistration:`${e}/v1/auth/webauthn/cross-device/register/start`,getCrossDeviceTicketStatus:`${e}/v1/auth/webauthn/cross-device/status`,attachDeviceToCrossDeviceSession:`${e}/v1/auth/webauthn/cross-device/attach-device`}}static getApiPaths(){return this._apiPaths}static async sendRequest(t,e,i){U.log(`[WebAuthn SDK] Calling ${e.method} ${t}...`);const a=new URL(this._serverPath);return a.pathname=t,i&&(a.search=i),fetch(a.toString(),e)}static async startRegistration(t){const e=await this.sendRequest(this._apiPaths.startRegistration,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u(u({client_id:this.getValidatedClientId(),username:t.username,display_name:t.displayName},t.timeout&&{timeout:t.timeout}),t.limitSingleCredentialToDevice&&{limit_single_credential_to_device:t.limitSingleCredentialToDevice}))});if(!(null==e?void 0:e.ok))throw new Y("Failed to start registration",null==e?void 0:e.body);return await e.json()}static async startAuthentication(t){const e=await this.sendRequest(this._apiPaths.startAuthentication,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u(u(u({client_id:this.getValidatedClientId()},t.username&&{username:t.username}),t.approvalData&&{approval_data:t.approvalData}),t.timeout&&{timeout:t.timeout}))});if(!(null==e?void 0:e.ok))throw new Y("Failed to start authentication",null==e?void 0:e.body);return await e.json()}static async initCrossDeviceAuthentication(t){const e=await this.sendRequest(this._apiPaths.initCrossDeviceAuthentication,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u(u({client_id:this.getValidatedClientId()},t.username&&{username:t.username}),t.approvalData&&{approval_data:t.approvalData}))});if(!(null==e?void 0:e.ok))throw new ot(void 0,null==e?void 0:e.body);return await e.json()}static async getCrossDeviceTicketStatus(t){const e=await this.sendRequest(this._apiPaths.getCrossDeviceTicketStatus,{method:"GET"},`cross_device_ticket_id=${t.ticketId}`);if(!(null==e?void 0:e.ok))throw new ct(void 0,null==e?void 0:e.body);return await e.json()}static async startCrossDeviceAuthentication(t){const e=await this.sendRequest(this._apiPaths.startCrossDeviceAuthentication,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cross_device_ticket_id:t.ticketId})});if(!(null==e?void 0:e.ok))throw new Y("Failed to start cross device authentication",null==e?void 0:e.body);return await e.json()}static async startCrossDeviceRegistration(t){const e=await this.sendRequest(this._apiPaths.startCrossDeviceRegistration,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cross_device_ticket_id:t.ticketId})});if(!(null==e?void 0:e.ok))throw new tt("Failed to start cross device registration",null==e?void 0:e.body);return await e.json()}static async attachDeviceToCrossDeviceSession(t){const e=await this.sendRequest(this._apiPaths.attachDeviceToCrossDeviceSession,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cross_device_ticket_id:t.ticketId})});if(!(null==e?void 0:e.ok))throw new tt("Failed to attach device to cross device session",null==e?void 0:e.body);return await e.json()}static getValidatedClientId(){var t;const e=null!==(t=this._clientId)&&void 0!==t?t:ut.get("clientId");if(!e)throw new X("Missing clientId");return e}}var ht,yt,pt,gt;dt.newApiDomains=["api.idsec-dev.com","api.idsec-stg.com"],function(t){t.InputAutofill="input-autofill",t.Modal="modal"}(ht||(ht={})),exports.WebauthnCrossDeviceStatus=void 0,(yt=exports.WebauthnCrossDeviceStatus||(exports.WebauthnCrossDeviceStatus={})).Pending="pending",yt.Scanned="scanned",yt.Success="success",yt.Error="error",yt.Timeout="timeout",yt.Aborted="aborted",function(t){t.toAuthenticationError=t=>lt(t)?t:"NotAllowedError"===t.name?new Q:"OperationError"===t.name?new st(t.message):"SecurityError"===t.name?new Y(t.message):t===V.AuthenticationAbortedTimeout?new at:"AbortError"===t.name||t===V.AutofillAuthenticationAborted?new it:new Y("Something went wrong during authentication",{error:t}),t.toRegistrationError=t=>lt(t)?t:"NotAllowedError"===t.name?new et:"SecurityError"===t.name?new tt(t.message):"InvalidStateError"===t.name?new nt:t===V.RegistrationAbortedTimeout?new at:new tt("Something went wrong during registration",{error:t})}(pt||(pt={})),function(t){t.processCredentialRequestOptions=t=>u(u({},t),{},{challenge:L.base64ToArrayBuffer(t.challenge),allowCredentials:t.allowCredentials.map((t=>u(u({},t),{},{id:L.base64ToArrayBuffer(t.id)})))}),t.processCredentialCreationOptions=(t,e)=>{var i;const a=JSON.parse(JSON.stringify(t));return a.challenge=L.base64ToArrayBuffer(t.challenge),a.user.id=L.base64ToArrayBuffer(t.user.id),(null==e?void 0:e.limitSingleCredentialToDevice)&&(a.excludeCredentials=null===(i=t.excludeCredentials)||void 0===i?void 0:i.map((t=>u(u({},t),{},{id:L.base64ToArrayBuffer(t.id)})))),(null==e?void 0:e.registerAsDiscoverable)?(a.authenticatorSelection.residentKey="preferred",a.authenticatorSelection.requireResidentKey=!0):(a.authenticatorSelection.residentKey="discouraged",a.authenticatorSelection.requireResidentKey=!1),a.authenticatorSelection.authenticatorAttachment=(null==e?void 0:e.allowCrossPlatformAuthenticators)?void 0:"platform",a},t.encodeAuthenticationResult=t=>{const{authenticatorAttachment:e}=t,i=t.response;return{id:t.id,rawId:L.arrayBufferToBase64(t.rawId),response:{authenticatorData:L.arrayBufferToBase64(i.authenticatorData),clientDataJSON:L.arrayBufferToBase64(i.clientDataJSON),signature:L.arrayBufferToBase64(i.signature),userHandle:L.arrayBufferToBase64(i.userHandle)},authenticatorAttachment:e,type:t.type}},t.encodeRegistrationResult=t=>{const{authenticatorAttachment:e}=t,i=t.response;return{id:t.id,rawId:L.arrayBufferToBase64(t.rawId),response:{attestationObject:L.arrayBufferToBase64(i.attestationObject),clientDataJSON:L.arrayBufferToBase64(i.clientDataJSON)},authenticatorAttachment:e,type:t.type}}}(gt||(gt={}));class vt{async modal(t){try{const e=await this.performAuthentication(u(u({},t),{},{mediationType:ht.Modal}));return L.jsonToBase64(e)}catch(t){throw pt.toAuthenticationError(t)}}activateAutofill(t,e){const{onSuccess:i,onError:a,onReady:n}=t;this.performAuthentication({username:e,mediationType:ht.InputAutofill,onReady:n}).then((t=>{i(L.jsonToBase64(t))})).catch((t=>{const e=pt.toAuthenticationError(t);if(!a)throw e;a(e)}))}abortAutofill(){this.abortController&&this.abortController.abort(V.AutofillAuthenticationAborted)}abortAuthentication(){this.abortController&&this.abortController.abort(V.AuthenticationAbortedTimeout)}async performAuthentication(t){var e,i;const a="crossDeviceTicketId"in t?await dt.startCrossDeviceAuthentication({ticketId:t.crossDeviceTicketId}):await dt.startAuthentication({username:t.username,timeout:null===(e=t.options)||void 0===e?void 0:e.timeout}),n=a.credential_request_options,s=gt.processCredentialRequestOptions(n),r=this.getMediatedCredentialRequest(s,t.mediationType);t.mediationType===ht.InputAutofill&&(null===(i=t.onReady)||void 0===i||i.call(t));const o=await navigator.credentials.get(r).catch((t=>{throw pt.toAuthenticationError(t)}));return{webauthnSessionId:a.webauthn_session_id,publicKeyCredential:gt.encodeAuthenticationResult(o),userAgent:navigator.userAgent}}getMediatedCredentialRequest(t,e){const i={publicKey:t};return this.abortController=new AbortController,i.signal=this.abortController&&this.abortController.signal,e===ht.InputAutofill?i.mediation="conditional":t.timeout&&setTimeout((()=>{this.abortAuthentication()}),t.timeout),i}}class ft{constructor(t,e){this.handler=t,this.intervalInMs=e}begin(){var t;this.intervalId=window.setInterval((t=this.handler,async function(){t.isRunning||(t.isRunning=!0,await t(...arguments),t.isRunning=!1)}),this.intervalInMs)}stop(){clearInterval(this.intervalId)}}const wt=/^[A-Za-z0-9\-_.: ]*$/;function mt(t){if(t&&(!function(t){return Object.keys(t).length<=10}(t)||!function(t){const e=t=>"string"==typeof t,i=t=>wt.test(t);return Object.keys(t).every((a=>e(a)&&e(t[a])&&i(a)&&i(t[a])))}(t)))throw U.error("Failed validating approval data"),new rt("Provided approval data should have 10 properties max. Also, it should contain only \n alphanumeric characters, numbers, and the special characters: '-', '_', '.'")}class bt{constructor(t,e,i){this.authenticationHandler=t,this.registrationHandler=e,this.approvalHandler=i,this.init={registration:async t=>(this.ticketStatus=exports.WebauthnCrossDeviceStatus.Pending,this.pollCrossDeviceSession(t.crossDeviceTicketId,t.handlers)),authentication:async t=>{const{username:e}=t,i=(await dt.initCrossDeviceAuthentication(u({},e&&{username:e}))).cross_device_ticket_id;return this.ticketStatus=exports.WebauthnCrossDeviceStatus.Pending,this.pollCrossDeviceSession(i,t.handlers)},approval:async t=>{const{username:e,approvalData:i}=t;mt(i);const a=(await dt.initCrossDeviceAuthentication({username:e,approvalData:i})).cross_device_ticket_id;return this.ticketStatus=exports.WebauthnCrossDeviceStatus.Pending,this.pollCrossDeviceSession(a,t.handlers)}},this.authenticate={modal:async t=>this.authenticationHandler.modal({crossDeviceTicketId:t})},this.approve={modal:async t=>this.approvalHandler.modal({crossDeviceTicketId:t})}}async register(t,e){return this.registrationHandler.register({crossDeviceTicketId:t},e)}async attachDevice(t){const e=await dt.attachDeviceToCrossDeviceSession({ticketId:t});return u({status:e.status,startedAt:e.started_at},e.approval_data&&{approvalData:e.approval_data})}async pollCrossDeviceSession(t,e){return this.poller=new ft((async()=>{var i,a;const n=await dt.getCrossDeviceTicketStatus({ticketId:t}),s=n.status;if(s!==this.ticketStatus)switch(this.ticketStatus=s,s){case exports.WebauthnCrossDeviceStatus.Scanned:await e.onDeviceAttach();break;case exports.WebauthnCrossDeviceStatus.Error:case exports.WebauthnCrossDeviceStatus.Timeout:case exports.WebauthnCrossDeviceStatus.Aborted:await e.onFailure(n),null===(i=this.poller)||void 0===i||i.stop();break;case exports.WebauthnCrossDeviceStatus.Success:if("onCredentialRegister"in e)await e.onCredentialRegister();else{if(!n.session_id)throw new ct("Cross device session is complete without returning session_id",n);await e.onCredentialAuthenticate(n.session_id)}null===(a=this.poller)||void 0===a||a.stop()}}),1e3),this.poller.begin(),setTimeout((()=>{var t;null===(t=this.poller)||void 0===t||t.stop(),e.onFailure({status:exports.WebauthnCrossDeviceStatus.Timeout})}),3e5),{crossDeviceTicketId:t,stop:()=>{var t;null===(t=this.poller)||void 0===t||t.stop()}}}}class Dt{async register(t,e){this.abortController=new AbortController;const i=u({allowCrossPlatformAuthenticators:!("crossDeviceTicketId"in t),registerAsDiscoverable:!0},e);try{const a="crossDeviceTicketId"in t?await dt.startCrossDeviceRegistration({ticketId:t.crossDeviceTicketId}):await dt.startRegistration({username:t.username,displayName:(null==e?void 0:e.displayName)||t.username,timeout:null==e?void 0:e.timeout,limitSingleCredentialToDevice:null==e?void 0:e.limitSingleCredentialToDevice}),n=gt.processCredentialCreationOptions(a.credential_creation_options,i);setTimeout((()=>{this.abortRegistration()}),n.timeout);const s=await this.registerCredential(n),r={webauthnSessionId:a.webauthn_session_id,publicKeyCredential:s,userAgent:navigator.userAgent};return L.jsonToBase64(r)}catch(t){throw pt.toRegistrationError(t)}}abortRegistration(){this.abortController&&this.abortController.abort(V.RegistrationAbortedTimeout)}async registerCredential(t){const e=await navigator.credentials.create({publicKey:t,signal:this.abortController&&this.abortController.signal}).catch((t=>{throw pt.toRegistrationError(t)}));return gt.encodeRegistrationResult(e)}}class St{async modal(t){try{const e=await this.performApproval(t);return L.jsonToBase64(e)}catch(t){throw pt.toAuthenticationError(t)}}async performApproval(t){"approvalData"in t&&mt(t.approvalData);const e="crossDeviceTicketId"in t?await dt.startCrossDeviceAuthentication({ticketId:t.crossDeviceTicketId}):await dt.startAuthentication({username:t.username,approvalData:t.approvalData}),i=e.credential_request_options,a=gt.processCredentialRequestOptions(i),n=await navigator.credentials.get({publicKey:a}).catch((t=>{throw pt.toAuthenticationError(t)}));return{webauthnSessionId:e.webauthn_session_id,publicKeyCredential:gt.encodeAuthenticationResult(n),userAgent:navigator.userAgent}}}class At{constructor(){this._initialized=!1,this._authenticationHandler=new vt,this._registrationHandler=new Dt,this._approvalHandler=new St,this._crossDeviceHandler=new bt(this._authenticationHandler,this._registrationHandler,this._approvalHandler),this.authenticate={modal:async(t,e)=>(this.initCheck(),this._authenticationHandler.modal({username:t,options:e})),autofill:{activate:(t,e)=>(this.initCheck(),this._authenticationHandler.activateAutofill(t,e)),abort:()=>this._authenticationHandler.abortAutofill()}},this.approve={modal:async(t,e)=>(this.initCheck(),this._approvalHandler.modal({username:t,approvalData:e}))},this.register=async(t,e)=>(this.initCheck(),this._registrationHandler.register({username:t},e)),this.crossDevice={init:{registration:async t=>(this.initCheck(),this._crossDeviceHandler.init.registration(t)),authentication:async t=>(this.initCheck(),this._crossDeviceHandler.init.authentication(t)),approval:async t=>(this.initCheck(),this._crossDeviceHandler.init.approval(t))},authenticate:{modal:async t=>(this.initCheck(),this._crossDeviceHandler.authenticate.modal(t))},approve:{modal:async t=>(this.initCheck(),this._crossDeviceHandler.approve.modal(t))},register:async(t,e)=>(this.initCheck(),this._crossDeviceHandler.register(t,e)),attachDevice:async t=>(this.initCheck(),this._crossDeviceHandler.attachDevice(t))},this.isPlatformAuthenticatorSupported=async()=>{var t;try{return await(null===(t=At.StaticPublicKeyCredential)||void 0===t?void 0:t.isUserVerifyingPlatformAuthenticatorAvailable())}catch(t){return!1}},this.isAutofillSupported=async()=>{var t,e;return!(!(null===(t=At.StaticPublicKeyCredential)||void 0===t?void 0:t.isConditionalMediationAvailable)||!await(null===(e=At.StaticPublicKeyCredential)||void 0===e?void 0:e.isConditionalMediationAvailable()))}}async init(t,e){try{if(!t)throw new X("Invalid clientId",{clientId:t});if(e.webauthnApiPaths){const t=dt.getDefaultPaths();if(function(t,e){const i=new Set(t),a=new Set(e);return[...t.filter((t=>!a.has(t))),...e.filter((t=>!i.has(t)))]}(Object.keys(e.webauthnApiPaths),Object.keys(t)).length)throw new X("Invalid custom paths",{customApiPaths:e.webauthnApiPaths})}dt.init(t,e),this._initialized=!0}catch(t){throw lt(t)?t:new X("Failed to initialize SDK")}}getDefaultPaths(){return this.initCheck(),dt.getDefaultPaths()}getApiPaths(){return this.initCheck(),dt.getApiPaths()}initCheck(){if(!this._initialized)throw new X}}At.StaticPublicKeyCredential=window.PublicKeyCredential;const Ct=new W("webauthn"),_t=new At;Ct.events.on(Ct.events.MODULE_INITIALIZED,(()=>{var t;const e=Ct.moduleMetadata.getInitConfig();if(!(null===(t=null==e?void 0:e.webauthn)||void 0===t?void 0:t.serverPath))return;const{clientId:i,webauthn:a}=e;_t.init(i,u({},a))}));const Kt={modal:async(t,e)=>(_t.initCheck(),_t.authenticate.modal(t,e)),autofill:{activate:(t,e)=>{_t.initCheck(),_t.authenticate.autofill.activate(t,e)},abort:()=>{_t.initCheck(),_t.authenticate.autofill.abort()}}},kt={modal:async(t,e)=>(_t.initCheck(),_t.approve.modal(t,e))};async function Rt(t,e){return _t.initCheck(),_t.register(t,e)}const{crossDevice:Pt}=_t,{isPlatformAuthenticatorSupported:Tt}=_t,{isAutofillSupported:It}=_t,{getDefaultPaths:Ot}=_t;window.localWebAuthnSDK=_t;const xt={initialize:r,...Object.freeze({__proto__:null,get WebauthnCrossDeviceStatus(){return exports.WebauthnCrossDeviceStatus},approve:kt,authenticate:Kt,crossDevice:Pt,getDefaultPaths:Ot,isAutofillSupported:It,isPlatformAuthenticatorSupported:Tt,register:Rt})};exports.PACKAGE_VERSION="1.18.2",exports.approve=kt,exports.authenticate=Kt,exports.crossDevice=Pt,exports.getDefaultPaths=Ot,exports.initialize=r,exports.isAutofillSupported=It,exports.isPlatformAuthenticatorSupported=Tt,exports.register=Rt,exports.webauthn=xt;
|