UNPKG

@simplewebauthn/browser

Version:
125 lines (124 loc) 6.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.startRegistration = startRegistration; const bufferToBase64URLString_js_1 = require("../helpers/bufferToBase64URLString.js"); const base64URLStringToBuffer_js_1 = require("../helpers/base64URLStringToBuffer.js"); const browserSupportsWebAuthn_js_1 = require("../helpers/browserSupportsWebAuthn.js"); const toPublicKeyCredentialDescriptor_js_1 = require("../helpers/toPublicKeyCredentialDescriptor.js"); const identifyRegistrationError_js_1 = require("../helpers/identifyRegistrationError.js"); const webAuthnAbortService_js_1 = require("../helpers/webAuthnAbortService.js"); const toAuthenticatorAttachment_js_1 = require("../helpers/toAuthenticatorAttachment.js"); /** * Begin authenticator "registration" via WebAuthn attestation * * @param optionsJSON Output from **@simplewebauthn/server**'s `generateRegistrationOptions()` * @param useAutoRegister (Optional) Try to silently create a passkey with the password manager that the user just signed in with. Defaults to `false`. */ async function startRegistration(options) { // @ts-ignore: Intentionally check for old call structure to warn about improper API call if (!options.optionsJSON && options.challenge) { 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.'); // @ts-ignore: Reassign the options, passed in as a positional argument, to the expected variable options = { optionsJSON: options }; } const { optionsJSON, useAutoRegister = false } = options; if (!(0, browserSupportsWebAuthn_js_1.browserSupportsWebAuthn)()) { throw new Error('WebAuthn is not supported in this browser'); } // We need to convert some values to Uint8Arrays before passing the credentials to the navigator const publicKey = { ...optionsJSON, challenge: (0, base64URLStringToBuffer_js_1.base64URLStringToBuffer)(optionsJSON.challenge), user: { ...optionsJSON.user, id: (0, base64URLStringToBuffer_js_1.base64URLStringToBuffer)(optionsJSON.user.id), }, excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor_js_1.toPublicKeyCredentialDescriptor), }; // Prepare options for `.create()` const createOptions = {}; /** * Try to use conditional create to register a passkey for the user with the password manager * the user just used to authenticate with. The user won't be shown any prominent UI by the * browser. */ if (useAutoRegister) { // @ts-ignore: `mediation` doesn't yet exist on CredentialCreationOptions but it's possible as of Sept 2024 createOptions.mediation = 'conditional'; } // Finalize options createOptions.publicKey = publicKey; // Set up the ability to cancel this request if the user attempts another createOptions.signal = webAuthnAbortService_js_1.WebAuthnAbortService.createNewAbortSignal(); // Wait for the user to complete attestation let credential; try { credential = (await navigator.credentials.create(createOptions)); } catch (err) { throw (0, identifyRegistrationError_js_1.identifyRegistrationError)({ error: err, options: createOptions }); } if (!credential) { throw new Error('Registration was not completed'); } const { id, rawId, response, type } = credential; // Continue to play it safe with `getTransports()` for now, even when L3 types say it's required let transports = undefined; if (typeof response.getTransports === 'function') { transports = response.getTransports(); } // L3 says this is required, but browser and webview support are still not guaranteed. let responsePublicKeyAlgorithm = undefined; if (typeof response.getPublicKeyAlgorithm === 'function') { try { responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm(); } catch (error) { warnOnBrokenImplementation('getPublicKeyAlgorithm()', error); } } let responsePublicKey = undefined; if (typeof response.getPublicKey === 'function') { try { const _publicKey = response.getPublicKey(); if (_publicKey !== null) { responsePublicKey = (0, bufferToBase64URLString_js_1.bufferToBase64URLString)(_publicKey); } } catch (error) { warnOnBrokenImplementation('getPublicKey()', error); } } // L3 says this is required, but browser and webview support are still not guaranteed. let responseAuthenticatorData; if (typeof response.getAuthenticatorData === 'function') { try { responseAuthenticatorData = (0, bufferToBase64URLString_js_1.bufferToBase64URLString)(response.getAuthenticatorData()); } catch (error) { warnOnBrokenImplementation('getAuthenticatorData()', error); } } return { id, rawId: (0, bufferToBase64URLString_js_1.bufferToBase64URLString)(rawId), response: { attestationObject: (0, bufferToBase64URLString_js_1.bufferToBase64URLString)(response.attestationObject), clientDataJSON: (0, bufferToBase64URLString_js_1.bufferToBase64URLString)(response.clientDataJSON), transports, publicKeyAlgorithm: responsePublicKeyAlgorithm, publicKey: responsePublicKey, authenticatorData: responseAuthenticatorData, }, type, clientExtensionResults: credential.getClientExtensionResults(), authenticatorAttachment: (0, toAuthenticatorAttachment_js_1.toAuthenticatorAttachment)(credential.authenticatorAttachment), }; } /** * Visibly warn when we detect an issue related to a passkey provider intercepting WebAuthn API * calls */ function warnOnBrokenImplementation(methodName, cause) { console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.\n`, cause); }