UNPKG

@supabase/auth-js

Version:
689 lines 34 kB
import { __rest } from "tslib"; import { base64UrlToUint8Array, bytesToBase64URL } from './base64url'; import { AuthError, AuthUnknownError, isAuthError } from './errors'; import { isBrowser } from './helpers'; import { identifyAuthenticationError, identifyRegistrationError, isWebAuthnError, WebAuthnError, WebAuthnUnknownError, } from './webauthn.errors'; export { WebAuthnError, isWebAuthnError, identifyRegistrationError, identifyAuthenticationError }; /** * WebAuthn abort service to manage ceremony cancellation. * Ensures only one WebAuthn ceremony is active at a time to prevent "operation already in progress" errors. * * @experimental This class is experimental and may change in future releases * @see {@link https://w3c.github.io/webauthn/#sctn-automation-webdriver-capability W3C WebAuthn Spec - Aborting Ceremonies} */ export class WebAuthnAbortService { /** * Create an abort signal for a new WebAuthn operation. * Automatically cancels any existing operation. * * @returns {AbortSignal} Signal to pass to navigator.credentials.create() or .get() * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal MDN - AbortSignal} */ createNewAbortSignal() { // Abort any existing calls to navigator.credentials.create() or navigator.credentials.get() if (this.controller) { const abortError = new Error('Cancelling existing WebAuthn API call for new one'); abortError.name = 'AbortError'; this.controller.abort(abortError); } const newController = new AbortController(); this.controller = newController; return newController.signal; } /** * Manually cancel the current WebAuthn operation. * Useful for cleaning up when user cancels or navigates away. * * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort MDN - AbortController.abort} */ cancelCeremony() { if (this.controller) { const abortError = new Error('Manually cancelling existing WebAuthn API call'); abortError.name = 'AbortError'; this.controller.abort(abortError); this.controller = undefined; } } } /** * Singleton instance to ensure only one WebAuthn ceremony is active at a time. * This prevents "operation already in progress" errors when retrying WebAuthn operations. * * @experimental This instance is experimental and may change in future releases */ export const webAuthnAbortService = new WebAuthnAbortService(); /** * Convert base64url encoded strings in WebAuthn credential creation options to ArrayBuffers * as required by the WebAuthn browser API. * Supports both native WebAuthn Level 3 parseCreationOptionsFromJSON and manual fallback. * * @param {ServerCredentialCreationOptions} options - JSON options from server with base64url encoded fields * @returns {PublicKeyCredentialCreationOptionsFuture} Options ready for navigator.credentials.create() * @see {@link https://w3c.github.io/webauthn/#sctn-parseCreationOptionsFromJSON W3C WebAuthn Spec - parseCreationOptionsFromJSON} */ export function deserializeCredentialCreationOptions(options) { if (!options) { throw new Error('Credential creation options are required'); } // Check if the native parseCreationOptionsFromJSON method is available if (typeof PublicKeyCredential !== 'undefined' && 'parseCreationOptionsFromJSON' in PublicKeyCredential && typeof PublicKeyCredential .parseCreationOptionsFromJSON === 'function') { // Use the native WebAuthn Level 3 method return PublicKeyCredential.parseCreationOptionsFromJSON( /** we assert the options here as typescript still doesn't know about future webauthn types */ options); } // Fallback to manual parsing for browsers that don't support the native method // Destructure to separate fields that need transformation const { challenge: challengeStr, user: userOpts, excludeCredentials } = options, restOptions = __rest(options // Convert challenge from base64url to ArrayBuffer , ["challenge", "user", "excludeCredentials"]); // Convert challenge from base64url to ArrayBuffer const challenge = base64UrlToUint8Array(challengeStr).buffer; // Convert user.id from base64url to ArrayBuffer const user = Object.assign(Object.assign({}, userOpts), { id: base64UrlToUint8Array(userOpts.id).buffer }); // Build the result object const result = Object.assign(Object.assign({}, restOptions), { challenge, user }); // Only add excludeCredentials if it exists if (excludeCredentials && excludeCredentials.length > 0) { result.excludeCredentials = new Array(excludeCredentials.length); for (let i = 0; i < excludeCredentials.length; i++) { const cred = excludeCredentials[i]; result.excludeCredentials[i] = Object.assign(Object.assign({}, cred), { id: base64UrlToUint8Array(cred.id).buffer, type: cred.type || 'public-key', // Cast transports to handle future transport types like "cable" transports: cred.transports }); } } return result; } /** * Convert base64url encoded strings in WebAuthn credential request options to ArrayBuffers * as required by the WebAuthn browser API. * Supports both native WebAuthn Level 3 parseRequestOptionsFromJSON and manual fallback. * * @param {ServerCredentialRequestOptions} options - JSON options from server with base64url encoded fields * @returns {PublicKeyCredentialRequestOptionsFuture} Options ready for navigator.credentials.get() * @see {@link https://w3c.github.io/webauthn/#sctn-parseRequestOptionsFromJSON W3C WebAuthn Spec - parseRequestOptionsFromJSON} */ export function deserializeCredentialRequestOptions(options) { if (!options) { throw new Error('Credential request options are required'); } // Check if the native parseRequestOptionsFromJSON method is available if (typeof PublicKeyCredential !== 'undefined' && 'parseRequestOptionsFromJSON' in PublicKeyCredential && typeof PublicKeyCredential .parseRequestOptionsFromJSON === 'function') { // Use the native WebAuthn Level 3 method return PublicKeyCredential.parseRequestOptionsFromJSON(options); } // Fallback to manual parsing for browsers that don't support the native method // Destructure to separate fields that need transformation const { challenge: challengeStr, allowCredentials } = options, restOptions = __rest(options // Convert challenge from base64url to ArrayBuffer , ["challenge", "allowCredentials"]); // Convert challenge from base64url to ArrayBuffer const challenge = base64UrlToUint8Array(challengeStr).buffer; // Build the result object const result = Object.assign(Object.assign({}, restOptions), { challenge }); // Only add allowCredentials if it exists if (allowCredentials && allowCredentials.length > 0) { result.allowCredentials = new Array(allowCredentials.length); for (let i = 0; i < allowCredentials.length; i++) { const cred = allowCredentials[i]; result.allowCredentials[i] = Object.assign(Object.assign({}, cred), { id: base64UrlToUint8Array(cred.id).buffer, type: cred.type || 'public-key', // Cast transports to handle future transport types like "cable" transports: cred.transports }); } } return result; } /** * Convert a registration/enrollment credential response to server format. * Serializes binary fields to base64url for JSON transmission. * Supports both native WebAuthn Level 3 toJSON and manual fallback. * * @param {RegistrationCredential} credential - Credential from navigator.credentials.create() * @returns {RegistrationResponseJSON} JSON-serializable credential for server * @see {@link https://w3c.github.io/webauthn/#dom-publickeycredential-tojson W3C WebAuthn Spec - toJSON} */ export function serializeCredentialCreationResponse(credential) { var _a; // Check if the credential instance has the toJSON method if ('toJSON' in credential && typeof credential.toJSON === 'function') { // Use the native WebAuthn Level 3 method return credential.toJSON(); } const credentialWithAttachment = credential; return { id: credential.id, rawId: credential.id, response: { attestationObject: bytesToBase64URL(new Uint8Array(credential.response.attestationObject)), clientDataJSON: bytesToBase64URL(new Uint8Array(credential.response.clientDataJSON)), }, type: 'public-key', clientExtensionResults: credential.getClientExtensionResults(), // Convert null to undefined and cast to AuthenticatorAttachment type authenticatorAttachment: ((_a = credentialWithAttachment.authenticatorAttachment) !== null && _a !== void 0 ? _a : undefined), }; } /** * Convert an authentication/verification credential response to server format. * Serializes binary fields to base64url for JSON transmission. * Supports both native WebAuthn Level 3 toJSON and manual fallback. * * @param {AuthenticationCredential} credential - Credential from navigator.credentials.get() * @returns {AuthenticationResponseJSON} JSON-serializable credential for server * @see {@link https://w3c.github.io/webauthn/#dom-publickeycredential-tojson W3C WebAuthn Spec - toJSON} */ export function serializeCredentialRequestResponse(credential) { var _a; // Check if the credential instance has the toJSON method if ('toJSON' in credential && typeof credential.toJSON === 'function') { // Use the native WebAuthn Level 3 method return credential.toJSON(); } // Fallback to manual conversion for browsers that don't support toJSON // Access authenticatorAttachment via type assertion to handle TypeScript version differences // @simplewebauthn/types includes this property but base TypeScript 4.7.4 doesn't const credentialWithAttachment = credential; const clientExtensionResults = credential.getClientExtensionResults(); const assertionResponse = credential.response; return { id: credential.id, rawId: credential.id, // W3C spec expects rawId to match id for JSON format response: { authenticatorData: bytesToBase64URL(new Uint8Array(assertionResponse.authenticatorData)), clientDataJSON: bytesToBase64URL(new Uint8Array(assertionResponse.clientDataJSON)), signature: bytesToBase64URL(new Uint8Array(assertionResponse.signature)), userHandle: assertionResponse.userHandle ? bytesToBase64URL(new Uint8Array(assertionResponse.userHandle)) : undefined, }, type: 'public-key', clientExtensionResults, // Convert null to undefined and cast to AuthenticatorAttachment type authenticatorAttachment: ((_a = credentialWithAttachment.authenticatorAttachment) !== null && _a !== void 0 ? _a : undefined), }; } /** * A simple test to determine if a hostname is a properly-formatted domain name. * Considers localhost valid for development environments. * * A "valid domain" is defined here: https://url.spec.whatwg.org/#valid-domain * * Regex sourced from here: * https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch08s15.html * * @param {string} hostname - The hostname to validate * @returns {boolean} True if valid domain or localhost * @see {@link https://url.spec.whatwg.org/#valid-domain WHATWG URL Spec - Valid Domain} */ export function isValidDomain(hostname) { return ( // Consider localhost valid as well since it's okay wrt Secure Contexts hostname === 'localhost' || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname)); } /** * Determine if the browser is capable of WebAuthn. * Checks for necessary Web APIs: PublicKeyCredential and Credential Management. * * @returns {boolean} True if browser supports WebAuthn * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential#browser_compatibility MDN - PublicKeyCredential Browser Compatibility} */ function browserSupportsWebAuthn() { var _a, _b; return !!(isBrowser() && 'PublicKeyCredential' in window && window.PublicKeyCredential && 'credentials' in navigator && typeof ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.credentials) === null || _a === void 0 ? void 0 : _a.create) === 'function' && typeof ((_b = navigator === null || navigator === void 0 ? void 0 : navigator.credentials) === null || _b === void 0 ? void 0 : _b.get) === 'function'); } /** * Create a WebAuthn credential using the browser's credentials API. * Wraps navigator.credentials.create() with error handling. * * @param {CredentialCreationOptions} options - Options including publicKey parameters * @returns {Promise<RequestResult<RegistrationCredential, WebAuthnError>>} Created credential or error * @see {@link https://w3c.github.io/webauthn/#sctn-createCredential W3C WebAuthn Spec - Create Credential} * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create MDN - credentials.create} */ export async function createCredential(options) { try { const response = await navigator.credentials.create( /** we assert the type here until typescript types are updated */ options); if (!response) { return { data: null, error: new WebAuthnUnknownError('Empty credential response', response), }; } if (!(response instanceof PublicKeyCredential)) { return { data: null, error: new WebAuthnUnknownError('Browser returned unexpected credential type', response), }; } return { data: response, error: null }; } catch (err) { return { data: null, error: identifyRegistrationError({ error: err, options, }), }; } } /** * Get a WebAuthn credential using the browser's credentials API. * Wraps navigator.credentials.get() with error handling. * * @param {CredentialRequestOptions} options - Options including publicKey parameters * @returns {Promise<RequestResult<AuthenticationCredential, WebAuthnError>>} Retrieved credential or error * @see {@link https://w3c.github.io/webauthn/#sctn-getAssertion W3C WebAuthn Spec - Get Assertion} * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get MDN - credentials.get} */ export async function getCredential(options) { try { const response = await navigator.credentials.get( /** we assert the type here until typescript types are updated */ options); if (!response) { return { data: null, error: new WebAuthnUnknownError('Empty credential response', response), }; } if (!(response instanceof PublicKeyCredential)) { return { data: null, error: new WebAuthnUnknownError('Browser returned unexpected credential type', response), }; } return { data: response, error: null }; } catch (err) { return { data: null, error: identifyAuthenticationError({ error: err, options, }), }; } } export const DEFAULT_CREATION_OPTIONS = { hints: ['security-key'], authenticatorSelection: { authenticatorAttachment: 'cross-platform', requireResidentKey: false, /** set to preferred because older yubikeys don't have PIN/Biometric */ userVerification: 'preferred', residentKey: 'discouraged', }, attestation: 'direct', }; export const DEFAULT_REQUEST_OPTIONS = { /** set to preferred because older yubikeys don't have PIN/Biometric */ userVerification: 'preferred', hints: ['security-key'], attestation: 'direct', }; function deepMerge(...sources) { const isObject = (val) => val !== null && typeof val === 'object' && !Array.isArray(val); const isArrayBufferLike = (val) => val instanceof ArrayBuffer || ArrayBuffer.isView(val); const result = {}; for (const source of sources) { if (!source) continue; for (const key in source) { const value = source[key]; if (value === undefined) continue; if (Array.isArray(value)) { // preserve array reference, including unions like AuthenticatorTransport[] result[key] = value; } else if (isArrayBufferLike(value)) { result[key] = value; } else if (isObject(value)) { const existing = result[key]; if (isObject(existing)) { result[key] = deepMerge(existing, value); } else { result[key] = deepMerge(value); } } else { result[key] = value; } } } return result; } /** * Merges WebAuthn credential creation options with overrides. * Sets sensible defaults for authenticator selection and extensions. * * @param {PublicKeyCredentialCreationOptionsFuture} baseOptions - The base options from the server * @param {PublicKeyCredentialCreationOptionsFuture} overrides - Optional overrides to apply * @param {string} friendlyName - Optional friendly name for the credential * @returns {PublicKeyCredentialCreationOptionsFuture} Merged credential creation options * @see {@link https://w3c.github.io/webauthn/#dictdef-authenticatorselectioncriteria W3C WebAuthn Spec - AuthenticatorSelectionCriteria} */ export function mergeCredentialCreationOptions(baseOptions, overrides) { return deepMerge(DEFAULT_CREATION_OPTIONS, baseOptions, overrides || {}); } /** * Merges WebAuthn credential request options with overrides. * Sets sensible defaults for user verification and hints. * * @param {PublicKeyCredentialRequestOptionsFuture} baseOptions - The base options from the server * @param {PublicKeyCredentialRequestOptionsFuture} overrides - Optional overrides to apply * @returns {PublicKeyCredentialRequestOptionsFuture} Merged credential request options * @see {@link https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptions W3C WebAuthn Spec - PublicKeyCredentialRequestOptions} */ export function mergeCredentialRequestOptions(baseOptions, overrides) { return deepMerge(DEFAULT_REQUEST_OPTIONS, baseOptions, overrides || {}); } /** * WebAuthn API wrapper for Supabase Auth. * Provides methods for enrolling, challenging, verifying, authenticating, and registering WebAuthn credentials. * * @experimental This API is experimental and may change in future releases * @see {@link https://w3c.github.io/webauthn/ W3C WebAuthn Specification} * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API MDN - Web Authentication API} */ export class WebAuthnApi { constructor(client) { this.client = client; // Bind all methods so they can be destructured this.enroll = this._enroll.bind(this); this.challenge = this._challenge.bind(this); this.verify = this._verify.bind(this); this.authenticate = this._authenticate.bind(this); this.register = this._register.bind(this); } /** * Enroll a new WebAuthn factor. * Creates an unverified WebAuthn factor that must be verified with a credential. * * @experimental This method is experimental and may change in future releases * @param {Omit<MFAEnrollWebauthnParams, 'factorType'>} params - Enrollment parameters (friendlyName required) * @returns {Promise<AuthMFAEnrollWebauthnResponse>} Enrolled factor details or error * @see {@link https://w3c.github.io/webauthn/#sctn-registering-a-new-credential W3C WebAuthn Spec - Registering a New Credential} */ async _enroll(params) { return this.client.mfa.enroll(Object.assign(Object.assign({}, params), { factorType: 'webauthn' })); } /** * Challenge for WebAuthn credential creation or authentication. * Combines server challenge with browser credential operations. * Handles both registration (create) and authentication (request) flows. * * @experimental This method is experimental and may change in future releases * @param {MFAChallengeWebauthnParams & { friendlyName?: string; signal?: AbortSignal }} params - Challenge parameters including factorId * @param {Object} overrides - Allows you to override the parameters passed to navigator.credentials * @param {PublicKeyCredentialCreationOptionsFuture} overrides.create - Override options for credential creation * @param {PublicKeyCredentialRequestOptionsFuture} overrides.request - Override options for credential request * @returns {Promise<RequestResult>} Challenge response with credential or error * @see {@link https://w3c.github.io/webauthn/#sctn-credential-creation W3C WebAuthn Spec - Credential Creation} * @see {@link https://w3c.github.io/webauthn/#sctn-verifying-assertion W3C WebAuthn Spec - Verifying Assertion} */ async _challenge({ factorId, webauthn, friendlyName, signal, }, overrides) { var _a; try { // Get challenge from server using the client's MFA methods const { data: challengeResponse, error: challengeError } = await this.client.mfa.challenge({ factorId, webauthn, }); if (!challengeResponse) { return { data: null, error: challengeError }; } const abortSignal = signal !== null && signal !== void 0 ? signal : webAuthnAbortService.createNewAbortSignal(); /** webauthn will fail if either of the name/displayname are blank */ if (challengeResponse.webauthn.type === 'create') { const { user } = challengeResponse.webauthn.credential_options.publicKey; if (!user.name) { // Preserve original format: use friendlyName if provided, otherwise fetch fallback // This maintains backward compatibility with the ${user.id}:${name} format const nameToUse = friendlyName; if (!nameToUse) { // Only fetch user data if friendlyName is not provided (bug fix for null friendlyName) const currentUser = await this.client.getUser(); const userData = currentUser.data.user; const fallbackName = ((_a = userData === null || userData === void 0 ? void 0 : userData.user_metadata) === null || _a === void 0 ? void 0 : _a.name) || (userData === null || userData === void 0 ? void 0 : userData.email) || (userData === null || userData === void 0 ? void 0 : userData.id) || 'User'; user.name = `${user.id}:${fallbackName}`; } else { user.name = `${user.id}:${nameToUse}`; } } if (!user.displayName) { user.displayName = user.name; } } switch (challengeResponse.webauthn.type) { case 'create': { const options = mergeCredentialCreationOptions(challengeResponse.webauthn.credential_options.publicKey, overrides === null || overrides === void 0 ? void 0 : overrides.create); const { data, error } = await createCredential({ publicKey: options, signal: abortSignal, }); if (data) { return { data: { factorId, challengeId: challengeResponse.id, webauthn: { type: challengeResponse.webauthn.type, credential_response: data, }, }, error: null, }; } return { data: null, error }; } case 'request': { const options = mergeCredentialRequestOptions(challengeResponse.webauthn.credential_options.publicKey, overrides === null || overrides === void 0 ? void 0 : overrides.request); const { data, error } = await getCredential(Object.assign(Object.assign({}, challengeResponse.webauthn.credential_options), { publicKey: options, signal: abortSignal })); if (data) { return { data: { factorId, challengeId: challengeResponse.id, webauthn: { type: challengeResponse.webauthn.type, credential_response: data, }, }, error: null, }; } return { data: null, error }; } } } catch (error) { if (isAuthError(error)) { return { data: null, error }; } return { data: null, error: new AuthUnknownError('Unexpected error in challenge', error), }; } } /** * Verify a WebAuthn credential with the server. * Completes the WebAuthn ceremony by sending the credential to the server for verification. * * @experimental This method is experimental and may change in future releases * @param {Object} params - Verification parameters * @param {string} params.challengeId - ID of the challenge being verified * @param {string} params.factorId - ID of the WebAuthn factor * @param {MFAVerifyWebauthnParams<T>['webauthn']} params.webauthn - WebAuthn credential response * @returns {Promise<AuthMFAVerifyResponse>} Verification result with session or error * @see {@link https://w3c.github.io/webauthn/#sctn-verifying-assertion W3C WebAuthn Spec - Verifying an Authentication Assertion} * */ async _verify({ challengeId, factorId, webauthn, }) { return this.client.mfa.verify({ factorId, challengeId, webauthn: webauthn, }); } /** * Complete WebAuthn authentication flow. * Performs challenge and verification in a single operation for existing credentials. * * @experimental This method is experimental and may change in future releases * @param {Object} params - Authentication parameters * @param {string} params.factorId - ID of the WebAuthn factor to authenticate with * @param {Object} params.webauthn - WebAuthn configuration * @param {string} params.webauthn.rpId - Relying Party ID (defaults to current hostname) * @param {string[]} params.webauthn.rpOrigins - Allowed origins (defaults to current origin) * @param {AbortSignal} params.webauthn.signal - Optional abort signal * @param {PublicKeyCredentialRequestOptionsFuture} overrides - Override options for navigator.credentials.get * @returns {Promise<RequestResult<AuthMFAVerifyResponseData, WebAuthnError | AuthError>>} Authentication result * @see {@link https://w3c.github.io/webauthn/#sctn-authentication W3C WebAuthn Spec - Authentication Ceremony} * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialRequestOptions MDN - PublicKeyCredentialRequestOptions} */ async _authenticate({ factorId, webauthn: { rpId = typeof window !== 'undefined' ? window.location.hostname : undefined, rpOrigins = typeof window !== 'undefined' ? [window.location.origin] : undefined, signal, } = {}, }, overrides) { if (!rpId) { return { data: null, error: new AuthError('rpId is required for WebAuthn authentication'), }; } try { if (!browserSupportsWebAuthn()) { return { data: null, error: new AuthUnknownError('Browser does not support WebAuthn', null), }; } // Get challenge and credential const { data: challengeResponse, error: challengeError } = await this.challenge({ factorId, webauthn: { rpId, rpOrigins }, signal, }, { request: overrides }); if (!challengeResponse) { return { data: null, error: challengeError }; } const { webauthn } = challengeResponse; // Verify credential return this._verify({ factorId, challengeId: challengeResponse.challengeId, webauthn: { type: webauthn.type, rpId, rpOrigins, credential_response: webauthn.credential_response, }, }); } catch (error) { if (isAuthError(error)) { return { data: null, error }; } return { data: null, error: new AuthUnknownError('Unexpected error in authenticate', error), }; } } /** * Complete WebAuthn registration flow. * Performs enrollment, challenge, and verification in a single operation for new credentials. * * @experimental This method is experimental and may change in future releases * @param {Object} params - Registration parameters * @param {string} params.friendlyName - User-friendly name for the credential * @param {string} params.rpId - Relying Party ID (defaults to current hostname) * @param {string[]} params.rpOrigins - Allowed origins (defaults to current origin) * @param {AbortSignal} params.signal - Optional abort signal * @param {PublicKeyCredentialCreationOptionsFuture} overrides - Override options for navigator.credentials.create * @returns {Promise<RequestResult<AuthMFAVerifyResponseData, WebAuthnError | AuthError>>} Registration result * @see {@link https://w3c.github.io/webauthn/#sctn-registering-a-new-credential W3C WebAuthn Spec - Registration Ceremony} * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions MDN - PublicKeyCredentialCreationOptions} */ async _register({ friendlyName, webauthn: { rpId = typeof window !== 'undefined' ? window.location.hostname : undefined, rpOrigins = typeof window !== 'undefined' ? [window.location.origin] : undefined, signal, } = {}, }, overrides) { if (!rpId) { return { data: null, error: new AuthError('rpId is required for WebAuthn registration'), }; } try { if (!browserSupportsWebAuthn()) { return { data: null, error: new AuthUnknownError('Browser does not support WebAuthn', null), }; } // Enroll factor const { data: factor, error: enrollError } = await this._enroll({ friendlyName, }); if (!factor) { await this.client.mfa .listFactors() .then((factors) => { var _a; return (_a = factors.data) === null || _a === void 0 ? void 0 : _a.all.find((v) => v.factor_type === 'webauthn' && v.friendly_name === friendlyName && v.status !== 'unverified'); }) .then((factor) => (factor ? this.client.mfa.unenroll({ factorId: factor === null || factor === void 0 ? void 0 : factor.id }) : void 0)); return { data: null, error: enrollError }; } // Get challenge and create credential const { data: challengeResponse, error: challengeError } = await this._challenge({ factorId: factor.id, friendlyName: factor.friendly_name, webauthn: { rpId, rpOrigins }, signal, }, { create: overrides, }); if (!challengeResponse) { return { data: null, error: challengeError }; } return this._verify({ factorId: factor.id, challengeId: challengeResponse.challengeId, webauthn: { rpId, rpOrigins, type: challengeResponse.webauthn.type, credential_response: challengeResponse.webauthn.credential_response, }, }); } catch (error) { if (isAuthError(error)) { return { data: null, error }; } return { data: null, error: new AuthUnknownError('Unexpected error in register', error), }; } } } //# sourceMappingURL=webauthn.js.map