simplewebauthn-browser.js
⎇
Raw
1// node_modules/@simplewebauthn/browser/esm/helpers/bufferToBase64URLString.js
2function bufferToBase64URLString(buffer) {
3 const bytes = new Uint8Array(buffer);
4 let str = "";
5 for (const charCode of bytes) {
6 str += String.fromCharCode(charCode);
7 }
8 const base64String = btoa(str);
9 return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
10}
11
12// node_modules/@simplewebauthn/browser/esm/helpers/base64URLStringToBuffer.js
13function base64URLStringToBuffer(base64URLString) {
14 const base64 = base64URLString.replace(/-/g, "+").replace(/_/g, "/");
15 const padLength = (4 - base64.length % 4) % 4;
16 const padded = base64.padEnd(base64.length + padLength, "=");
17 const binary = atob(padded);
18 const buffer = new ArrayBuffer(binary.length);
19 const bytes = new Uint8Array(buffer);
20 for (let i = 0;i < binary.length; i++) {
21 bytes[i] = binary.charCodeAt(i);
22 }
23 return buffer;
24}
25
26// node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthn.js
27function browserSupportsWebAuthn() {
28 return _browserSupportsWebAuthnInternals.stubThis(globalThis?.PublicKeyCredential !== undefined && typeof globalThis.PublicKeyCredential === "function");
29}
30var _browserSupportsWebAuthnInternals = {
31 stubThis: (value) => value
32};
33
34// node_modules/@simplewebauthn/browser/esm/helpers/toPublicKeyCredentialDescriptor.js
35function toPublicKeyCredentialDescriptor(descriptor) {
36 const { id } = descriptor;
37 return {
38 ...descriptor,
39 id: base64URLStringToBuffer(id),
40 transports: descriptor.transports
41 };
42}
43
44// node_modules/@simplewebauthn/browser/esm/helpers/isValidDomain.js
45function isValidDomain(hostname) {
46 return hostname === "localhost" || /^((xn--[a-z0-9-]+|[a-z0-9]+(-[a-z0-9]+)*)\.)+([a-z]{2,}|xn--[a-z0-9-]+)$/i.test(hostname);
47}
48
49// node_modules/@simplewebauthn/browser/esm/helpers/webAuthnError.js
50class WebAuthnError extends Error {
51 constructor({ message, code, cause, name }) {
52 super(message, { cause });
53 Object.defineProperty(this, "code", {
54 enumerable: true,
55 configurable: true,
56 writable: true,
57 value: undefined
58 });
59 this.name = name ?? cause.name;
60 this.code = code;
61 }
62}
63
64// node_modules/@simplewebauthn/browser/esm/helpers/identifyRegistrationError.js
65function identifyRegistrationError({ error, options }) {
66 const { publicKey } = options;
67 if (!publicKey) {
68 throw Error("options was missing required publicKey property");
69 }
70 if (error.name === "AbortError") {
71 if (options.signal instanceof AbortSignal) {
72 return new WebAuthnError({
73 message: "Registration ceremony was sent an abort signal",
74 code: "ERROR_CEREMONY_ABORTED",
75 cause: error
76 });
77 }
78 } else if (error.name === "ConstraintError") {
79 if (publicKey.authenticatorSelection?.requireResidentKey === true) {
80 return new WebAuthnError({
81 message: "Discoverable credentials were required but no available authenticator supported it",
82 code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
83 cause: error
84 });
85 } else if (options.mediation === "conditional" && publicKey.authenticatorSelection?.userVerification === "required") {
86 return new WebAuthnError({
87 message: "User verification was required during automatic registration but it could not be performed",
88 code: "ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",
89 cause: error
90 });
91 } else if (publicKey.authenticatorSelection?.userVerification === "required") {
92 return new WebAuthnError({
93 message: "User verification was required but no available authenticator supported it",
94 code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
95 cause: error
96 });
97 }
98 } else if (error.name === "InvalidStateError") {
99 return new WebAuthnError({
100 message: "The authenticator was previously registered",
101 code: "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",
102 cause: error
103 });
104 } else if (error.name === "NotAllowedError") {
105 return new WebAuthnError({
106 message: error.message,
107 code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
108 cause: error
109 });
110 } else if (error.name === "NotSupportedError") {
111 const validPubKeyCredParams = publicKey.pubKeyCredParams.filter((param) => param.type === "public-key");
112 if (validPubKeyCredParams.length === 0) {
113 return new WebAuthnError({
114 message: 'No entry in pubKeyCredParams was of type "public-key"',
115 code: "ERROR_MALFORMED_PUBKEYCREDPARAMS",
116 cause: error
117 });
118 }
119 return new WebAuthnError({
120 message: "No available authenticator supported any of the specified pubKeyCredParams algorithms",
121 code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
122 cause: error
123 });
124 } else if (error.name === "SecurityError") {
125 const effectiveDomain = globalThis.location.hostname;
126 if (!isValidDomain(effectiveDomain)) {
127 return new WebAuthnError({
128 message: `${globalThis.location.hostname} is an invalid domain`,
129 code: "ERROR_INVALID_DOMAIN",
130 cause: error
131 });
132 } else if (publicKey.rp.id !== effectiveDomain) {
133 return new WebAuthnError({
134 message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
135 code: "ERROR_INVALID_RP_ID",
136 cause: error
137 });
138 }
139 } else if (error.name === "TypeError") {
140 if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
141 return new WebAuthnError({
142 message: "User ID was not between 1 and 64 characters",
143 code: "ERROR_INVALID_USER_ID_LENGTH",
144 cause: error
145 });
146 }
147 } else if (error.name === "UnknownError") {
148 return new WebAuthnError({
149 message: "The authenticator was unable to process the specified options, or could not create a new credential",
150 code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
151 cause: error
152 });
153 }
154 return error;
155}
156
157// node_modules/@simplewebauthn/browser/esm/helpers/webAuthnAbortService.js
158class BaseWebAuthnAbortService {
159 constructor() {
160 Object.defineProperty(this, "controller", {
161 enumerable: true,
162 configurable: true,
163 writable: true,
164 value: undefined
165 });
166 }
167 createNewAbortSignal() {
168 if (this.controller) {
169 const abortError = new Error("Cancelling existing WebAuthn API call for new one");
170 abortError.name = "AbortError";
171 this.controller.abort(abortError);
172 }
173 const newController = new AbortController;
174 this.controller = newController;
175 return newController.signal;
176 }
177 cancelCeremony() {
178 if (this.controller) {
179 const abortError = new Error("Manually cancelling existing WebAuthn API call");
180 abortError.name = "AbortError";
181 this.controller.abort(abortError);
182 this.controller = undefined;
183 }
184 }
185}
186var WebAuthnAbortService = new BaseWebAuthnAbortService;
187
188// node_modules/@simplewebauthn/browser/esm/helpers/toAuthenticatorAttachment.js
189var attachments = ["cross-platform", "platform"];
190function toAuthenticatorAttachment(attachment) {
191 if (!attachment) {
192 return;
193 }
194 if (attachments.indexOf(attachment) < 0) {
195 return;
196 }
197 return attachment;
198}
199
200// node_modules/@simplewebauthn/browser/esm/methods/startRegistration.js
201async function startRegistration(options) {
202 if (!options.optionsJSON && options.challenge) {
203 console.warn("startRegistration() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
204 options = { optionsJSON: options };
205 }
206 const { optionsJSON, useAutoRegister = false } = options;
207 if (!browserSupportsWebAuthn()) {
208 throw new Error("WebAuthn is not supported in this browser");
209 }
210 const publicKey = {
211 ...optionsJSON,
212 challenge: base64URLStringToBuffer(optionsJSON.challenge),
213 user: {
214 ...optionsJSON.user,
215 id: base64URLStringToBuffer(optionsJSON.user.id)
216 },
217 excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor)
218 };
219 const createOptions = {};
220 if (useAutoRegister) {
221 createOptions.mediation = "conditional";
222 }
223 createOptions.publicKey = publicKey;
224 createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
225 let credential;
226 try {
227 credential = await navigator.credentials.create(createOptions);
228 } catch (err) {
229 throw identifyRegistrationError({ error: err, options: createOptions });
230 }
231 if (!credential) {
232 throw new Error("Registration was not completed");
233 }
234 const { id, rawId, response, type } = credential;
235 let transports = undefined;
236 if (typeof response.getTransports === "function") {
237 transports = response.getTransports();
238 }
239 let responsePublicKeyAlgorithm = undefined;
240 if (typeof response.getPublicKeyAlgorithm === "function") {
241 try {
242 responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
243 } catch (error) {
244 warnOnBrokenImplementation("getPublicKeyAlgorithm()", error);
245 }
246 }
247 let responsePublicKey = undefined;
248 if (typeof response.getPublicKey === "function") {
249 try {
250 const _publicKey = response.getPublicKey();
251 if (_publicKey !== null) {
252 responsePublicKey = bufferToBase64URLString(_publicKey);
253 }
254 } catch (error) {
255 warnOnBrokenImplementation("getPublicKey()", error);
256 }
257 }
258 let responseAuthenticatorData;
259 if (typeof response.getAuthenticatorData === "function") {
260 try {
261 responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
262 } catch (error) {
263 warnOnBrokenImplementation("getAuthenticatorData()", error);
264 }
265 }
266 return {
267 id,
268 rawId: bufferToBase64URLString(rawId),
269 response: {
270 attestationObject: bufferToBase64URLString(response.attestationObject),
271 clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
272 transports,
273 publicKeyAlgorithm: responsePublicKeyAlgorithm,
274 publicKey: responsePublicKey,
275 authenticatorData: responseAuthenticatorData
276 },
277 type,
278 clientExtensionResults: credential.getClientExtensionResults(),
279 authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
280 };
281}
282function warnOnBrokenImplementation(methodName, cause) {
283 console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.
284`, cause);
285}
286// node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthnAutofill.js
287function browserSupportsWebAuthnAutofill() {
288 if (!browserSupportsWebAuthn()) {
289 return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
290 }
291 const globalPublicKeyCredential = globalThis.PublicKeyCredential;
292 if (globalPublicKeyCredential?.isConditionalMediationAvailable === undefined) {
293 return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
294 }
295 return _browserSupportsWebAuthnAutofillInternals.stubThis(globalPublicKeyCredential.isConditionalMediationAvailable());
296}
297var _browserSupportsWebAuthnAutofillInternals = {
298 stubThis: (value) => value
299};
300
301// node_modules/@simplewebauthn/browser/esm/helpers/identifyAuthenticationError.js
302function identifyAuthenticationError({ error, options }) {
303 const { publicKey } = options;
304 if (!publicKey) {
305 throw Error("options was missing required publicKey property");
306 }
307 if (error.name === "AbortError") {
308 if (options.signal instanceof AbortSignal) {
309 return new WebAuthnError({
310 message: "Authentication ceremony was sent an abort signal",
311 code: "ERROR_CEREMONY_ABORTED",
312 cause: error
313 });
314 }
315 } else if (error.name === "NotAllowedError") {
316 return new WebAuthnError({
317 message: error.message,
318 code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
319 cause: error
320 });
321 } else if (error.name === "SecurityError") {
322 const effectiveDomain = globalThis.location.hostname;
323 if (!isValidDomain(effectiveDomain)) {
324 return new WebAuthnError({
325 message: `${globalThis.location.hostname} is an invalid domain`,
326 code: "ERROR_INVALID_DOMAIN",
327 cause: error
328 });
329 } else if (publicKey.rpId !== effectiveDomain) {
330 return new WebAuthnError({
331 message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
332 code: "ERROR_INVALID_RP_ID",
333 cause: error
334 });
335 }
336 } else if (error.name === "UnknownError") {
337 return new WebAuthnError({
338 message: "The authenticator was unable to process the specified options, or could not create a new assertion signature",
339 code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
340 cause: error
341 });
342 }
343 return error;
344}
345
346// node_modules/@simplewebauthn/browser/esm/methods/startAuthentication.js
347async function startAuthentication(options) {
348 if (!options.optionsJSON && options.challenge) {
349 console.warn("startAuthentication() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
350 options = { optionsJSON: options };
351 }
352 const { optionsJSON, useBrowserAutofill = false, verifyBrowserAutofillInput = true } = options;
353 if (!browserSupportsWebAuthn()) {
354 throw new Error("WebAuthn is not supported in this browser");
355 }
356 let allowCredentials;
357 if (optionsJSON.allowCredentials?.length !== 0) {
358 allowCredentials = optionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
359 }
360 const publicKey = {
361 ...optionsJSON,
362 challenge: base64URLStringToBuffer(optionsJSON.challenge),
363 allowCredentials
364 };
365 const getOptions = {};
366 if (useBrowserAutofill) {
367 if (!await browserSupportsWebAuthnAutofill()) {
368 throw Error("Browser does not support WebAuthn autofill");
369 }
370 const eligibleInputs = document.querySelectorAll("input[autocomplete$='webauthn']");
371 if (eligibleInputs.length < 1 && verifyBrowserAutofillInput) {
372 throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');
373 }
374 getOptions.mediation = "conditional";
375 publicKey.allowCredentials = [];
376 }
377 getOptions.publicKey = publicKey;
378 getOptions.signal = WebAuthnAbortService.createNewAbortSignal();
379 let credential;
380 try {
381 credential = await navigator.credentials.get(getOptions);
382 } catch (err) {
383 throw identifyAuthenticationError({ error: err, options: getOptions });
384 }
385 if (!credential) {
386 throw new Error("Authentication was not completed");
387 }
388 const { id, rawId, response, type } = credential;
389 let userHandle = undefined;
390 if (response.userHandle) {
391 userHandle = bufferToBase64URLString(response.userHandle);
392 }
393 return {
394 id,
395 rawId: bufferToBase64URLString(rawId),
396 response: {
397 authenticatorData: bufferToBase64URLString(response.authenticatorData),
398 clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
399 signature: bufferToBase64URLString(response.signature),
400 userHandle
401 },
402 type,
403 clientExtensionResults: credential.getClientExtensionResults(),
404 authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
405 };
406}
407// node_modules/@simplewebauthn/browser/esm/helpers/platformAuthenticatorIsAvailable.js
408function platformAuthenticatorIsAvailable() {
409 if (!browserSupportsWebAuthn()) {
410 return new Promise((resolve) => resolve(false));
411 }
412 return PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
413}
414export {
415 WebAuthnAbortService,
416 WebAuthnError,
417 _browserSupportsWebAuthnAutofillInternals,
418 _browserSupportsWebAuthnInternals,
419 base64URLStringToBuffer,
420 browserSupportsWebAuthn,
421 browserSupportsWebAuthnAutofill,
422 bufferToBase64URLString,
423 platformAuthenticatorIsAvailable,
424 startAuthentication,
425 startRegistration
426};
427