@authsignal/browser 0.3.4 → 0.3.6
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/dist/authsignal.d.ts +2 -0
- package/dist/index.js +1589 -1552
- package/dist/index.min.js +2 -2
- package/dist/passkey.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -90,1661 +90,1664 @@ var AuthsignalWindowMessage;
|
|
|
90
90
|
AuthsignalWindowMessage["AUTHSIGNAL_CLOSE_POPUP"] = "AUTHSIGNAL_CLOSE_POPUP";
|
|
91
91
|
})(AuthsignalWindowMessage || (AuthsignalWindowMessage = {}));
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
93
|
+
/******************************************************************************
|
|
94
|
+
Copyright (c) Microsoft Corporation.
|
|
95
|
+
|
|
96
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
97
|
+
purpose with or without fee is hereby granted.
|
|
98
|
+
|
|
99
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
100
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
101
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
102
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
103
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
104
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
105
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
106
|
+
***************************************************************************** */
|
|
107
|
+
|
|
108
|
+
function __rest(s, e) {
|
|
109
|
+
var t = {};
|
|
110
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
111
|
+
t[p] = s[p];
|
|
112
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
113
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
114
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
115
|
+
t[p[i]] = s[p[i]];
|
|
116
|
+
}
|
|
117
|
+
return t;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
121
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
122
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
123
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
124
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
125
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
126
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function __generator(thisArg, body) {
|
|
131
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
132
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
133
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
134
|
+
function step(op) {
|
|
135
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
136
|
+
while (_) try {
|
|
137
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
138
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
139
|
+
switch (op[0]) {
|
|
140
|
+
case 0: case 1: t = op; break;
|
|
141
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
142
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
143
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
144
|
+
default:
|
|
145
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
146
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
147
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
148
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
149
|
+
if (t[2]) _.ops.pop();
|
|
150
|
+
_.trys.pop(); continue;
|
|
151
|
+
}
|
|
152
|
+
op = body.call(thisArg, _);
|
|
153
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
154
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
155
|
+
}
|
|
124
156
|
}
|
|
125
157
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
'input:not([type="hidden"]):not([type="radio"]):not([disabled]):not([tabindex^="-"])',
|
|
130
|
-
'input[type="radio"]:not([disabled]):not([tabindex^="-"])',
|
|
131
|
-
'select:not([disabled]):not([tabindex^="-"])',
|
|
132
|
-
'textarea:not([disabled]):not([tabindex^="-"])',
|
|
133
|
-
'button:not([disabled]):not([tabindex^="-"])',
|
|
134
|
-
'iframe:not([tabindex^="-"])',
|
|
135
|
-
'audio[controls]:not([tabindex^="-"])',
|
|
136
|
-
'video[controls]:not([tabindex^="-"])',
|
|
137
|
-
'[contenteditable]:not([tabindex^="-"])',
|
|
138
|
-
'[tabindex]:not([tabindex^="-"])',
|
|
139
|
-
];
|
|
140
|
-
|
|
141
|
-
var TAB_KEY = 'Tab';
|
|
142
|
-
var ESCAPE_KEY = 'Escape';
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Define the constructor to instantiate a dialog
|
|
146
|
-
*
|
|
147
|
-
* @constructor
|
|
148
|
-
* @param {Element} element
|
|
149
|
-
*/
|
|
150
|
-
function A11yDialog(element) {
|
|
151
|
-
// Prebind the functions that will be bound in addEventListener and
|
|
152
|
-
// removeEventListener to avoid losing references
|
|
153
|
-
this._show = this.show.bind(this);
|
|
154
|
-
this._hide = this.hide.bind(this);
|
|
155
|
-
this._maintainFocus = this._maintainFocus.bind(this);
|
|
156
|
-
this._bindKeypress = this._bindKeypress.bind(this);
|
|
157
|
-
|
|
158
|
-
this.$el = element;
|
|
159
|
-
this.shown = false;
|
|
160
|
-
this._id = this.$el.getAttribute('data-a11y-dialog') || this.$el.id;
|
|
161
|
-
this._previouslyFocused = null;
|
|
162
|
-
this._listeners = {};
|
|
163
|
-
|
|
164
|
-
// Initialise everything needed for the dialog to work properly
|
|
165
|
-
this.create();
|
|
158
|
+
/* [@simplewebauthn/browser@8.2.1] */
|
|
159
|
+
function utf8StringToBuffer(value) {
|
|
160
|
+
return new TextEncoder().encode(value);
|
|
166
161
|
}
|
|
167
162
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
this.$el.setAttribute('tabindex', -1);
|
|
178
|
-
|
|
179
|
-
if (!this.$el.hasAttribute('role')) {
|
|
180
|
-
this.$el.setAttribute('role', 'dialog');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Keep a collection of dialog openers, each of which will be bound a click
|
|
184
|
-
// event listener to open the dialog
|
|
185
|
-
this._openers = $$('[data-a11y-dialog-show="' + this._id + '"]');
|
|
186
|
-
this._openers.forEach(
|
|
187
|
-
function (opener) {
|
|
188
|
-
opener.addEventListener('click', this._show);
|
|
189
|
-
}.bind(this)
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
// Keep a collection of dialog closers, each of which will be bound a click
|
|
193
|
-
// event listener to close the dialog
|
|
194
|
-
const $el = this.$el;
|
|
195
|
-
|
|
196
|
-
this._closers = $$('[data-a11y-dialog-hide]', this.$el)
|
|
197
|
-
// This filter is necessary in case there are nested dialogs, so that
|
|
198
|
-
// only closers from the current dialog are retrieved and effective
|
|
199
|
-
.filter(function (closer) {
|
|
200
|
-
// Testing for `[aria-modal="true"]` is not enough since this attribute
|
|
201
|
-
// and the collect of closers is done at instantation time, when nested
|
|
202
|
-
// dialogs might not have yet been instantiated. Note that if the dialogs
|
|
203
|
-
// are manually instantiated, this could still fail because none of these
|
|
204
|
-
// selectors would match; this would cause closers to close all parent
|
|
205
|
-
// dialogs instead of just the current one
|
|
206
|
-
return closer.closest('[aria-modal="true"], [data-a11y-dialog]') === $el
|
|
207
|
-
})
|
|
208
|
-
.concat($$('[data-a11y-dialog-hide="' + this._id + '"]'));
|
|
209
|
-
|
|
210
|
-
this._closers.forEach(
|
|
211
|
-
function (closer) {
|
|
212
|
-
closer.addEventListener('click', this._hide);
|
|
213
|
-
}.bind(this)
|
|
214
|
-
);
|
|
163
|
+
function bufferToBase64URLString(buffer) {
|
|
164
|
+
const bytes = new Uint8Array(buffer);
|
|
165
|
+
let str = '';
|
|
166
|
+
for (const charCode of bytes) {
|
|
167
|
+
str += String.fromCharCode(charCode);
|
|
168
|
+
}
|
|
169
|
+
const base64String = btoa(str);
|
|
170
|
+
return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
171
|
+
}
|
|
215
172
|
|
|
216
|
-
|
|
217
|
-
|
|
173
|
+
function base64URLStringToBuffer(base64URLString) {
|
|
174
|
+
const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/');
|
|
175
|
+
const padLength = (4 - (base64.length % 4)) % 4;
|
|
176
|
+
const padded = base64.padEnd(base64.length + padLength, '=');
|
|
177
|
+
const binary = atob(padded);
|
|
178
|
+
const buffer = new ArrayBuffer(binary.length);
|
|
179
|
+
const bytes = new Uint8Array(buffer);
|
|
180
|
+
for (let i = 0; i < binary.length; i++) {
|
|
181
|
+
bytes[i] = binary.charCodeAt(i);
|
|
182
|
+
}
|
|
183
|
+
return buffer;
|
|
184
|
+
}
|
|
218
185
|
|
|
219
|
-
|
|
220
|
-
|
|
186
|
+
function browserSupportsWebAuthn() {
|
|
187
|
+
return (window?.PublicKeyCredential !== undefined &&
|
|
188
|
+
typeof window.PublicKeyCredential === 'function');
|
|
189
|
+
}
|
|
221
190
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
A11yDialog.prototype.show = function (event) {
|
|
231
|
-
// If the dialog is already open, abort
|
|
232
|
-
if (this.shown) {
|
|
233
|
-
return this
|
|
234
|
-
}
|
|
191
|
+
function toPublicKeyCredentialDescriptor(descriptor) {
|
|
192
|
+
const { id } = descriptor;
|
|
193
|
+
return {
|
|
194
|
+
...descriptor,
|
|
195
|
+
id: base64URLStringToBuffer(id),
|
|
196
|
+
transports: descriptor.transports,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
235
199
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
this.shown = true;
|
|
200
|
+
function isValidDomain(hostname) {
|
|
201
|
+
return (hostname === 'localhost' ||
|
|
202
|
+
/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname));
|
|
203
|
+
}
|
|
241
204
|
|
|
242
|
-
|
|
243
|
-
|
|
205
|
+
class WebAuthnError extends Error {
|
|
206
|
+
constructor({ message, code, cause, name, }) {
|
|
207
|
+
super(message, { cause });
|
|
208
|
+
this.name = name ?? cause.name;
|
|
209
|
+
this.code = code;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
244
212
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
213
|
+
function identifyRegistrationError({ error, options, }) {
|
|
214
|
+
const { publicKey } = options;
|
|
215
|
+
if (!publicKey) {
|
|
216
|
+
throw Error('options was missing required publicKey property');
|
|
217
|
+
}
|
|
218
|
+
if (error.name === 'AbortError') {
|
|
219
|
+
if (options.signal instanceof AbortSignal) {
|
|
220
|
+
return new WebAuthnError({
|
|
221
|
+
message: 'Registration ceremony was sent an abort signal',
|
|
222
|
+
code: 'ERROR_CEREMONY_ABORTED',
|
|
223
|
+
cause: error,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else if (error.name === 'ConstraintError') {
|
|
228
|
+
if (publicKey.authenticatorSelection?.requireResidentKey === true) {
|
|
229
|
+
return new WebAuthnError({
|
|
230
|
+
message: 'Discoverable credentials were required but no available authenticator supported it',
|
|
231
|
+
code: 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT',
|
|
232
|
+
cause: error,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
else if (publicKey.authenticatorSelection?.userVerification === 'required') {
|
|
236
|
+
return new WebAuthnError({
|
|
237
|
+
message: 'User verification was required but no available authenticator supported it',
|
|
238
|
+
code: 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT',
|
|
239
|
+
cause: error,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else if (error.name === 'InvalidStateError') {
|
|
244
|
+
return new WebAuthnError({
|
|
245
|
+
message: 'The authenticator was previously registered',
|
|
246
|
+
code: 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED',
|
|
247
|
+
cause: error,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
else if (error.name === 'NotAllowedError') {
|
|
251
|
+
return new WebAuthnError({
|
|
252
|
+
message: error.message,
|
|
253
|
+
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
|
|
254
|
+
cause: error,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
else if (error.name === 'NotSupportedError') {
|
|
258
|
+
const validPubKeyCredParams = publicKey.pubKeyCredParams.filter((param) => param.type === 'public-key');
|
|
259
|
+
if (validPubKeyCredParams.length === 0) {
|
|
260
|
+
return new WebAuthnError({
|
|
261
|
+
message: 'No entry in pubKeyCredParams was of type "public-key"',
|
|
262
|
+
code: 'ERROR_MALFORMED_PUBKEYCREDPARAMS',
|
|
263
|
+
cause: error,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return new WebAuthnError({
|
|
267
|
+
message: 'No available authenticator supported any of the specified pubKeyCredParams algorithms',
|
|
268
|
+
code: 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG',
|
|
269
|
+
cause: error,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
else if (error.name === 'SecurityError') {
|
|
273
|
+
const effectiveDomain = window.location.hostname;
|
|
274
|
+
if (!isValidDomain(effectiveDomain)) {
|
|
275
|
+
return new WebAuthnError({
|
|
276
|
+
message: `${window.location.hostname} is an invalid domain`,
|
|
277
|
+
code: 'ERROR_INVALID_DOMAIN',
|
|
278
|
+
cause: error,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
else if (publicKey.rp.id !== effectiveDomain) {
|
|
282
|
+
return new WebAuthnError({
|
|
283
|
+
message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
|
|
284
|
+
code: 'ERROR_INVALID_RP_ID',
|
|
285
|
+
cause: error,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else if (error.name === 'TypeError') {
|
|
290
|
+
if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
|
|
291
|
+
return new WebAuthnError({
|
|
292
|
+
message: 'User ID was not between 1 and 64 characters',
|
|
293
|
+
code: 'ERROR_INVALID_USER_ID_LENGTH',
|
|
294
|
+
cause: error,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else if (error.name === 'UnknownError') {
|
|
299
|
+
return new WebAuthnError({
|
|
300
|
+
message: 'The authenticator was unable to process the specified options, or could not create a new credential',
|
|
301
|
+
code: 'ERROR_AUTHENTICATOR_GENERAL_ERROR',
|
|
302
|
+
cause: error,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return error;
|
|
306
|
+
}
|
|
273
307
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
308
|
+
class WebAuthnAbortService {
|
|
309
|
+
createNewAbortSignal() {
|
|
310
|
+
if (this.controller) {
|
|
311
|
+
const abortError = new Error('Cancelling existing WebAuthn API call for new one');
|
|
312
|
+
abortError.name = 'AbortError';
|
|
313
|
+
this.controller.abort(abortError);
|
|
314
|
+
}
|
|
315
|
+
const newController = new AbortController();
|
|
316
|
+
this.controller = newController;
|
|
317
|
+
return newController.signal;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const webauthnAbortService = new WebAuthnAbortService();
|
|
280
321
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
322
|
+
const attachments = ['cross-platform', 'platform'];
|
|
323
|
+
function toAuthenticatorAttachment(attachment) {
|
|
324
|
+
if (!attachment) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (attachments.indexOf(attachment) < 0) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
return attachment;
|
|
331
|
+
}
|
|
285
332
|
|
|
286
|
-
|
|
287
|
-
|
|
333
|
+
async function startRegistration(creationOptionsJSON) {
|
|
334
|
+
if (!browserSupportsWebAuthn()) {
|
|
335
|
+
throw new Error('WebAuthn is not supported in this browser');
|
|
336
|
+
}
|
|
337
|
+
const publicKey = {
|
|
338
|
+
...creationOptionsJSON,
|
|
339
|
+
challenge: base64URLStringToBuffer(creationOptionsJSON.challenge),
|
|
340
|
+
user: {
|
|
341
|
+
...creationOptionsJSON.user,
|
|
342
|
+
id: utf8StringToBuffer(creationOptionsJSON.user.id),
|
|
343
|
+
},
|
|
344
|
+
excludeCredentials: creationOptionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor),
|
|
345
|
+
};
|
|
346
|
+
const options = { publicKey };
|
|
347
|
+
options.signal = webauthnAbortService.createNewAbortSignal();
|
|
348
|
+
let credential;
|
|
349
|
+
try {
|
|
350
|
+
credential = (await navigator.credentials.create(options));
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
throw identifyRegistrationError({ error: err, options });
|
|
354
|
+
}
|
|
355
|
+
if (!credential) {
|
|
356
|
+
throw new Error('Registration was not completed');
|
|
357
|
+
}
|
|
358
|
+
const { id, rawId, response, type } = credential;
|
|
359
|
+
let transports = undefined;
|
|
360
|
+
if (typeof response.getTransports === 'function') {
|
|
361
|
+
transports = response.getTransports();
|
|
362
|
+
}
|
|
363
|
+
let responsePublicKeyAlgorithm = undefined;
|
|
364
|
+
if (typeof response.getPublicKeyAlgorithm === 'function') {
|
|
365
|
+
try {
|
|
366
|
+
responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
warnOnBrokenImplementation('getPublicKeyAlgorithm()', error);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
let responsePublicKey = undefined;
|
|
373
|
+
if (typeof response.getPublicKey === 'function') {
|
|
374
|
+
try {
|
|
375
|
+
const _publicKey = response.getPublicKey();
|
|
376
|
+
if (_publicKey !== null) {
|
|
377
|
+
responsePublicKey = bufferToBase64URLString(_publicKey);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
warnOnBrokenImplementation('getPublicKey()', error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
let responseAuthenticatorData;
|
|
385
|
+
if (typeof response.getAuthenticatorData === 'function') {
|
|
386
|
+
try {
|
|
387
|
+
responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
warnOnBrokenImplementation('getAuthenticatorData()', error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
id,
|
|
395
|
+
rawId: bufferToBase64URLString(rawId),
|
|
396
|
+
response: {
|
|
397
|
+
attestationObject: bufferToBase64URLString(response.attestationObject),
|
|
398
|
+
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
399
|
+
transports,
|
|
400
|
+
publicKeyAlgorithm: responsePublicKeyAlgorithm,
|
|
401
|
+
publicKey: responsePublicKey,
|
|
402
|
+
authenticatorData: responseAuthenticatorData,
|
|
403
|
+
},
|
|
404
|
+
type,
|
|
405
|
+
clientExtensionResults: credential.getClientExtensionResults(),
|
|
406
|
+
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function warnOnBrokenImplementation(methodName, cause) {
|
|
410
|
+
console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.\n`, cause);
|
|
411
|
+
}
|
|
288
412
|
|
|
289
|
-
|
|
290
|
-
|
|
413
|
+
function bufferToUTF8String(value) {
|
|
414
|
+
return new TextDecoder('utf-8').decode(value);
|
|
415
|
+
}
|
|
291
416
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
this.hide();
|
|
417
|
+
function browserSupportsWebAuthnAutofill() {
|
|
418
|
+
const globalPublicKeyCredential = window
|
|
419
|
+
.PublicKeyCredential;
|
|
420
|
+
if (globalPublicKeyCredential.isConditionalMediationAvailable === undefined) {
|
|
421
|
+
return new Promise((resolve) => resolve(false));
|
|
422
|
+
}
|
|
423
|
+
return globalPublicKeyCredential.isConditionalMediationAvailable();
|
|
424
|
+
}
|
|
301
425
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
426
|
+
function identifyAuthenticationError({ error, options, }) {
|
|
427
|
+
const { publicKey } = options;
|
|
428
|
+
if (!publicKey) {
|
|
429
|
+
throw Error('options was missing required publicKey property');
|
|
430
|
+
}
|
|
431
|
+
if (error.name === 'AbortError') {
|
|
432
|
+
if (options.signal instanceof AbortSignal) {
|
|
433
|
+
return new WebAuthnError({
|
|
434
|
+
message: 'Authentication ceremony was sent an abort signal',
|
|
435
|
+
code: 'ERROR_CEREMONY_ABORTED',
|
|
436
|
+
cause: error,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else if (error.name === 'NotAllowedError') {
|
|
441
|
+
return new WebAuthnError({
|
|
442
|
+
message: error.message,
|
|
443
|
+
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
|
|
444
|
+
cause: error,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
else if (error.name === 'SecurityError') {
|
|
448
|
+
const effectiveDomain = window.location.hostname;
|
|
449
|
+
if (!isValidDomain(effectiveDomain)) {
|
|
450
|
+
return new WebAuthnError({
|
|
451
|
+
message: `${window.location.hostname} is an invalid domain`,
|
|
452
|
+
code: 'ERROR_INVALID_DOMAIN',
|
|
453
|
+
cause: error,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
else if (publicKey.rpId !== effectiveDomain) {
|
|
457
|
+
return new WebAuthnError({
|
|
458
|
+
message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
|
|
459
|
+
code: 'ERROR_INVALID_RP_ID',
|
|
460
|
+
cause: error,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
else if (error.name === 'UnknownError') {
|
|
465
|
+
return new WebAuthnError({
|
|
466
|
+
message: 'The authenticator was unable to process the specified options, or could not create a new assertion signature',
|
|
467
|
+
code: 'ERROR_AUTHENTICATOR_GENERAL_ERROR',
|
|
468
|
+
cause: error,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
return error;
|
|
472
|
+
}
|
|
318
473
|
|
|
319
|
-
|
|
320
|
-
|
|
474
|
+
async function startAuthentication(requestOptionsJSON, useBrowserAutofill = false) {
|
|
475
|
+
if (!browserSupportsWebAuthn()) {
|
|
476
|
+
throw new Error('WebAuthn is not supported in this browser');
|
|
477
|
+
}
|
|
478
|
+
let allowCredentials;
|
|
479
|
+
if (requestOptionsJSON.allowCredentials?.length !== 0) {
|
|
480
|
+
allowCredentials = requestOptionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
|
|
481
|
+
}
|
|
482
|
+
const publicKey = {
|
|
483
|
+
...requestOptionsJSON,
|
|
484
|
+
challenge: base64URLStringToBuffer(requestOptionsJSON.challenge),
|
|
485
|
+
allowCredentials,
|
|
486
|
+
};
|
|
487
|
+
const options = {};
|
|
488
|
+
if (useBrowserAutofill) {
|
|
489
|
+
if (!(await browserSupportsWebAuthnAutofill())) {
|
|
490
|
+
throw Error('Browser does not support WebAuthn autofill');
|
|
491
|
+
}
|
|
492
|
+
const eligibleInputs = document.querySelectorAll('input[autocomplete*=\'webauthn\']');
|
|
493
|
+
if (eligibleInputs.length < 1) {
|
|
494
|
+
throw Error('No <input> with `"webauthn"` in its `autocomplete` attribute was detected');
|
|
495
|
+
}
|
|
496
|
+
options.mediation = 'conditional';
|
|
497
|
+
publicKey.allowCredentials = [];
|
|
498
|
+
}
|
|
499
|
+
options.publicKey = publicKey;
|
|
500
|
+
options.signal = webauthnAbortService.createNewAbortSignal();
|
|
501
|
+
let credential;
|
|
502
|
+
try {
|
|
503
|
+
credential = (await navigator.credentials.get(options));
|
|
504
|
+
}
|
|
505
|
+
catch (err) {
|
|
506
|
+
throw identifyAuthenticationError({ error: err, options });
|
|
507
|
+
}
|
|
508
|
+
if (!credential) {
|
|
509
|
+
throw new Error('Authentication was not completed');
|
|
510
|
+
}
|
|
511
|
+
const { id, rawId, response, type } = credential;
|
|
512
|
+
let userHandle = undefined;
|
|
513
|
+
if (response.userHandle) {
|
|
514
|
+
userHandle = bufferToUTF8String(response.userHandle);
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
id,
|
|
518
|
+
rawId: bufferToBase64URLString(rawId),
|
|
519
|
+
response: {
|
|
520
|
+
authenticatorData: bufferToBase64URLString(response.authenticatorData),
|
|
521
|
+
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
522
|
+
signature: bufferToBase64URLString(response.signature),
|
|
523
|
+
userHandle,
|
|
524
|
+
},
|
|
525
|
+
type,
|
|
526
|
+
clientExtensionResults: credential.getClientExtensionResults(),
|
|
527
|
+
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
321
530
|
|
|
322
|
-
|
|
323
|
-
|
|
531
|
+
// eslint-lint-disable-next-line @typescript-eslint/naming-convention
|
|
532
|
+
class HTTPError extends Error {
|
|
533
|
+
constructor(response, request, options) {
|
|
534
|
+
const code = (response.status || response.status === 0) ? response.status : '';
|
|
535
|
+
const title = response.statusText || '';
|
|
536
|
+
const status = `${code} ${title}`.trim();
|
|
537
|
+
const reason = status ? `status code ${status}` : 'an unknown error';
|
|
538
|
+
super(`Request failed with ${reason}`);
|
|
539
|
+
Object.defineProperty(this, "response", {
|
|
540
|
+
enumerable: true,
|
|
541
|
+
configurable: true,
|
|
542
|
+
writable: true,
|
|
543
|
+
value: void 0
|
|
544
|
+
});
|
|
545
|
+
Object.defineProperty(this, "request", {
|
|
546
|
+
enumerable: true,
|
|
547
|
+
configurable: true,
|
|
548
|
+
writable: true,
|
|
549
|
+
value: void 0
|
|
550
|
+
});
|
|
551
|
+
Object.defineProperty(this, "options", {
|
|
552
|
+
enumerable: true,
|
|
553
|
+
configurable: true,
|
|
554
|
+
writable: true,
|
|
555
|
+
value: void 0
|
|
556
|
+
});
|
|
557
|
+
this.name = 'HTTPError';
|
|
558
|
+
this.response = response;
|
|
559
|
+
this.request = request;
|
|
560
|
+
this.options = options;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
324
563
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
564
|
+
class TimeoutError extends Error {
|
|
565
|
+
constructor(request) {
|
|
566
|
+
super('Request timed out');
|
|
567
|
+
Object.defineProperty(this, "request", {
|
|
568
|
+
enumerable: true,
|
|
569
|
+
configurable: true,
|
|
570
|
+
writable: true,
|
|
571
|
+
value: void 0
|
|
572
|
+
});
|
|
573
|
+
this.name = 'TimeoutError';
|
|
574
|
+
this.request = request;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
335
577
|
|
|
336
|
-
|
|
578
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
579
|
+
const isObject = (value) => value !== null && typeof value === 'object';
|
|
337
580
|
|
|
338
|
-
|
|
581
|
+
const validateAndMerge = (...sources) => {
|
|
582
|
+
for (const source of sources) {
|
|
583
|
+
if ((!isObject(source) || Array.isArray(source)) && typeof source !== 'undefined') {
|
|
584
|
+
throw new TypeError('The `options` argument must be an object');
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return deepMerge({}, ...sources);
|
|
339
588
|
};
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
589
|
+
const mergeHeaders = (source1 = {}, source2 = {}) => {
|
|
590
|
+
const result = new globalThis.Headers(source1);
|
|
591
|
+
const isHeadersInstance = source2 instanceof globalThis.Headers;
|
|
592
|
+
const source = new globalThis.Headers(source2);
|
|
593
|
+
for (const [key, value] of source.entries()) {
|
|
594
|
+
if ((isHeadersInstance && value === 'undefined') || value === undefined) {
|
|
595
|
+
result.delete(key);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
result.set(key, value);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return result;
|
|
602
|
+
};
|
|
603
|
+
// TODO: Make this strongly-typed (no `any`).
|
|
604
|
+
const deepMerge = (...sources) => {
|
|
605
|
+
let returnValue = {};
|
|
606
|
+
let headers = {};
|
|
607
|
+
for (const source of sources) {
|
|
608
|
+
if (Array.isArray(source)) {
|
|
609
|
+
if (!Array.isArray(returnValue)) {
|
|
610
|
+
returnValue = [];
|
|
611
|
+
}
|
|
612
|
+
returnValue = [...returnValue, ...source];
|
|
613
|
+
}
|
|
614
|
+
else if (isObject(source)) {
|
|
615
|
+
for (let [key, value] of Object.entries(source)) {
|
|
616
|
+
if (isObject(value) && key in returnValue) {
|
|
617
|
+
value = deepMerge(returnValue[key], value);
|
|
618
|
+
}
|
|
619
|
+
returnValue = { ...returnValue, [key]: value };
|
|
620
|
+
}
|
|
621
|
+
if (isObject(source.headers)) {
|
|
622
|
+
headers = mergeHeaders(headers, source.headers);
|
|
623
|
+
returnValue.headers = headers;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return returnValue;
|
|
355
628
|
};
|
|
356
629
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
630
|
+
const supportsRequestStreams = (() => {
|
|
631
|
+
let duplexAccessed = false;
|
|
632
|
+
let hasContentType = false;
|
|
633
|
+
const supportsReadableStream = typeof globalThis.ReadableStream === 'function';
|
|
634
|
+
const supportsRequest = typeof globalThis.Request === 'function';
|
|
635
|
+
if (supportsReadableStream && supportsRequest) {
|
|
636
|
+
hasContentType = new globalThis.Request('https://a.com', {
|
|
637
|
+
body: new globalThis.ReadableStream(),
|
|
638
|
+
method: 'POST',
|
|
639
|
+
// @ts-expect-error - Types are outdated.
|
|
640
|
+
get duplex() {
|
|
641
|
+
duplexAccessed = true;
|
|
642
|
+
return 'half';
|
|
643
|
+
},
|
|
644
|
+
}).headers.has('Content-Type');
|
|
645
|
+
}
|
|
646
|
+
return duplexAccessed && !hasContentType;
|
|
647
|
+
})();
|
|
648
|
+
const supportsAbortController = typeof globalThis.AbortController === 'function';
|
|
649
|
+
const supportsResponseStreams = typeof globalThis.ReadableStream === 'function';
|
|
650
|
+
const supportsFormData = typeof globalThis.FormData === 'function';
|
|
651
|
+
const requestMethods = ['get', 'post', 'put', 'patch', 'head', 'delete'];
|
|
652
|
+
const responseTypes = {
|
|
653
|
+
json: 'application/json',
|
|
654
|
+
text: 'text/*',
|
|
655
|
+
formData: 'multipart/form-data',
|
|
656
|
+
arrayBuffer: '*/*',
|
|
657
|
+
blob: '*/*',
|
|
378
658
|
};
|
|
659
|
+
// The maximum value of a 32bit int (see issue #117)
|
|
660
|
+
const maxSafeTimeout = 2147483647;
|
|
661
|
+
const stop = Symbol('stop');
|
|
379
662
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (focused && focused.closest('[aria-modal="true"]') !== this.$el) return
|
|
392
|
-
|
|
393
|
-
// If the dialog is shown and the ESCAPE key is being pressed, prevent any
|
|
394
|
-
// further effects from the ESCAPE key and hide the dialog, unless its role
|
|
395
|
-
// is 'alertdialog', which should be modal
|
|
396
|
-
if (
|
|
397
|
-
this.shown &&
|
|
398
|
-
event.key === ESCAPE_KEY &&
|
|
399
|
-
this.$el.getAttribute('role') !== 'alertdialog'
|
|
400
|
-
) {
|
|
401
|
-
event.preventDefault();
|
|
402
|
-
this.hide(event);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// If the dialog is shown and the TAB key is being pressed, make sure the
|
|
406
|
-
// focus stays trapped within the dialog element
|
|
407
|
-
if (this.shown && event.key === TAB_KEY) {
|
|
408
|
-
trapTabKey(this.$el, event);
|
|
409
|
-
}
|
|
663
|
+
const normalizeRequestMethod = (input) => requestMethods.includes(input) ? input.toUpperCase() : input;
|
|
664
|
+
const retryMethods = ['get', 'put', 'head', 'delete', 'options', 'trace'];
|
|
665
|
+
const retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
|
|
666
|
+
const retryAfterStatusCodes = [413, 429, 503];
|
|
667
|
+
const defaultRetryOptions = {
|
|
668
|
+
limit: 2,
|
|
669
|
+
methods: retryMethods,
|
|
670
|
+
statusCodes: retryStatusCodes,
|
|
671
|
+
afterStatusCodes: retryAfterStatusCodes,
|
|
672
|
+
maxRetryAfter: Number.POSITIVE_INFINITY,
|
|
673
|
+
backoffLimit: Number.POSITIVE_INFINITY,
|
|
410
674
|
};
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
) {
|
|
430
|
-
moveFocusToDialog(this.$el);
|
|
431
|
-
}
|
|
675
|
+
const normalizeRetryOptions = (retry = {}) => {
|
|
676
|
+
if (typeof retry === 'number') {
|
|
677
|
+
return {
|
|
678
|
+
...defaultRetryOptions,
|
|
679
|
+
limit: retry,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
if (retry.methods && !Array.isArray(retry.methods)) {
|
|
683
|
+
throw new Error('retry.methods must be an array');
|
|
684
|
+
}
|
|
685
|
+
if (retry.statusCodes && !Array.isArray(retry.statusCodes)) {
|
|
686
|
+
throw new Error('retry.statusCodes must be an array');
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
...defaultRetryOptions,
|
|
690
|
+
...retry,
|
|
691
|
+
afterStatusCodes: retryAfterStatusCodes,
|
|
692
|
+
};
|
|
432
693
|
};
|
|
433
694
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
*/
|
|
452
|
-
function $$(selector, context) {
|
|
453
|
-
return toArray((context || document).querySelectorAll(selector))
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Set the focus to the first element with `autofocus` with the element or the
|
|
458
|
-
* element itself
|
|
459
|
-
*
|
|
460
|
-
* @param {Element} node
|
|
461
|
-
*/
|
|
462
|
-
function moveFocusToDialog(node) {
|
|
463
|
-
var focused = node.querySelector('[autofocus]') || node;
|
|
464
|
-
|
|
465
|
-
focused.focus();
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Get the focusable children of the given element
|
|
470
|
-
*
|
|
471
|
-
* @param {Element} node
|
|
472
|
-
* @return {Array<Element>}
|
|
473
|
-
*/
|
|
474
|
-
function getFocusableChildren(node) {
|
|
475
|
-
return $$(focusableSelectors.join(','), node).filter(function (child) {
|
|
476
|
-
return !!(
|
|
477
|
-
child.offsetWidth ||
|
|
478
|
-
child.offsetHeight ||
|
|
479
|
-
child.getClientRects().length
|
|
480
|
-
)
|
|
481
|
-
})
|
|
695
|
+
// `Promise.race()` workaround (#91)
|
|
696
|
+
async function timeout(request, abortController, options) {
|
|
697
|
+
return new Promise((resolve, reject) => {
|
|
698
|
+
const timeoutId = setTimeout(() => {
|
|
699
|
+
if (abortController) {
|
|
700
|
+
abortController.abort();
|
|
701
|
+
}
|
|
702
|
+
reject(new TimeoutError(request));
|
|
703
|
+
}, options.timeout);
|
|
704
|
+
void options
|
|
705
|
+
.fetch(request)
|
|
706
|
+
.then(resolve)
|
|
707
|
+
.catch(reject)
|
|
708
|
+
.then(() => {
|
|
709
|
+
clearTimeout(timeoutId);
|
|
710
|
+
});
|
|
711
|
+
});
|
|
482
712
|
}
|
|
483
713
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// If the SHIFT key is not being pressed (moving forwards) and the currently
|
|
501
|
-
// focused item is the last one, move the focus to the first focusable item
|
|
502
|
-
// from the dialog element
|
|
503
|
-
} else if (
|
|
504
|
-
!event.shiftKey &&
|
|
505
|
-
focusedItemIndex === focusableChildren.length - 1
|
|
506
|
-
) {
|
|
507
|
-
focusableChildren[0].focus();
|
|
508
|
-
event.preventDefault();
|
|
509
|
-
}
|
|
714
|
+
// DOMException is supported on most modern browsers and Node.js 18+.
|
|
715
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException#browser_compatibility
|
|
716
|
+
const isDomExceptionSupported = Boolean(globalThis.DOMException);
|
|
717
|
+
// TODO: When targeting Node.js 18, use `signal.throwIfAborted()` (https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/throwIfAborted)
|
|
718
|
+
function composeAbortError(signal) {
|
|
719
|
+
/*
|
|
720
|
+
NOTE: Use DomException with AbortError name as specified in MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort)
|
|
721
|
+
> When abort() is called, the fetch() promise rejects with an Error of type DOMException, with name AbortError.
|
|
722
|
+
*/
|
|
723
|
+
if (isDomExceptionSupported) {
|
|
724
|
+
return new DOMException(signal?.reason ?? 'The operation was aborted.', 'AbortError');
|
|
725
|
+
}
|
|
726
|
+
// DOMException not supported. Fall back to use of error and override name.
|
|
727
|
+
const error = new Error(signal?.reason ?? 'The operation was aborted.');
|
|
728
|
+
error.name = 'AbortError';
|
|
729
|
+
return error;
|
|
510
730
|
}
|
|
511
731
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
new
|
|
515
|
-
|
|
732
|
+
// https://github.com/sindresorhus/delay/tree/ab98ae8dfcb38e1593286c94d934e70d14a4e111
|
|
733
|
+
async function delay(ms, { signal }) {
|
|
734
|
+
return new Promise((resolve, reject) => {
|
|
735
|
+
if (signal) {
|
|
736
|
+
if (signal.aborted) {
|
|
737
|
+
reject(composeAbortError(signal));
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
signal.addEventListener('abort', handleAbort, { once: true });
|
|
741
|
+
}
|
|
742
|
+
function handleAbort() {
|
|
743
|
+
reject(composeAbortError(signal));
|
|
744
|
+
clearTimeout(timeoutId);
|
|
745
|
+
}
|
|
746
|
+
const timeoutId = setTimeout(() => {
|
|
747
|
+
signal?.removeEventListener('abort', handleAbort);
|
|
748
|
+
resolve();
|
|
749
|
+
}, ms);
|
|
750
|
+
});
|
|
516
751
|
}
|
|
517
752
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
753
|
+
class Ky {
|
|
754
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
755
|
+
static create(input, options) {
|
|
756
|
+
const ky = new Ky(input, options);
|
|
757
|
+
const fn = async () => {
|
|
758
|
+
if (ky._options.timeout > maxSafeTimeout) {
|
|
759
|
+
throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`);
|
|
760
|
+
}
|
|
761
|
+
// Delay the fetch so that body method shortcuts can set the Accept header
|
|
762
|
+
await Promise.resolve();
|
|
763
|
+
let response = await ky._fetch();
|
|
764
|
+
for (const hook of ky._options.hooks.afterResponse) {
|
|
765
|
+
// eslint-disable-next-line no-await-in-loop
|
|
766
|
+
const modifiedResponse = await hook(ky.request, ky._options, ky._decorateResponse(response.clone()));
|
|
767
|
+
if (modifiedResponse instanceof globalThis.Response) {
|
|
768
|
+
response = modifiedResponse;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
ky._decorateResponse(response);
|
|
772
|
+
if (!response.ok && ky._options.throwHttpErrors) {
|
|
773
|
+
let error = new HTTPError(response, ky.request, ky._options);
|
|
774
|
+
for (const hook of ky._options.hooks.beforeError) {
|
|
775
|
+
// eslint-disable-next-line no-await-in-loop
|
|
776
|
+
error = await hook(error);
|
|
777
|
+
}
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
// If `onDownloadProgress` is passed, it uses the stream API internally
|
|
781
|
+
/* istanbul ignore next */
|
|
782
|
+
if (ky._options.onDownloadProgress) {
|
|
783
|
+
if (typeof ky._options.onDownloadProgress !== 'function') {
|
|
784
|
+
throw new TypeError('The `onDownloadProgress` option must be a function');
|
|
785
|
+
}
|
|
786
|
+
if (!supportsResponseStreams) {
|
|
787
|
+
throw new Error('Streams are not supported in your environment. `ReadableStream` is missing.');
|
|
788
|
+
}
|
|
789
|
+
return ky._stream(response.clone(), ky._options.onDownloadProgress);
|
|
790
|
+
}
|
|
791
|
+
return response;
|
|
792
|
+
};
|
|
793
|
+
const isRetriableMethod = ky._options.retry.methods.includes(ky.request.method.toLowerCase());
|
|
794
|
+
const result = (isRetriableMethod ? ky._retry(fn) : fn());
|
|
795
|
+
for (const [type, mimeType] of Object.entries(responseTypes)) {
|
|
796
|
+
result[type] = async () => {
|
|
797
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
798
|
+
ky.request.headers.set('accept', ky.request.headers.get('accept') || mimeType);
|
|
799
|
+
const awaitedResult = await result;
|
|
800
|
+
const response = awaitedResult.clone();
|
|
801
|
+
if (type === 'json') {
|
|
802
|
+
if (response.status === 204) {
|
|
803
|
+
return '';
|
|
804
|
+
}
|
|
805
|
+
const arrayBuffer = await response.clone().arrayBuffer();
|
|
806
|
+
const responseSize = arrayBuffer.byteLength;
|
|
807
|
+
if (responseSize === 0) {
|
|
808
|
+
return '';
|
|
809
|
+
}
|
|
810
|
+
if (options.parseJson) {
|
|
811
|
+
return options.parseJson(await response.text());
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return response[type]();
|
|
815
|
+
};
|
|
543
816
|
}
|
|
544
|
-
|
|
817
|
+
return result;
|
|
545
818
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
popupWidth = DEFAULT_WIDTH;
|
|
554
|
-
}
|
|
555
|
-
// Create dialog container
|
|
556
|
-
var container = document.createElement("div");
|
|
557
|
-
container.setAttribute("id", CONTAINER_ID);
|
|
558
|
-
container.setAttribute("aria-hidden", "true");
|
|
559
|
-
// Create dialog overlay
|
|
560
|
-
var overlay = document.createElement("div");
|
|
561
|
-
overlay.setAttribute("id", OVERLAY_ID);
|
|
562
|
-
overlay.setAttribute("data-a11y-dialog-hide", "true");
|
|
563
|
-
// Create dialog content
|
|
564
|
-
var content = document.createElement("div");
|
|
565
|
-
content.setAttribute("id", CONTENT_ID);
|
|
566
|
-
document.body.appendChild(container);
|
|
567
|
-
// Create CSS for dialog
|
|
568
|
-
var style = document.createElement("style");
|
|
569
|
-
style.setAttribute("id", STYLE_ID);
|
|
570
|
-
style.textContent = "\n #".concat(CONTAINER_ID, ",\n #").concat(OVERLAY_ID, " {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n }\n\n #").concat(CONTAINER_ID, " {\n z-index: 2147483647;\n display: flex;\n }\n\n #").concat(CONTAINER_ID, "[aria-hidden='true'] {\n display: none;\n }\n\n #").concat(OVERLAY_ID, " {\n background-color: rgba(0, 0, 0, 0.18);\n }\n\n #").concat(CONTENT_ID, " {\n margin: auto;\n z-index: 2147483647;\n position: relative;\n background-color: transparent;\n border-radius: 8px;\n width: ").concat(popupWidth, ";\n }\n\n #").concat(CONTENT_ID, " iframe {\n width: 1px;\n min-width: 100%;\n border-radius: inherit;\n max-height: 95vh;\n height: ").concat(INITIAL_HEIGHT, ";\n }\n ");
|
|
571
|
-
// Attach the created elements
|
|
572
|
-
document.head.insertAdjacentElement("beforeend", style);
|
|
573
|
-
container.appendChild(overlay);
|
|
574
|
-
container.appendChild(content);
|
|
575
|
-
this.popup = new A11yDialog(container);
|
|
576
|
-
// Make sure to remove any trace of the dialog on hide
|
|
577
|
-
this.popup.on("hide", function () {
|
|
578
|
-
_this.destroy();
|
|
819
|
+
// eslint-disable-next-line complexity
|
|
820
|
+
constructor(input, options = {}) {
|
|
821
|
+
Object.defineProperty(this, "request", {
|
|
822
|
+
enumerable: true,
|
|
823
|
+
configurable: true,
|
|
824
|
+
writable: true,
|
|
825
|
+
value: void 0
|
|
579
826
|
});
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
827
|
+
Object.defineProperty(this, "abortController", {
|
|
828
|
+
enumerable: true,
|
|
829
|
+
configurable: true,
|
|
830
|
+
writable: true,
|
|
831
|
+
value: void 0
|
|
832
|
+
});
|
|
833
|
+
Object.defineProperty(this, "_retryCount", {
|
|
834
|
+
enumerable: true,
|
|
835
|
+
configurable: true,
|
|
836
|
+
writable: true,
|
|
837
|
+
value: 0
|
|
838
|
+
});
|
|
839
|
+
Object.defineProperty(this, "_input", {
|
|
840
|
+
enumerable: true,
|
|
841
|
+
configurable: true,
|
|
842
|
+
writable: true,
|
|
843
|
+
value: void 0
|
|
844
|
+
});
|
|
845
|
+
Object.defineProperty(this, "_options", {
|
|
846
|
+
enumerable: true,
|
|
847
|
+
configurable: true,
|
|
848
|
+
writable: true,
|
|
849
|
+
value: void 0
|
|
850
|
+
});
|
|
851
|
+
this._input = input;
|
|
852
|
+
this._options = {
|
|
853
|
+
// TODO: credentials can be removed when the spec change is implemented in all browsers. Context: https://www.chromestatus.com/feature/4539473312350208
|
|
854
|
+
credentials: this._input.credentials || 'same-origin',
|
|
855
|
+
...options,
|
|
856
|
+
headers: mergeHeaders(this._input.headers, options.headers),
|
|
857
|
+
hooks: deepMerge({
|
|
858
|
+
beforeRequest: [],
|
|
859
|
+
beforeRetry: [],
|
|
860
|
+
beforeError: [],
|
|
861
|
+
afterResponse: [],
|
|
862
|
+
}, options.hooks),
|
|
863
|
+
method: normalizeRequestMethod(options.method ?? this._input.method),
|
|
864
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
865
|
+
prefixUrl: String(options.prefixUrl || ''),
|
|
866
|
+
retry: normalizeRetryOptions(options.retry),
|
|
867
|
+
throwHttpErrors: options.throwHttpErrors !== false,
|
|
868
|
+
timeout: typeof options.timeout === 'undefined' ? 10000 : options.timeout,
|
|
869
|
+
fetch: options.fetch ?? globalThis.fetch.bind(globalThis),
|
|
870
|
+
};
|
|
871
|
+
if (typeof this._input !== 'string' && !(this._input instanceof URL || this._input instanceof globalThis.Request)) {
|
|
872
|
+
throw new TypeError('`input` must be a string, URL, or Request');
|
|
587
873
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
874
|
+
if (this._options.prefixUrl && typeof this._input === 'string') {
|
|
875
|
+
if (this._input.startsWith('/')) {
|
|
876
|
+
throw new Error('`input` must not begin with a slash when using `prefixUrl`');
|
|
877
|
+
}
|
|
878
|
+
if (!this._options.prefixUrl.endsWith('/')) {
|
|
879
|
+
this._options.prefixUrl += '/';
|
|
880
|
+
}
|
|
881
|
+
this._input = this._options.prefixUrl + this._input;
|
|
595
882
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
dialogContent.appendChild(iframe);
|
|
883
|
+
if (supportsAbortController) {
|
|
884
|
+
this.abortController = new globalThis.AbortController();
|
|
885
|
+
if (this._options.signal) {
|
|
886
|
+
const originalSignal = this._options.signal;
|
|
887
|
+
this._options.signal.addEventListener('abort', () => {
|
|
888
|
+
this.abortController.abort(originalSignal.reason);
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
this._options.signal = this.abortController.signal;
|
|
606
892
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
PopupHandler.prototype.close = function () {
|
|
611
|
-
if (!this.popup) {
|
|
612
|
-
throw new Error("Popup is not initialized");
|
|
893
|
+
if (supportsRequestStreams) {
|
|
894
|
+
// @ts-expect-error - Types are outdated.
|
|
895
|
+
this._options.duplex = 'half';
|
|
613
896
|
}
|
|
614
|
-
this.
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
897
|
+
this.request = new globalThis.Request(this._input, this._options);
|
|
898
|
+
if (this._options.searchParams) {
|
|
899
|
+
// eslint-disable-next-line unicorn/prevent-abbreviations
|
|
900
|
+
const textSearchParams = typeof this._options.searchParams === 'string'
|
|
901
|
+
? this._options.searchParams.replace(/^\?/, '')
|
|
902
|
+
: new URLSearchParams(this._options.searchParams).toString();
|
|
903
|
+
// eslint-disable-next-line unicorn/prevent-abbreviations
|
|
904
|
+
const searchParams = '?' + textSearchParams;
|
|
905
|
+
const url = this.request.url.replace(/(?:\?.*?)?(?=#|$)/, searchParams);
|
|
906
|
+
// To provide correct form boundary, Content-Type header should be deleted each time when new Request instantiated from another one
|
|
907
|
+
if (((supportsFormData && this._options.body instanceof globalThis.FormData)
|
|
908
|
+
|| this._options.body instanceof URLSearchParams) && !(this._options.headers && this._options.headers['content-type'])) {
|
|
909
|
+
this.request.headers.delete('content-type');
|
|
910
|
+
}
|
|
911
|
+
// The spread of `this.request` is required as otherwise it misses the `duplex` option for some reason and throws.
|
|
912
|
+
this.request = new globalThis.Request(new globalThis.Request(url, { ...this.request }), this._options);
|
|
913
|
+
}
|
|
914
|
+
if (this._options.json !== undefined) {
|
|
915
|
+
this._options.body = JSON.stringify(this._options.json);
|
|
916
|
+
this.request.headers.set('content-type', this._options.headers.get('content-type') ?? 'application/json');
|
|
917
|
+
this.request = new globalThis.Request(this.request, { body: this._options.body });
|
|
619
918
|
}
|
|
620
|
-
this.popup.on(event, handler);
|
|
621
|
-
};
|
|
622
|
-
return PopupHandler;
|
|
623
|
-
}());
|
|
624
|
-
function resizeIframe(event) {
|
|
625
|
-
var iframeEl = document.querySelector("#".concat(IFRAME_ID));
|
|
626
|
-
if (iframeEl && event.data.height) {
|
|
627
|
-
iframeEl.style.height = event.data.height + "px";
|
|
628
919
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
659
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
660
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
661
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
662
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
663
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
664
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function __generator(thisArg, body) {
|
|
669
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
670
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
671
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
672
|
-
function step(op) {
|
|
673
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
674
|
-
while (_) try {
|
|
675
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
676
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
677
|
-
switch (op[0]) {
|
|
678
|
-
case 0: case 1: t = op; break;
|
|
679
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
680
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
681
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
682
|
-
default:
|
|
683
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
684
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
685
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
686
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
687
|
-
if (t[2]) _.ops.pop();
|
|
688
|
-
_.trys.pop(); continue;
|
|
689
|
-
}
|
|
690
|
-
op = body.call(thisArg, _);
|
|
691
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
692
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
/* [@simplewebauthn/browser@8.2.1] */
|
|
697
|
-
function utf8StringToBuffer(value) {
|
|
698
|
-
return new TextEncoder().encode(value);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
function bufferToBase64URLString(buffer) {
|
|
702
|
-
const bytes = new Uint8Array(buffer);
|
|
703
|
-
let str = '';
|
|
704
|
-
for (const charCode of bytes) {
|
|
705
|
-
str += String.fromCharCode(charCode);
|
|
706
|
-
}
|
|
707
|
-
const base64String = btoa(str);
|
|
708
|
-
return base64String.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
function base64URLStringToBuffer(base64URLString) {
|
|
712
|
-
const base64 = base64URLString.replace(/-/g, '+').replace(/_/g, '/');
|
|
713
|
-
const padLength = (4 - (base64.length % 4)) % 4;
|
|
714
|
-
const padded = base64.padEnd(base64.length + padLength, '=');
|
|
715
|
-
const binary = atob(padded);
|
|
716
|
-
const buffer = new ArrayBuffer(binary.length);
|
|
717
|
-
const bytes = new Uint8Array(buffer);
|
|
718
|
-
for (let i = 0; i < binary.length; i++) {
|
|
719
|
-
bytes[i] = binary.charCodeAt(i);
|
|
720
|
-
}
|
|
721
|
-
return buffer;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
function browserSupportsWebAuthn() {
|
|
725
|
-
return (window?.PublicKeyCredential !== undefined &&
|
|
726
|
-
typeof window.PublicKeyCredential === 'function');
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
function toPublicKeyCredentialDescriptor(descriptor) {
|
|
730
|
-
const { id } = descriptor;
|
|
731
|
-
return {
|
|
732
|
-
...descriptor,
|
|
733
|
-
id: base64URLStringToBuffer(id),
|
|
734
|
-
transports: descriptor.transports,
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
function isValidDomain(hostname) {
|
|
739
|
-
return (hostname === 'localhost' ||
|
|
740
|
-
/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname));
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
class WebAuthnError extends Error {
|
|
744
|
-
constructor({ message, code, cause, name, }) {
|
|
745
|
-
super(message, { cause });
|
|
746
|
-
this.name = name ?? cause.name;
|
|
747
|
-
this.code = code;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
function identifyRegistrationError({ error, options, }) {
|
|
752
|
-
const { publicKey } = options;
|
|
753
|
-
if (!publicKey) {
|
|
754
|
-
throw Error('options was missing required publicKey property');
|
|
755
|
-
}
|
|
756
|
-
if (error.name === 'AbortError') {
|
|
757
|
-
if (options.signal instanceof AbortSignal) {
|
|
758
|
-
return new WebAuthnError({
|
|
759
|
-
message: 'Registration ceremony was sent an abort signal',
|
|
760
|
-
code: 'ERROR_CEREMONY_ABORTED',
|
|
761
|
-
cause: error,
|
|
762
|
-
});
|
|
920
|
+
_calculateRetryDelay(error) {
|
|
921
|
+
this._retryCount++;
|
|
922
|
+
if (this._retryCount < this._options.retry.limit && !(error instanceof TimeoutError)) {
|
|
923
|
+
if (error instanceof HTTPError) {
|
|
924
|
+
if (!this._options.retry.statusCodes.includes(error.response.status)) {
|
|
925
|
+
return 0;
|
|
926
|
+
}
|
|
927
|
+
const retryAfter = error.response.headers.get('Retry-After');
|
|
928
|
+
if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) {
|
|
929
|
+
let after = Number(retryAfter);
|
|
930
|
+
if (Number.isNaN(after)) {
|
|
931
|
+
after = Date.parse(retryAfter) - Date.now();
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
after *= 1000;
|
|
935
|
+
}
|
|
936
|
+
if (typeof this._options.retry.maxRetryAfter !== 'undefined' && after > this._options.retry.maxRetryAfter) {
|
|
937
|
+
return 0;
|
|
938
|
+
}
|
|
939
|
+
return after;
|
|
940
|
+
}
|
|
941
|
+
if (error.response.status === 413) {
|
|
942
|
+
return 0;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
const BACKOFF_FACTOR = 0.3;
|
|
946
|
+
return Math.min(this._options.retry.backoffLimit, BACKOFF_FACTOR * (2 ** (this._retryCount - 1)) * 1000);
|
|
763
947
|
}
|
|
948
|
+
return 0;
|
|
764
949
|
}
|
|
765
|
-
|
|
766
|
-
if (
|
|
767
|
-
|
|
768
|
-
message: 'Discoverable credentials were required but no available authenticator supported it',
|
|
769
|
-
code: 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT',
|
|
770
|
-
cause: error,
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
else if (publicKey.authenticatorSelection?.userVerification === 'required') {
|
|
774
|
-
return new WebAuthnError({
|
|
775
|
-
message: 'User verification was required but no available authenticator supported it',
|
|
776
|
-
code: 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT',
|
|
777
|
-
cause: error,
|
|
778
|
-
});
|
|
950
|
+
_decorateResponse(response) {
|
|
951
|
+
if (this._options.parseJson) {
|
|
952
|
+
response.json = async () => this._options.parseJson(await response.text());
|
|
779
953
|
}
|
|
954
|
+
return response;
|
|
780
955
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
956
|
+
async _retry(fn) {
|
|
957
|
+
try {
|
|
958
|
+
return await fn();
|
|
959
|
+
// eslint-disable-next-line @typescript-eslint/no-implicit-any-catch
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout);
|
|
963
|
+
if (ms !== 0 && this._retryCount > 0) {
|
|
964
|
+
await delay(ms, { signal: this._options.signal });
|
|
965
|
+
for (const hook of this._options.hooks.beforeRetry) {
|
|
966
|
+
// eslint-disable-next-line no-await-in-loop
|
|
967
|
+
const hookResult = await hook({
|
|
968
|
+
request: this.request,
|
|
969
|
+
options: this._options,
|
|
970
|
+
error: error,
|
|
971
|
+
retryCount: this._retryCount,
|
|
972
|
+
});
|
|
973
|
+
// If `stop` is returned from the hook, the retry process is stopped
|
|
974
|
+
if (hookResult === stop) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return this._retry(fn);
|
|
979
|
+
}
|
|
980
|
+
throw error;
|
|
803
981
|
}
|
|
804
|
-
return new WebAuthnError({
|
|
805
|
-
message: 'No available authenticator supported any of the specified pubKeyCredParams algorithms',
|
|
806
|
-
code: 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG',
|
|
807
|
-
cause: error,
|
|
808
|
-
});
|
|
809
982
|
}
|
|
810
|
-
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
983
|
+
async _fetch() {
|
|
984
|
+
for (const hook of this._options.hooks.beforeRequest) {
|
|
985
|
+
// eslint-disable-next-line no-await-in-loop
|
|
986
|
+
const result = await hook(this.request, this._options);
|
|
987
|
+
if (result instanceof Request) {
|
|
988
|
+
this.request = result;
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
if (result instanceof Response) {
|
|
992
|
+
return result;
|
|
993
|
+
}
|
|
818
994
|
}
|
|
819
|
-
|
|
820
|
-
return
|
|
821
|
-
message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
|
|
822
|
-
code: 'ERROR_INVALID_RP_ID',
|
|
823
|
-
cause: error,
|
|
824
|
-
});
|
|
995
|
+
if (this._options.timeout === false) {
|
|
996
|
+
return this._options.fetch(this.request.clone());
|
|
825
997
|
}
|
|
998
|
+
return timeout(this.request.clone(), this.abortController, this._options);
|
|
826
999
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1000
|
+
/* istanbul ignore next */
|
|
1001
|
+
_stream(response, onDownloadProgress) {
|
|
1002
|
+
const totalBytes = Number(response.headers.get('content-length')) || 0;
|
|
1003
|
+
let transferredBytes = 0;
|
|
1004
|
+
if (response.status === 204) {
|
|
1005
|
+
if (onDownloadProgress) {
|
|
1006
|
+
onDownloadProgress({ percent: 1, totalBytes, transferredBytes }, new Uint8Array());
|
|
1007
|
+
}
|
|
1008
|
+
return new globalThis.Response(null, {
|
|
1009
|
+
status: response.status,
|
|
1010
|
+
statusText: response.statusText,
|
|
1011
|
+
headers: response.headers,
|
|
833
1012
|
});
|
|
834
1013
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1014
|
+
return new globalThis.Response(new globalThis.ReadableStream({
|
|
1015
|
+
async start(controller) {
|
|
1016
|
+
const reader = response.body.getReader();
|
|
1017
|
+
if (onDownloadProgress) {
|
|
1018
|
+
onDownloadProgress({ percent: 0, transferredBytes: 0, totalBytes }, new Uint8Array());
|
|
1019
|
+
}
|
|
1020
|
+
async function read() {
|
|
1021
|
+
const { done, value } = await reader.read();
|
|
1022
|
+
if (done) {
|
|
1023
|
+
controller.close();
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
if (onDownloadProgress) {
|
|
1027
|
+
transferredBytes += value.byteLength;
|
|
1028
|
+
const percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
|
|
1029
|
+
onDownloadProgress({ percent, transferredBytes, totalBytes }, value);
|
|
1030
|
+
}
|
|
1031
|
+
controller.enqueue(value);
|
|
1032
|
+
await read();
|
|
1033
|
+
}
|
|
1034
|
+
await read();
|
|
1035
|
+
},
|
|
1036
|
+
}), {
|
|
1037
|
+
status: response.status,
|
|
1038
|
+
statusText: response.statusText,
|
|
1039
|
+
headers: response.headers,
|
|
841
1040
|
});
|
|
842
1041
|
}
|
|
843
|
-
return error;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
class WebAuthnAbortService {
|
|
847
|
-
createNewAbortSignal() {
|
|
848
|
-
if (this.controller) {
|
|
849
|
-
const abortError = new Error('Cancelling existing WebAuthn API call for new one');
|
|
850
|
-
abortError.name = 'AbortError';
|
|
851
|
-
this.controller.abort(abortError);
|
|
852
|
-
}
|
|
853
|
-
const newController = new AbortController();
|
|
854
|
-
this.controller = newController;
|
|
855
|
-
return newController.signal;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
const webauthnAbortService = new WebAuthnAbortService();
|
|
859
|
-
|
|
860
|
-
const attachments = ['cross-platform', 'platform'];
|
|
861
|
-
function toAuthenticatorAttachment(attachment) {
|
|
862
|
-
if (!attachment) {
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
if (attachments.indexOf(attachment) < 0) {
|
|
866
|
-
return;
|
|
867
|
-
}
|
|
868
|
-
return attachment;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
async function startRegistration(creationOptionsJSON) {
|
|
872
|
-
if (!browserSupportsWebAuthn()) {
|
|
873
|
-
throw new Error('WebAuthn is not supported in this browser');
|
|
874
|
-
}
|
|
875
|
-
const publicKey = {
|
|
876
|
-
...creationOptionsJSON,
|
|
877
|
-
challenge: base64URLStringToBuffer(creationOptionsJSON.challenge),
|
|
878
|
-
user: {
|
|
879
|
-
...creationOptionsJSON.user,
|
|
880
|
-
id: utf8StringToBuffer(creationOptionsJSON.user.id),
|
|
881
|
-
},
|
|
882
|
-
excludeCredentials: creationOptionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor),
|
|
883
|
-
};
|
|
884
|
-
const options = { publicKey };
|
|
885
|
-
options.signal = webauthnAbortService.createNewAbortSignal();
|
|
886
|
-
let credential;
|
|
887
|
-
try {
|
|
888
|
-
credential = (await navigator.credentials.create(options));
|
|
889
|
-
}
|
|
890
|
-
catch (err) {
|
|
891
|
-
throw identifyRegistrationError({ error: err, options });
|
|
892
|
-
}
|
|
893
|
-
if (!credential) {
|
|
894
|
-
throw new Error('Registration was not completed');
|
|
895
|
-
}
|
|
896
|
-
const { id, rawId, response, type } = credential;
|
|
897
|
-
let transports = undefined;
|
|
898
|
-
if (typeof response.getTransports === 'function') {
|
|
899
|
-
transports = response.getTransports();
|
|
900
|
-
}
|
|
901
|
-
let responsePublicKeyAlgorithm = undefined;
|
|
902
|
-
if (typeof response.getPublicKeyAlgorithm === 'function') {
|
|
903
|
-
try {
|
|
904
|
-
responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
|
|
905
|
-
}
|
|
906
|
-
catch (error) {
|
|
907
|
-
warnOnBrokenImplementation('getPublicKeyAlgorithm()', error);
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
let responsePublicKey = undefined;
|
|
911
|
-
if (typeof response.getPublicKey === 'function') {
|
|
912
|
-
try {
|
|
913
|
-
const _publicKey = response.getPublicKey();
|
|
914
|
-
if (_publicKey !== null) {
|
|
915
|
-
responsePublicKey = bufferToBase64URLString(_publicKey);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
catch (error) {
|
|
919
|
-
warnOnBrokenImplementation('getPublicKey()', error);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
let responseAuthenticatorData;
|
|
923
|
-
if (typeof response.getAuthenticatorData === 'function') {
|
|
924
|
-
try {
|
|
925
|
-
responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
|
|
926
|
-
}
|
|
927
|
-
catch (error) {
|
|
928
|
-
warnOnBrokenImplementation('getAuthenticatorData()', error);
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
return {
|
|
932
|
-
id,
|
|
933
|
-
rawId: bufferToBase64URLString(rawId),
|
|
934
|
-
response: {
|
|
935
|
-
attestationObject: bufferToBase64URLString(response.attestationObject),
|
|
936
|
-
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
937
|
-
transports,
|
|
938
|
-
publicKeyAlgorithm: responsePublicKeyAlgorithm,
|
|
939
|
-
publicKey: responsePublicKey,
|
|
940
|
-
authenticatorData: responseAuthenticatorData,
|
|
941
|
-
},
|
|
942
|
-
type,
|
|
943
|
-
clientExtensionResults: credential.getClientExtensionResults(),
|
|
944
|
-
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment),
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
function warnOnBrokenImplementation(methodName, cause) {
|
|
948
|
-
console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.\n`, cause);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
function bufferToUTF8String(value) {
|
|
952
|
-
return new TextDecoder('utf-8').decode(value);
|
|
953
1042
|
}
|
|
954
1043
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1044
|
+
/*! MIT License © Sindre Sorhus */
|
|
1045
|
+
const createInstance = (defaults) => {
|
|
1046
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
1047
|
+
const ky = (input, options) => Ky.create(input, validateAndMerge(defaults, options));
|
|
1048
|
+
for (const method of requestMethods) {
|
|
1049
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
1050
|
+
ky[method] = (input, options) => Ky.create(input, validateAndMerge(defaults, options, { method }));
|
|
960
1051
|
}
|
|
961
|
-
|
|
962
|
-
|
|
1052
|
+
ky.create = (newDefaults) => createInstance(validateAndMerge(newDefaults));
|
|
1053
|
+
ky.extend = (newDefaults) => createInstance(validateAndMerge(defaults, newDefaults));
|
|
1054
|
+
ky.stop = stop;
|
|
1055
|
+
return ky;
|
|
1056
|
+
};
|
|
1057
|
+
const ky = createInstance();
|
|
1058
|
+
var ky$1 = ky;
|
|
963
1059
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
if (options.signal instanceof AbortSignal) {
|
|
971
|
-
return new WebAuthnError({
|
|
972
|
-
message: 'Authentication ceremony was sent an abort signal',
|
|
973
|
-
code: 'ERROR_CEREMONY_ABORTED',
|
|
974
|
-
cause: error,
|
|
975
|
-
});
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
else if (error.name === 'NotAllowedError') {
|
|
979
|
-
return new WebAuthnError({
|
|
980
|
-
message: error.message,
|
|
981
|
-
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
|
|
982
|
-
cause: error,
|
|
1060
|
+
var PasskeyApiClient = /** @class */ (function () {
|
|
1061
|
+
function PasskeyApiClient(_a) {
|
|
1062
|
+
var baseUrl = _a.baseUrl, tenantId = _a.tenantId;
|
|
1063
|
+
this.tenantId = tenantId;
|
|
1064
|
+
this.api = ky$1.create({
|
|
1065
|
+
prefixUrl: baseUrl
|
|
983
1066
|
});
|
|
984
1067
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1068
|
+
PasskeyApiClient.prototype.registrationOptions = function (_a) {
|
|
1069
|
+
var token = _a.token, userName = _a.userName;
|
|
1070
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1071
|
+
var response;
|
|
1072
|
+
return __generator(this, function (_b) {
|
|
1073
|
+
switch (_b.label) {
|
|
1074
|
+
case 0: return [4 /*yield*/, this.api.post("client/user-authenticators/passkey/registration-options", {
|
|
1075
|
+
json: { username: userName },
|
|
1076
|
+
headers: this.buildHeaders(token)
|
|
1077
|
+
})];
|
|
1078
|
+
case 1:
|
|
1079
|
+
response = _b.sent();
|
|
1080
|
+
return [2 /*return*/, response.json()];
|
|
1081
|
+
}
|
|
999
1082
|
});
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
else if (error.name === 'UnknownError') {
|
|
1003
|
-
return new WebAuthnError({
|
|
1004
|
-
message: 'The authenticator was unable to process the specified options, or could not create a new assertion signature',
|
|
1005
|
-
code: 'ERROR_AUTHENTICATOR_GENERAL_ERROR',
|
|
1006
|
-
cause: error,
|
|
1007
1083
|
});
|
|
1008
|
-
}
|
|
1009
|
-
return error;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
async function startAuthentication(requestOptionsJSON, useBrowserAutofill = false) {
|
|
1013
|
-
if (!browserSupportsWebAuthn()) {
|
|
1014
|
-
throw new Error('WebAuthn is not supported in this browser');
|
|
1015
|
-
}
|
|
1016
|
-
let allowCredentials;
|
|
1017
|
-
if (requestOptionsJSON.allowCredentials?.length !== 0) {
|
|
1018
|
-
allowCredentials = requestOptionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
|
|
1019
|
-
}
|
|
1020
|
-
const publicKey = {
|
|
1021
|
-
...requestOptionsJSON,
|
|
1022
|
-
challenge: base64URLStringToBuffer(requestOptionsJSON.challenge),
|
|
1023
|
-
allowCredentials,
|
|
1024
1084
|
};
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
try {
|
|
1041
|
-
credential = (await navigator.credentials.get(options));
|
|
1042
|
-
}
|
|
1043
|
-
catch (err) {
|
|
1044
|
-
throw identifyAuthenticationError({ error: err, options });
|
|
1045
|
-
}
|
|
1046
|
-
if (!credential) {
|
|
1047
|
-
throw new Error('Authentication was not completed');
|
|
1048
|
-
}
|
|
1049
|
-
const { id, rawId, response, type } = credential;
|
|
1050
|
-
let userHandle = undefined;
|
|
1051
|
-
if (response.userHandle) {
|
|
1052
|
-
userHandle = bufferToUTF8String(response.userHandle);
|
|
1053
|
-
}
|
|
1054
|
-
return {
|
|
1055
|
-
id,
|
|
1056
|
-
rawId: bufferToBase64URLString(rawId),
|
|
1057
|
-
response: {
|
|
1058
|
-
authenticatorData: bufferToBase64URLString(response.authenticatorData),
|
|
1059
|
-
clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
|
|
1060
|
-
signature: bufferToBase64URLString(response.signature),
|
|
1061
|
-
userHandle,
|
|
1062
|
-
},
|
|
1063
|
-
type,
|
|
1064
|
-
clientExtensionResults: credential.getClientExtensionResults(),
|
|
1065
|
-
authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment),
|
|
1066
|
-
};
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
// eslint-lint-disable-next-line @typescript-eslint/naming-convention
|
|
1070
|
-
class HTTPError extends Error {
|
|
1071
|
-
constructor(response, request, options) {
|
|
1072
|
-
const code = (response.status || response.status === 0) ? response.status : '';
|
|
1073
|
-
const title = response.statusText || '';
|
|
1074
|
-
const status = `${code} ${title}`.trim();
|
|
1075
|
-
const reason = status ? `status code ${status}` : 'an unknown error';
|
|
1076
|
-
super(`Request failed with ${reason}`);
|
|
1077
|
-
Object.defineProperty(this, "response", {
|
|
1078
|
-
enumerable: true,
|
|
1079
|
-
configurable: true,
|
|
1080
|
-
writable: true,
|
|
1081
|
-
value: void 0
|
|
1085
|
+
PasskeyApiClient.prototype.authenticationOptions = function (_a) {
|
|
1086
|
+
var token = _a.token;
|
|
1087
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1088
|
+
var response;
|
|
1089
|
+
return __generator(this, function (_b) {
|
|
1090
|
+
switch (_b.label) {
|
|
1091
|
+
case 0: return [4 /*yield*/, this.api.post("client/user-authenticators/passkey/authentication-options", {
|
|
1092
|
+
json: {},
|
|
1093
|
+
headers: this.buildHeaders(token)
|
|
1094
|
+
})];
|
|
1095
|
+
case 1:
|
|
1096
|
+
response = _b.sent();
|
|
1097
|
+
return [2 /*return*/, response.json()];
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1082
1100
|
});
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1101
|
+
};
|
|
1102
|
+
PasskeyApiClient.prototype.addAuthenticator = function (_a) {
|
|
1103
|
+
var token = _a.token, rest = __rest(_a, ["token"]);
|
|
1104
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1105
|
+
var response;
|
|
1106
|
+
return __generator(this, function (_b) {
|
|
1107
|
+
switch (_b.label) {
|
|
1108
|
+
case 0: return [4 /*yield*/, this.api.post("client/user-authenticators/passkey", {
|
|
1109
|
+
json: rest,
|
|
1110
|
+
headers: this.buildHeaders(token)
|
|
1111
|
+
})];
|
|
1112
|
+
case 1:
|
|
1113
|
+
response = _b.sent();
|
|
1114
|
+
return [2 /*return*/, response.json()];
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1088
1117
|
});
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1118
|
+
};
|
|
1119
|
+
PasskeyApiClient.prototype.verify = function (_a) {
|
|
1120
|
+
var token = _a.token, rest = __rest(_a, ["token"]);
|
|
1121
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1122
|
+
var response;
|
|
1123
|
+
return __generator(this, function (_b) {
|
|
1124
|
+
switch (_b.label) {
|
|
1125
|
+
case 0: return [4 /*yield*/, this.api.post("client/verify/passkey", {
|
|
1126
|
+
json: rest,
|
|
1127
|
+
headers: this.buildHeaders(token)
|
|
1128
|
+
})];
|
|
1129
|
+
case 1:
|
|
1130
|
+
response = _b.sent();
|
|
1131
|
+
return [2 /*return*/, response.json()];
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1094
1134
|
});
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
}
|
|
1135
|
+
};
|
|
1136
|
+
PasskeyApiClient.prototype.buildHeaders = function (token) {
|
|
1137
|
+
var authorizationHeader = token ? "Bearer ".concat(token) : "Basic ".concat(window.btoa(encodeURIComponent(this.tenantId)));
|
|
1138
|
+
return {
|
|
1139
|
+
Authorization: authorizationHeader
|
|
1140
|
+
};
|
|
1141
|
+
};
|
|
1142
|
+
return PasskeyApiClient;
|
|
1143
|
+
}());
|
|
1101
1144
|
|
|
1102
|
-
class
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
enumerable: true,
|
|
1107
|
-
configurable: true,
|
|
1108
|
-
writable: true,
|
|
1109
|
-
value: void 0
|
|
1110
|
-
});
|
|
1111
|
-
this.name = 'TimeoutError';
|
|
1112
|
-
this.request = request;
|
|
1145
|
+
var Passkey = /** @class */ (function () {
|
|
1146
|
+
function Passkey(_a) {
|
|
1147
|
+
var baseUrl = _a.baseUrl, tenantId = _a.tenantId;
|
|
1148
|
+
this.api = new PasskeyApiClient({ baseUrl: baseUrl, tenantId: tenantId });
|
|
1113
1149
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1150
|
+
Passkey.prototype.signUp = function (_a) {
|
|
1151
|
+
var userName = _a.userName, token = _a.token;
|
|
1152
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1153
|
+
var optionsResponse, registrationResponse, addAuthenticatorResponse;
|
|
1154
|
+
return __generator(this, function (_b) {
|
|
1155
|
+
switch (_b.label) {
|
|
1156
|
+
case 0: return [4 /*yield*/, this.api.registrationOptions({ userName: userName, token: token })];
|
|
1157
|
+
case 1:
|
|
1158
|
+
optionsResponse = _b.sent();
|
|
1159
|
+
return [4 /*yield*/, startRegistration(optionsResponse.options)];
|
|
1160
|
+
case 2:
|
|
1161
|
+
registrationResponse = _b.sent();
|
|
1162
|
+
return [4 /*yield*/, this.api.addAuthenticator({
|
|
1163
|
+
challengeId: optionsResponse.challengeId,
|
|
1164
|
+
registrationCredential: registrationResponse,
|
|
1165
|
+
token: token
|
|
1166
|
+
})];
|
|
1167
|
+
case 3:
|
|
1168
|
+
addAuthenticatorResponse = _b.sent();
|
|
1169
|
+
return [2 /*return*/, addAuthenticatorResponse === null || addAuthenticatorResponse === void 0 ? void 0 : addAuthenticatorResponse.accessToken];
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
});
|
|
1173
|
+
};
|
|
1174
|
+
Passkey.prototype.signIn = function (params) {
|
|
1175
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1176
|
+
var optionsResponse, authenticationResponse, verifyResponse;
|
|
1177
|
+
return __generator(this, function (_a) {
|
|
1178
|
+
switch (_a.label) {
|
|
1179
|
+
case 0:
|
|
1180
|
+
if ((params === null || params === void 0 ? void 0 : params.token) && params.autofill) {
|
|
1181
|
+
throw new Error("Autofill is not supported when providing a token");
|
|
1182
|
+
}
|
|
1183
|
+
return [4 /*yield*/, this.api.authenticationOptions({ token: params === null || params === void 0 ? void 0 : params.token })];
|
|
1184
|
+
case 1:
|
|
1185
|
+
optionsResponse = _a.sent();
|
|
1186
|
+
return [4 /*yield*/, startAuthentication(optionsResponse.options, params === null || params === void 0 ? void 0 : params.autofill)];
|
|
1187
|
+
case 2:
|
|
1188
|
+
authenticationResponse = _a.sent();
|
|
1189
|
+
return [4 /*yield*/, this.api.verify({
|
|
1190
|
+
challengeId: optionsResponse.challengeId,
|
|
1191
|
+
authenticationCredential: authenticationResponse,
|
|
1192
|
+
token: params === null || params === void 0 ? void 0 : params.token
|
|
1193
|
+
})];
|
|
1194
|
+
case 3:
|
|
1195
|
+
verifyResponse = _a.sent();
|
|
1196
|
+
return [2 /*return*/, verifyResponse === null || verifyResponse === void 0 ? void 0 : verifyResponse.accessToken];
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
});
|
|
1200
|
+
};
|
|
1201
|
+
return Passkey;
|
|
1202
|
+
}());
|
|
1118
1203
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1204
|
+
var DEFAULT_WIDTH$1 = 400;
|
|
1205
|
+
var DEFAULT_HEIGHT = 500;
|
|
1206
|
+
var WindowHandler = /** @class */ (function () {
|
|
1207
|
+
function WindowHandler() {
|
|
1208
|
+
this.windowRef = null;
|
|
1124
1209
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
const source = new globalThis.Headers(source2);
|
|
1131
|
-
for (const [key, value] of source.entries()) {
|
|
1132
|
-
if ((isHeadersInstance && value === 'undefined') || value === undefined) {
|
|
1133
|
-
result.delete(key);
|
|
1210
|
+
WindowHandler.prototype.show = function (_a) {
|
|
1211
|
+
var url = _a.url, _b = _a.width, width = _b === void 0 ? DEFAULT_WIDTH$1 : _b, _c = _a.height, height = _c === void 0 ? DEFAULT_HEIGHT : _c;
|
|
1212
|
+
var windowRef = openWindow({ url: url, width: width, height: height, win: window });
|
|
1213
|
+
if (!windowRef) {
|
|
1214
|
+
throw new Error("Window is not initialized");
|
|
1134
1215
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1216
|
+
this.windowRef = windowRef;
|
|
1217
|
+
return windowRef;
|
|
1218
|
+
};
|
|
1219
|
+
WindowHandler.prototype.close = function () {
|
|
1220
|
+
if (!this.windowRef) {
|
|
1221
|
+
throw new Error("Window is not initialized");
|
|
1137
1222
|
}
|
|
1223
|
+
this.windowRef.close();
|
|
1224
|
+
};
|
|
1225
|
+
return WindowHandler;
|
|
1226
|
+
}());
|
|
1227
|
+
function openWindow(_a) {
|
|
1228
|
+
var url = _a.url, width = _a.width, height = _a.height, win = _a.win;
|
|
1229
|
+
if (!win.top) {
|
|
1230
|
+
return null;
|
|
1138
1231
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
let returnValue = {};
|
|
1144
|
-
let headers = {};
|
|
1145
|
-
for (const source of sources) {
|
|
1146
|
-
if (Array.isArray(source)) {
|
|
1147
|
-
if (!Array.isArray(returnValue)) {
|
|
1148
|
-
returnValue = [];
|
|
1149
|
-
}
|
|
1150
|
-
returnValue = [...returnValue, ...source];
|
|
1151
|
-
}
|
|
1152
|
-
else if (isObject(source)) {
|
|
1153
|
-
for (let [key, value] of Object.entries(source)) {
|
|
1154
|
-
if (isObject(value) && key in returnValue) {
|
|
1155
|
-
value = deepMerge(returnValue[key], value);
|
|
1156
|
-
}
|
|
1157
|
-
returnValue = { ...returnValue, [key]: value };
|
|
1158
|
-
}
|
|
1159
|
-
if (isObject(source.headers)) {
|
|
1160
|
-
headers = mergeHeaders(headers, source.headers);
|
|
1161
|
-
returnValue.headers = headers;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
return returnValue;
|
|
1166
|
-
};
|
|
1232
|
+
var y = win.top.outerHeight / 2 + win.top.screenY - height / 2;
|
|
1233
|
+
var x = win.top.outerWidth / 2 + win.top.screenX - width / 2;
|
|
1234
|
+
return window.open(url, "", "toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=".concat(width, ", height=").concat(height, ", top=").concat(y, ", left=").concat(x));
|
|
1235
|
+
}
|
|
1167
1236
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
}).headers.has('Content-Type');
|
|
1183
|
-
}
|
|
1184
|
-
return duplexAccessed && !hasContentType;
|
|
1185
|
-
})();
|
|
1186
|
-
const supportsAbortController = typeof globalThis.AbortController === 'function';
|
|
1187
|
-
const supportsResponseStreams = typeof globalThis.ReadableStream === 'function';
|
|
1188
|
-
const supportsFormData = typeof globalThis.FormData === 'function';
|
|
1189
|
-
const requestMethods = ['get', 'post', 'put', 'patch', 'head', 'delete'];
|
|
1190
|
-
const responseTypes = {
|
|
1191
|
-
json: 'application/json',
|
|
1192
|
-
text: 'text/*',
|
|
1193
|
-
formData: 'multipart/form-data',
|
|
1194
|
-
arrayBuffer: '*/*',
|
|
1195
|
-
blob: '*/*',
|
|
1196
|
-
};
|
|
1197
|
-
// The maximum value of a 32bit int (see issue #117)
|
|
1198
|
-
const maxSafeTimeout = 2147483647;
|
|
1199
|
-
const stop = Symbol('stop');
|
|
1237
|
+
var focusableSelectors = [
|
|
1238
|
+
'a[href]:not([tabindex^="-"])',
|
|
1239
|
+
'area[href]:not([tabindex^="-"])',
|
|
1240
|
+
'input:not([type="hidden"]):not([type="radio"]):not([disabled]):not([tabindex^="-"])',
|
|
1241
|
+
'input[type="radio"]:not([disabled]):not([tabindex^="-"])',
|
|
1242
|
+
'select:not([disabled]):not([tabindex^="-"])',
|
|
1243
|
+
'textarea:not([disabled]):not([tabindex^="-"])',
|
|
1244
|
+
'button:not([disabled]):not([tabindex^="-"])',
|
|
1245
|
+
'iframe:not([tabindex^="-"])',
|
|
1246
|
+
'audio[controls]:not([tabindex^="-"])',
|
|
1247
|
+
'video[controls]:not([tabindex^="-"])',
|
|
1248
|
+
'[contenteditable]:not([tabindex^="-"])',
|
|
1249
|
+
'[tabindex]:not([tabindex^="-"])',
|
|
1250
|
+
];
|
|
1200
1251
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
const retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
|
|
1204
|
-
const retryAfterStatusCodes = [413, 429, 503];
|
|
1205
|
-
const defaultRetryOptions = {
|
|
1206
|
-
limit: 2,
|
|
1207
|
-
methods: retryMethods,
|
|
1208
|
-
statusCodes: retryStatusCodes,
|
|
1209
|
-
afterStatusCodes: retryAfterStatusCodes,
|
|
1210
|
-
maxRetryAfter: Number.POSITIVE_INFINITY,
|
|
1211
|
-
backoffLimit: Number.POSITIVE_INFINITY,
|
|
1212
|
-
};
|
|
1213
|
-
const normalizeRetryOptions = (retry = {}) => {
|
|
1214
|
-
if (typeof retry === 'number') {
|
|
1215
|
-
return {
|
|
1216
|
-
...defaultRetryOptions,
|
|
1217
|
-
limit: retry,
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
if (retry.methods && !Array.isArray(retry.methods)) {
|
|
1221
|
-
throw new Error('retry.methods must be an array');
|
|
1222
|
-
}
|
|
1223
|
-
if (retry.statusCodes && !Array.isArray(retry.statusCodes)) {
|
|
1224
|
-
throw new Error('retry.statusCodes must be an array');
|
|
1225
|
-
}
|
|
1226
|
-
return {
|
|
1227
|
-
...defaultRetryOptions,
|
|
1228
|
-
...retry,
|
|
1229
|
-
afterStatusCodes: retryAfterStatusCodes,
|
|
1230
|
-
};
|
|
1231
|
-
};
|
|
1252
|
+
var TAB_KEY = 'Tab';
|
|
1253
|
+
var ESCAPE_KEY = 'Escape';
|
|
1232
1254
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
.then(() => {
|
|
1247
|
-
clearTimeout(timeoutId);
|
|
1248
|
-
});
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Define the constructor to instantiate a dialog
|
|
1257
|
+
*
|
|
1258
|
+
* @constructor
|
|
1259
|
+
* @param {Element} element
|
|
1260
|
+
*/
|
|
1261
|
+
function A11yDialog(element) {
|
|
1262
|
+
// Prebind the functions that will be bound in addEventListener and
|
|
1263
|
+
// removeEventListener to avoid losing references
|
|
1264
|
+
this._show = this.show.bind(this);
|
|
1265
|
+
this._hide = this.hide.bind(this);
|
|
1266
|
+
this._maintainFocus = this._maintainFocus.bind(this);
|
|
1267
|
+
this._bindKeypress = this._bindKeypress.bind(this);
|
|
1251
1268
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
/*
|
|
1258
|
-
NOTE: Use DomException with AbortError name as specified in MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort)
|
|
1259
|
-
> When abort() is called, the fetch() promise rejects with an Error of type DOMException, with name AbortError.
|
|
1260
|
-
*/
|
|
1261
|
-
if (isDomExceptionSupported) {
|
|
1262
|
-
return new DOMException(signal?.reason ?? 'The operation was aborted.', 'AbortError');
|
|
1263
|
-
}
|
|
1264
|
-
// DOMException not supported. Fall back to use of error and override name.
|
|
1265
|
-
const error = new Error(signal?.reason ?? 'The operation was aborted.');
|
|
1266
|
-
error.name = 'AbortError';
|
|
1267
|
-
return error;
|
|
1268
|
-
}
|
|
1269
|
+
this.$el = element;
|
|
1270
|
+
this.shown = false;
|
|
1271
|
+
this._id = this.$el.getAttribute('data-a11y-dialog') || this.$el.id;
|
|
1272
|
+
this._previouslyFocused = null;
|
|
1273
|
+
this._listeners = {};
|
|
1269
1274
|
|
|
1270
|
-
//
|
|
1271
|
-
|
|
1272
|
-
return new Promise((resolve, reject) => {
|
|
1273
|
-
if (signal) {
|
|
1274
|
-
if (signal.aborted) {
|
|
1275
|
-
reject(composeAbortError(signal));
|
|
1276
|
-
return;
|
|
1277
|
-
}
|
|
1278
|
-
signal.addEventListener('abort', handleAbort, { once: true });
|
|
1279
|
-
}
|
|
1280
|
-
function handleAbort() {
|
|
1281
|
-
reject(composeAbortError(signal));
|
|
1282
|
-
clearTimeout(timeoutId);
|
|
1283
|
-
}
|
|
1284
|
-
const timeoutId = setTimeout(() => {
|
|
1285
|
-
signal?.removeEventListener('abort', handleAbort);
|
|
1286
|
-
resolve();
|
|
1287
|
-
}, ms);
|
|
1288
|
-
});
|
|
1275
|
+
// Initialise everything needed for the dialog to work properly
|
|
1276
|
+
this.create();
|
|
1289
1277
|
}
|
|
1290
1278
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1279
|
+
/**
|
|
1280
|
+
* Set up everything necessary for the dialog to be functioning
|
|
1281
|
+
*
|
|
1282
|
+
* @param {(NodeList | Element | string)} targets
|
|
1283
|
+
* @return {this}
|
|
1284
|
+
*/
|
|
1285
|
+
A11yDialog.prototype.create = function () {
|
|
1286
|
+
this.$el.setAttribute('aria-hidden', true);
|
|
1287
|
+
this.$el.setAttribute('aria-modal', true);
|
|
1288
|
+
this.$el.setAttribute('tabindex', -1);
|
|
1289
|
+
|
|
1290
|
+
if (!this.$el.hasAttribute('role')) {
|
|
1291
|
+
this.$el.setAttribute('role', 'dialog');
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// Keep a collection of dialog openers, each of which will be bound a click
|
|
1295
|
+
// event listener to open the dialog
|
|
1296
|
+
this._openers = $$('[data-a11y-dialog-show="' + this._id + '"]');
|
|
1297
|
+
this._openers.forEach(
|
|
1298
|
+
function (opener) {
|
|
1299
|
+
opener.addEventListener('click', this._show);
|
|
1300
|
+
}.bind(this)
|
|
1301
|
+
);
|
|
1302
|
+
|
|
1303
|
+
// Keep a collection of dialog closers, each of which will be bound a click
|
|
1304
|
+
// event listener to close the dialog
|
|
1305
|
+
const $el = this.$el;
|
|
1306
|
+
|
|
1307
|
+
this._closers = $$('[data-a11y-dialog-hide]', this.$el)
|
|
1308
|
+
// This filter is necessary in case there are nested dialogs, so that
|
|
1309
|
+
// only closers from the current dialog are retrieved and effective
|
|
1310
|
+
.filter(function (closer) {
|
|
1311
|
+
// Testing for `[aria-modal="true"]` is not enough since this attribute
|
|
1312
|
+
// and the collect of closers is done at instantation time, when nested
|
|
1313
|
+
// dialogs might not have yet been instantiated. Note that if the dialogs
|
|
1314
|
+
// are manually instantiated, this could still fail because none of these
|
|
1315
|
+
// selectors would match; this would cause closers to close all parent
|
|
1316
|
+
// dialogs instead of just the current one
|
|
1317
|
+
return closer.closest('[aria-modal="true"], [data-a11y-dialog]') === $el
|
|
1318
|
+
})
|
|
1319
|
+
.concat($$('[data-a11y-dialog-hide="' + this._id + '"]'));
|
|
1320
|
+
|
|
1321
|
+
this._closers.forEach(
|
|
1322
|
+
function (closer) {
|
|
1323
|
+
closer.addEventListener('click', this._hide);
|
|
1324
|
+
}.bind(this)
|
|
1325
|
+
);
|
|
1326
|
+
|
|
1327
|
+
// Execute all callbacks registered for the `create` event
|
|
1328
|
+
this._fire('create');
|
|
1329
|
+
|
|
1330
|
+
return this
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Show the dialog element, disable all the targets (siblings), trap the
|
|
1335
|
+
* current focus within it, listen for some specific key presses and fire all
|
|
1336
|
+
* registered callbacks for `show` event
|
|
1337
|
+
*
|
|
1338
|
+
* @param {CustomEvent} event
|
|
1339
|
+
* @return {this}
|
|
1340
|
+
*/
|
|
1341
|
+
A11yDialog.prototype.show = function (event) {
|
|
1342
|
+
// If the dialog is already open, abort
|
|
1343
|
+
if (this.shown) {
|
|
1344
|
+
return this
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Keep a reference to the currently focused element to be able to restore
|
|
1348
|
+
// it later
|
|
1349
|
+
this._previouslyFocused = document.activeElement;
|
|
1350
|
+
this.$el.removeAttribute('aria-hidden');
|
|
1351
|
+
this.shown = true;
|
|
1352
|
+
|
|
1353
|
+
// Set the focus to the dialog element
|
|
1354
|
+
moveFocusToDialog(this.$el);
|
|
1355
|
+
|
|
1356
|
+
// Bind a focus event listener to the body element to make sure the focus
|
|
1357
|
+
// stays trapped inside the dialog while open, and start listening for some
|
|
1358
|
+
// specific key presses (TAB and ESC)
|
|
1359
|
+
document.body.addEventListener('focus', this._maintainFocus, true);
|
|
1360
|
+
document.addEventListener('keydown', this._bindKeypress);
|
|
1361
|
+
|
|
1362
|
+
// Execute all callbacks registered for the `show` event
|
|
1363
|
+
this._fire('show', event);
|
|
1364
|
+
|
|
1365
|
+
return this
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* Hide the dialog element, enable all the targets (siblings), restore the
|
|
1370
|
+
* focus to the previously active element, stop listening for some specific
|
|
1371
|
+
* key presses and fire all registered callbacks for `hide` event
|
|
1372
|
+
*
|
|
1373
|
+
* @param {CustomEvent} event
|
|
1374
|
+
* @return {this}
|
|
1375
|
+
*/
|
|
1376
|
+
A11yDialog.prototype.hide = function (event) {
|
|
1377
|
+
// If the dialog is already closed, abort
|
|
1378
|
+
if (!this.shown) {
|
|
1379
|
+
return this
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
this.shown = false;
|
|
1383
|
+
this.$el.setAttribute('aria-hidden', 'true');
|
|
1384
|
+
|
|
1385
|
+
// If there was a focused element before the dialog was opened (and it has a
|
|
1386
|
+
// `focus` method), restore the focus back to it
|
|
1387
|
+
// See: https://github.com/KittyGiraudel/a11y-dialog/issues/108
|
|
1388
|
+
if (this._previouslyFocused && this._previouslyFocused.focus) {
|
|
1389
|
+
this._previouslyFocused.focus();
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// Remove the focus event listener to the body element and stop listening
|
|
1393
|
+
// for specific key presses
|
|
1394
|
+
document.body.removeEventListener('focus', this._maintainFocus, true);
|
|
1395
|
+
document.removeEventListener('keydown', this._bindKeypress);
|
|
1396
|
+
|
|
1397
|
+
// Execute all callbacks registered for the `hide` event
|
|
1398
|
+
this._fire('hide', event);
|
|
1399
|
+
|
|
1400
|
+
return this
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
/**
|
|
1404
|
+
* Destroy the current instance (after making sure the dialog has been hidden)
|
|
1405
|
+
* and remove all associated listeners from dialog openers and closers
|
|
1406
|
+
*
|
|
1407
|
+
* @return {this}
|
|
1408
|
+
*/
|
|
1409
|
+
A11yDialog.prototype.destroy = function () {
|
|
1410
|
+
// Hide the dialog to avoid destroying an open instance
|
|
1411
|
+
this.hide();
|
|
1412
|
+
|
|
1413
|
+
// Remove the click event listener from all dialog openers
|
|
1414
|
+
this._openers.forEach(
|
|
1415
|
+
function (opener) {
|
|
1416
|
+
opener.removeEventListener('click', this._show);
|
|
1417
|
+
}.bind(this)
|
|
1418
|
+
);
|
|
1419
|
+
|
|
1420
|
+
// Remove the click event listener from all dialog closers
|
|
1421
|
+
this._closers.forEach(
|
|
1422
|
+
function (closer) {
|
|
1423
|
+
closer.removeEventListener('click', this._hide);
|
|
1424
|
+
}.bind(this)
|
|
1425
|
+
);
|
|
1426
|
+
|
|
1427
|
+
// Execute all callbacks registered for the `destroy` event
|
|
1428
|
+
this._fire('destroy');
|
|
1429
|
+
|
|
1430
|
+
// Keep an object of listener types mapped to callback functions
|
|
1431
|
+
this._listeners = {};
|
|
1432
|
+
|
|
1433
|
+
return this
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* Register a new callback for the given event type
|
|
1438
|
+
*
|
|
1439
|
+
* @param {string} type
|
|
1440
|
+
* @param {Function} handler
|
|
1441
|
+
*/
|
|
1442
|
+
A11yDialog.prototype.on = function (type, handler) {
|
|
1443
|
+
if (typeof this._listeners[type] === 'undefined') {
|
|
1444
|
+
this._listeners[type] = [];
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
this._listeners[type].push(handler);
|
|
1448
|
+
|
|
1449
|
+
return this
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
* Unregister an existing callback for the given event type
|
|
1454
|
+
*
|
|
1455
|
+
* @param {string} type
|
|
1456
|
+
* @param {Function} handler
|
|
1457
|
+
*/
|
|
1458
|
+
A11yDialog.prototype.off = function (type, handler) {
|
|
1459
|
+
var index = (this._listeners[type] || []).indexOf(handler);
|
|
1460
|
+
|
|
1461
|
+
if (index > -1) {
|
|
1462
|
+
this._listeners[type].splice(index, 1);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
return this
|
|
1466
|
+
};
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* Iterate over all registered handlers for given type and call them all with
|
|
1470
|
+
* the dialog element as first argument, event as second argument (if any). Also
|
|
1471
|
+
* dispatch a custom event on the DOM element itself to make it possible to
|
|
1472
|
+
* react to the lifecycle of auto-instantiated dialogs.
|
|
1473
|
+
*
|
|
1474
|
+
* @access private
|
|
1475
|
+
* @param {string} type
|
|
1476
|
+
* @param {CustomEvent} event
|
|
1477
|
+
*/
|
|
1478
|
+
A11yDialog.prototype._fire = function (type, event) {
|
|
1479
|
+
var listeners = this._listeners[type] || [];
|
|
1480
|
+
var domEvent = new CustomEvent(type, { detail: event });
|
|
1481
|
+
|
|
1482
|
+
this.$el.dispatchEvent(domEvent);
|
|
1483
|
+
|
|
1484
|
+
listeners.forEach(
|
|
1485
|
+
function (listener) {
|
|
1486
|
+
listener(this.$el, event);
|
|
1487
|
+
}.bind(this)
|
|
1488
|
+
);
|
|
1489
|
+
};
|
|
1490
|
+
|
|
1491
|
+
/**
|
|
1492
|
+
* Private event handler used when listening to some specific key presses
|
|
1493
|
+
* (namely ESCAPE and TAB)
|
|
1494
|
+
*
|
|
1495
|
+
* @access private
|
|
1496
|
+
* @param {Event} event
|
|
1497
|
+
*/
|
|
1498
|
+
A11yDialog.prototype._bindKeypress = function (event) {
|
|
1499
|
+
// This is an escape hatch in case there are nested dialogs, so the keypresses
|
|
1500
|
+
// are only reacted to for the most recent one
|
|
1501
|
+
const focused = document.activeElement;
|
|
1502
|
+
if (focused && focused.closest('[aria-modal="true"]') !== this.$el) return
|
|
1503
|
+
|
|
1504
|
+
// If the dialog is shown and the ESCAPE key is being pressed, prevent any
|
|
1505
|
+
// further effects from the ESCAPE key and hide the dialog, unless its role
|
|
1506
|
+
// is 'alertdialog', which should be modal
|
|
1507
|
+
if (
|
|
1508
|
+
this.shown &&
|
|
1509
|
+
event.key === ESCAPE_KEY &&
|
|
1510
|
+
this.$el.getAttribute('role') !== 'alertdialog'
|
|
1511
|
+
) {
|
|
1512
|
+
event.preventDefault();
|
|
1513
|
+
this.hide(event);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// If the dialog is shown and the TAB key is being pressed, make sure the
|
|
1517
|
+
// focus stays trapped within the dialog element
|
|
1518
|
+
if (this.shown && event.key === TAB_KEY) {
|
|
1519
|
+
trapTabKey(this.$el, event);
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
/**
|
|
1524
|
+
* Private event handler used when making sure the focus stays within the
|
|
1525
|
+
* currently open dialog
|
|
1526
|
+
*
|
|
1527
|
+
* @access private
|
|
1528
|
+
* @param {Event} event
|
|
1529
|
+
*/
|
|
1530
|
+
A11yDialog.prototype._maintainFocus = function (event) {
|
|
1531
|
+
// If the dialog is shown and the focus is not within a dialog element (either
|
|
1532
|
+
// this one or another one in case of nested dialogs) or within an element
|
|
1533
|
+
// with the `data-a11y-dialog-focus-trap-ignore` attribute, move it back to
|
|
1534
|
+
// its first focusable child.
|
|
1535
|
+
// See: https://github.com/KittyGiraudel/a11y-dialog/issues/177
|
|
1536
|
+
if (
|
|
1537
|
+
this.shown &&
|
|
1538
|
+
!event.target.closest('[aria-modal="true"]') &&
|
|
1539
|
+
!event.target.closest('[data-a11y-dialog-ignore-focus-trap]')
|
|
1540
|
+
) {
|
|
1541
|
+
moveFocusToDialog(this.$el);
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* Convert a NodeList into an array
|
|
1547
|
+
*
|
|
1548
|
+
* @param {NodeList} collection
|
|
1549
|
+
* @return {Array<Element>}
|
|
1550
|
+
*/
|
|
1551
|
+
function toArray(collection) {
|
|
1552
|
+
return Array.prototype.slice.call(collection)
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* Query the DOM for nodes matching the given selector, scoped to context (or
|
|
1557
|
+
* the whole document)
|
|
1558
|
+
*
|
|
1559
|
+
* @param {String} selector
|
|
1560
|
+
* @param {Element} [context = document]
|
|
1561
|
+
* @return {Array<Element>}
|
|
1562
|
+
*/
|
|
1563
|
+
function $$(selector, context) {
|
|
1564
|
+
return toArray((context || document).querySelectorAll(selector))
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
* Set the focus to the first element with `autofocus` with the element or the
|
|
1569
|
+
* element itself
|
|
1570
|
+
*
|
|
1571
|
+
* @param {Element} node
|
|
1572
|
+
*/
|
|
1573
|
+
function moveFocusToDialog(node) {
|
|
1574
|
+
var focused = node.querySelector('[autofocus]') || node;
|
|
1575
|
+
|
|
1576
|
+
focused.focus();
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* Get the focusable children of the given element
|
|
1581
|
+
*
|
|
1582
|
+
* @param {Element} node
|
|
1583
|
+
* @return {Array<Element>}
|
|
1584
|
+
*/
|
|
1585
|
+
function getFocusableChildren(node) {
|
|
1586
|
+
return $$(focusableSelectors.join(','), node).filter(function (child) {
|
|
1587
|
+
return !!(
|
|
1588
|
+
child.offsetWidth ||
|
|
1589
|
+
child.offsetHeight ||
|
|
1590
|
+
child.getClientRects().length
|
|
1591
|
+
)
|
|
1592
|
+
})
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* Trap the focus inside the given element
|
|
1597
|
+
*
|
|
1598
|
+
* @param {Element} node
|
|
1599
|
+
* @param {Event} event
|
|
1600
|
+
*/
|
|
1601
|
+
function trapTabKey(node, event) {
|
|
1602
|
+
var focusableChildren = getFocusableChildren(node);
|
|
1603
|
+
var focusedItemIndex = focusableChildren.indexOf(document.activeElement);
|
|
1604
|
+
|
|
1605
|
+
// If the SHIFT key is being pressed while tabbing (moving backwards) and
|
|
1606
|
+
// the currently focused item is the first one, move the focus to the last
|
|
1607
|
+
// focusable item from the dialog element
|
|
1608
|
+
if (event.shiftKey && focusedItemIndex === 0) {
|
|
1609
|
+
focusableChildren[focusableChildren.length - 1].focus();
|
|
1610
|
+
event.preventDefault();
|
|
1611
|
+
// If the SHIFT key is not being pressed (moving forwards) and the currently
|
|
1612
|
+
// focused item is the last one, move the focus to the first focusable item
|
|
1613
|
+
// from the dialog element
|
|
1614
|
+
} else if (
|
|
1615
|
+
!event.shiftKey &&
|
|
1616
|
+
focusedItemIndex === focusableChildren.length - 1
|
|
1617
|
+
) {
|
|
1618
|
+
focusableChildren[0].focus();
|
|
1619
|
+
event.preventDefault();
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
function instantiateDialogs() {
|
|
1624
|
+
$$('[data-a11y-dialog]').forEach(function (node) {
|
|
1625
|
+
new A11yDialog(node);
|
|
1626
|
+
});
|
|
1580
1627
|
}
|
|
1581
1628
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1629
|
+
if (typeof document !== 'undefined') {
|
|
1630
|
+
if (document.readyState === 'loading') {
|
|
1631
|
+
document.addEventListener('DOMContentLoaded', instantiateDialogs);
|
|
1632
|
+
} else {
|
|
1633
|
+
if (window.requestAnimationFrame) {
|
|
1634
|
+
window.requestAnimationFrame(instantiateDialogs);
|
|
1635
|
+
} else {
|
|
1636
|
+
window.setTimeout(instantiateDialogs, 16);
|
|
1589
1637
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
ky.stop = stop;
|
|
1593
|
-
return ky;
|
|
1594
|
-
};
|
|
1595
|
-
const ky = createInstance();
|
|
1596
|
-
var ky$1 = ky;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1597
1640
|
|
|
1598
|
-
var
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1641
|
+
var CONTAINER_ID = "__authsignal-popup-container";
|
|
1642
|
+
var CONTENT_ID = "__authsignal-popup-content";
|
|
1643
|
+
var OVERLAY_ID = "__authsignal-popup-overlay";
|
|
1644
|
+
var STYLE_ID = "__authsignal-popup-style";
|
|
1645
|
+
var IFRAME_ID = "__authsignal-popup-iframe";
|
|
1646
|
+
var DEFAULT_WIDTH = "385px";
|
|
1647
|
+
var INITIAL_HEIGHT = "384px";
|
|
1648
|
+
var PopupHandler = /** @class */ (function () {
|
|
1649
|
+
function PopupHandler(_a) {
|
|
1650
|
+
var width = _a.width;
|
|
1651
|
+
this.popup = null;
|
|
1652
|
+
if (document.querySelector("#".concat(CONTAINER_ID))) {
|
|
1653
|
+
throw new Error("Multiple instances of Authsignal popup is not supported.");
|
|
1654
|
+
}
|
|
1655
|
+
this.create({ width: width });
|
|
1605
1656
|
}
|
|
1606
|
-
|
|
1607
|
-
var
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1657
|
+
PopupHandler.prototype.create = function (_a) {
|
|
1658
|
+
var _this = this;
|
|
1659
|
+
var _b = _a.width, width = _b === void 0 ? DEFAULT_WIDTH : _b;
|
|
1660
|
+
var isWidthValidCSSValue = CSS.supports("width", width);
|
|
1661
|
+
var popupWidth = width;
|
|
1662
|
+
if (!isWidthValidCSSValue) {
|
|
1663
|
+
console.warn("Invalid CSS value for `popupOptions.width`. Using default value instead.");
|
|
1664
|
+
popupWidth = DEFAULT_WIDTH;
|
|
1665
|
+
}
|
|
1666
|
+
// Create dialog container
|
|
1667
|
+
var container = document.createElement("div");
|
|
1668
|
+
container.setAttribute("id", CONTAINER_ID);
|
|
1669
|
+
container.setAttribute("aria-hidden", "true");
|
|
1670
|
+
// Create dialog overlay
|
|
1671
|
+
var overlay = document.createElement("div");
|
|
1672
|
+
overlay.setAttribute("id", OVERLAY_ID);
|
|
1673
|
+
overlay.setAttribute("data-a11y-dialog-hide", "true");
|
|
1674
|
+
// Create dialog content
|
|
1675
|
+
var content = document.createElement("div");
|
|
1676
|
+
content.setAttribute("id", CONTENT_ID);
|
|
1677
|
+
document.body.appendChild(container);
|
|
1678
|
+
// Create CSS for dialog
|
|
1679
|
+
var style = document.createElement("style");
|
|
1680
|
+
style.setAttribute("id", STYLE_ID);
|
|
1681
|
+
style.textContent = "\n #".concat(CONTAINER_ID, ",\n #").concat(OVERLAY_ID, " {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n }\n\n #").concat(CONTAINER_ID, " {\n z-index: 2147483647;\n display: flex;\n }\n\n #").concat(CONTAINER_ID, "[aria-hidden='true'] {\n display: none;\n }\n\n #").concat(OVERLAY_ID, " {\n background-color: rgba(0, 0, 0, 0.18);\n }\n\n #").concat(CONTENT_ID, " {\n margin: auto;\n z-index: 2147483647;\n position: relative;\n background-color: transparent;\n border-radius: 8px;\n width: ").concat(popupWidth, ";\n }\n\n #").concat(CONTENT_ID, " iframe {\n width: 1px;\n min-width: 100%;\n border-radius: inherit;\n max-height: 95vh;\n height: ").concat(INITIAL_HEIGHT, ";\n }\n ");
|
|
1682
|
+
// Attach the created elements
|
|
1683
|
+
document.head.insertAdjacentElement("beforeend", style);
|
|
1684
|
+
container.appendChild(overlay);
|
|
1685
|
+
container.appendChild(content);
|
|
1686
|
+
this.popup = new A11yDialog(container);
|
|
1687
|
+
// Make sure to remove any trace of the dialog on hide
|
|
1688
|
+
this.popup.on("hide", function () {
|
|
1689
|
+
_this.destroy();
|
|
1621
1690
|
});
|
|
1622
1691
|
};
|
|
1623
|
-
|
|
1624
|
-
var
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
headers: this.buildHeaders(token)
|
|
1632
|
-
})];
|
|
1633
|
-
case 1:
|
|
1634
|
-
response = _b.sent();
|
|
1635
|
-
return [2 /*return*/, response.json()];
|
|
1636
|
-
}
|
|
1637
|
-
});
|
|
1638
|
-
});
|
|
1692
|
+
PopupHandler.prototype.destroy = function () {
|
|
1693
|
+
var dialogEl = document.querySelector("#".concat(CONTAINER_ID));
|
|
1694
|
+
var styleEl = document.querySelector("#".concat(STYLE_ID));
|
|
1695
|
+
if (dialogEl && styleEl) {
|
|
1696
|
+
document.body.removeChild(dialogEl);
|
|
1697
|
+
document.head.removeChild(styleEl);
|
|
1698
|
+
}
|
|
1699
|
+
window.removeEventListener("message", resizeIframe);
|
|
1639
1700
|
};
|
|
1640
|
-
|
|
1641
|
-
var
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1701
|
+
PopupHandler.prototype.show = function (_a) {
|
|
1702
|
+
var _b;
|
|
1703
|
+
var url = _a.url;
|
|
1704
|
+
if (!this.popup) {
|
|
1705
|
+
throw new Error("Popup is not initialized");
|
|
1706
|
+
}
|
|
1707
|
+
var iframe = document.createElement("iframe");
|
|
1708
|
+
iframe.setAttribute("id", IFRAME_ID);
|
|
1709
|
+
iframe.setAttribute("name", "authsignal");
|
|
1710
|
+
iframe.setAttribute("title", "Authsignal multi-factor authentication");
|
|
1711
|
+
iframe.setAttribute("src", url);
|
|
1712
|
+
iframe.setAttribute("frameborder", "0");
|
|
1713
|
+
iframe.setAttribute("allow", "publickey-credentials-get *; publickey-credentials-create *; clipboard-write");
|
|
1714
|
+
var dialogContent = document.querySelector("#".concat(CONTENT_ID));
|
|
1715
|
+
if (dialogContent) {
|
|
1716
|
+
dialogContent.appendChild(iframe);
|
|
1717
|
+
}
|
|
1718
|
+
window.addEventListener("message", resizeIframe);
|
|
1719
|
+
(_b = this.popup) === null || _b === void 0 ? void 0 : _b.show();
|
|
1656
1720
|
};
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
switch (_b.label) {
|
|
1663
|
-
case 0: return [4 /*yield*/, this.api.post("client/verify/passkey", {
|
|
1664
|
-
json: rest,
|
|
1665
|
-
headers: this.buildHeaders(token)
|
|
1666
|
-
})];
|
|
1667
|
-
case 1:
|
|
1668
|
-
response = _b.sent();
|
|
1669
|
-
return [2 /*return*/, response.json()];
|
|
1670
|
-
}
|
|
1671
|
-
});
|
|
1672
|
-
});
|
|
1721
|
+
PopupHandler.prototype.close = function () {
|
|
1722
|
+
if (!this.popup) {
|
|
1723
|
+
throw new Error("Popup is not initialized");
|
|
1724
|
+
}
|
|
1725
|
+
this.popup.hide();
|
|
1673
1726
|
};
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1727
|
+
PopupHandler.prototype.on = function (event, handler) {
|
|
1728
|
+
if (!this.popup) {
|
|
1729
|
+
throw new Error("Popup is not initialized");
|
|
1730
|
+
}
|
|
1731
|
+
this.popup.on(event, handler);
|
|
1679
1732
|
};
|
|
1680
|
-
return
|
|
1733
|
+
return PopupHandler;
|
|
1681
1734
|
}());
|
|
1682
|
-
|
|
1683
|
-
var
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
this.api = new PasskeyApiClient({ baseUrl: baseUrl, tenantId: tenantId });
|
|
1735
|
+
function resizeIframe(event) {
|
|
1736
|
+
var iframeEl = document.querySelector("#".concat(IFRAME_ID));
|
|
1737
|
+
if (iframeEl && event.data.height) {
|
|
1738
|
+
iframeEl.style.height = event.data.height + "px";
|
|
1687
1739
|
}
|
|
1688
|
-
|
|
1689
|
-
var userName = _a.userName, token = _a.token;
|
|
1690
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
1691
|
-
var optionsResponse, registrationResponse, addAuthenticatorResponse;
|
|
1692
|
-
return __generator(this, function (_b) {
|
|
1693
|
-
switch (_b.label) {
|
|
1694
|
-
case 0: return [4 /*yield*/, this.api.registrationOptions({ userName: userName, token: token })];
|
|
1695
|
-
case 1:
|
|
1696
|
-
optionsResponse = _b.sent();
|
|
1697
|
-
return [4 /*yield*/, startRegistration(optionsResponse.options)];
|
|
1698
|
-
case 2:
|
|
1699
|
-
registrationResponse = _b.sent();
|
|
1700
|
-
return [4 /*yield*/, this.api.addAuthenticator({
|
|
1701
|
-
challengeId: optionsResponse.challengeId,
|
|
1702
|
-
registrationCredential: registrationResponse,
|
|
1703
|
-
token: token
|
|
1704
|
-
})];
|
|
1705
|
-
case 3:
|
|
1706
|
-
addAuthenticatorResponse = _b.sent();
|
|
1707
|
-
return [2 /*return*/, addAuthenticatorResponse === null || addAuthenticatorResponse === void 0 ? void 0 : addAuthenticatorResponse.accessToken];
|
|
1708
|
-
}
|
|
1709
|
-
});
|
|
1710
|
-
});
|
|
1711
|
-
};
|
|
1712
|
-
Passkey.prototype.signIn = function (params) {
|
|
1713
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
1714
|
-
var optionsResponse, authenticationResponse, verifyResponse;
|
|
1715
|
-
return __generator(this, function (_a) {
|
|
1716
|
-
switch (_a.label) {
|
|
1717
|
-
case 0:
|
|
1718
|
-
if ((params === null || params === void 0 ? void 0 : params.token) && params.autofill) {
|
|
1719
|
-
throw new Error("Autofill is not supported when providing a token");
|
|
1720
|
-
}
|
|
1721
|
-
return [4 /*yield*/, this.api.authenticationOptions({ token: params === null || params === void 0 ? void 0 : params.token })];
|
|
1722
|
-
case 1:
|
|
1723
|
-
optionsResponse = _a.sent();
|
|
1724
|
-
return [4 /*yield*/, startAuthentication(optionsResponse.options, params === null || params === void 0 ? void 0 : params.autofill)];
|
|
1725
|
-
case 2:
|
|
1726
|
-
authenticationResponse = _a.sent();
|
|
1727
|
-
return [4 /*yield*/, this.api.verify({
|
|
1728
|
-
challengeId: optionsResponse.challengeId,
|
|
1729
|
-
authenticationCredential: authenticationResponse,
|
|
1730
|
-
token: params === null || params === void 0 ? void 0 : params.token
|
|
1731
|
-
})];
|
|
1732
|
-
case 3:
|
|
1733
|
-
verifyResponse = _a.sent();
|
|
1734
|
-
return [2 /*return*/, verifyResponse === null || verifyResponse === void 0 ? void 0 : verifyResponse.accessToken];
|
|
1735
|
-
}
|
|
1736
|
-
});
|
|
1737
|
-
});
|
|
1738
|
-
};
|
|
1739
|
-
return Passkey;
|
|
1740
|
-
}());
|
|
1740
|
+
}
|
|
1741
1741
|
|
|
1742
1742
|
var DEFAULT_COOKIE_NAME = "__as_aid";
|
|
1743
|
+
var DEFAULT_PROFILING_COOKIE_NAME = "__as_pid";
|
|
1743
1744
|
var DEFAULT_BASE_URL = "https://api.authsignal.com/v1";
|
|
1745
|
+
var TMX_ORG_ID = "4a08uqve";
|
|
1744
1746
|
var Authsignal = /** @class */ (function () {
|
|
1745
1747
|
function Authsignal(_a) {
|
|
1746
1748
|
var cookieDomain = _a.cookieDomain, _b = _a.cookieName, cookieName = _b === void 0 ? DEFAULT_COOKIE_NAME : _b, _c = _a.baseUrl, baseUrl = _c === void 0 ? DEFAULT_BASE_URL : _c, tenantId = _a.tenantId;
|
|
1747
1749
|
this.anonymousId = "";
|
|
1750
|
+
this.profilingId = "";
|
|
1748
1751
|
this.cookieDomain = "";
|
|
1749
1752
|
this.anonymousIdCookieName = "";
|
|
1750
1753
|
this._token = undefined;
|
|
@@ -1780,6 +1783,40 @@ var Authsignal = /** @class */ (function () {
|
|
|
1780
1783
|
this.launchWithRedirect(url);
|
|
1781
1784
|
}
|
|
1782
1785
|
};
|
|
1786
|
+
Authsignal.prototype.initAdvancedProfiling = function (baseUrl) {
|
|
1787
|
+
var profilingId = v4();
|
|
1788
|
+
this.profilingId = profilingId;
|
|
1789
|
+
setCookie({
|
|
1790
|
+
name: DEFAULT_PROFILING_COOKIE_NAME,
|
|
1791
|
+
value: profilingId,
|
|
1792
|
+
expire: Infinity,
|
|
1793
|
+
domain: this.cookieDomain,
|
|
1794
|
+
secure: document.location.protocol !== "http:"
|
|
1795
|
+
});
|
|
1796
|
+
var tmxProfilingScruiptUrl = baseUrl
|
|
1797
|
+
? "".concat(baseUrl, "/fp/tags.js?org_id=").concat(TMX_ORG_ID, "&session_id=").concat(profilingId)
|
|
1798
|
+
: "https://h.online-metrix.net/fp/tags.js?org_id=".concat(TMX_ORG_ID, "&session_id=").concat(profilingId);
|
|
1799
|
+
var script = document.createElement("script");
|
|
1800
|
+
script.src = tmxProfilingScruiptUrl;
|
|
1801
|
+
script.async = false;
|
|
1802
|
+
script.id = "as_adv_profile";
|
|
1803
|
+
document.head.appendChild(script);
|
|
1804
|
+
var pixelContainer = document.createElement("noscript");
|
|
1805
|
+
pixelContainer.setAttribute("id", "as_adv_profile_pixel");
|
|
1806
|
+
pixelContainer.setAttribute("aria-hidden", "true");
|
|
1807
|
+
// Instantiate Pixel
|
|
1808
|
+
var iframe = document.createElement("iframe");
|
|
1809
|
+
var profilingPixelUrl = baseUrl
|
|
1810
|
+
? "".concat(baseUrl, "/fp/tags?org_id=").concat(TMX_ORG_ID, "&session_id=").concat(profilingId)
|
|
1811
|
+
: "https://h.online-metrix.net/fp/tags?org_id=".concat(TMX_ORG_ID, "&session_id=").concat(profilingId);
|
|
1812
|
+
iframe.setAttribute("id", "as_adv_profile_pixel");
|
|
1813
|
+
iframe.setAttribute("src", profilingPixelUrl);
|
|
1814
|
+
iframe.setAttribute("style", "width: 100px; height: 100px; border: 0; position: absolute; top: -5000px;");
|
|
1815
|
+
if (pixelContainer) {
|
|
1816
|
+
pixelContainer.appendChild(iframe);
|
|
1817
|
+
document.body.prepend(pixelContainer);
|
|
1818
|
+
}
|
|
1819
|
+
};
|
|
1783
1820
|
Authsignal.prototype.launchWithRedirect = function (url) {
|
|
1784
1821
|
window.location.href = url;
|
|
1785
1822
|
};
|