"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebAuthnApi = exports.DEFAULT_REQUEST_OPTIONS = exports.DEFAULT_CREATION_OPTIONS = exports.webAuthnAbortService = exports.WebAuthnAbortService = exports.identifyAuthenticationError = exports.identifyRegistrationError = exports.isWebAuthnError = exports.WebAuthnError = void 0;
exports.deserializeCredentialCreationOptions = deserializeCredentialCreationOptions;
exports.deserializeCredentialRequestOptions = deserializeCredentialRequestOptions;
exports.serializeCredentialCreationResponse = serializeCredentialCreationResponse;
exports.serializeCredentialRequestResponse = serializeCredentialRequestResponse;
exports.isValidDomain = isValidDomain;
exports.createCredential = createCredential;
exports.getCredential = getCredential;
exports.mergeCredentialCreationOptions = mergeCredentialCreationOptions;
exports.mergeCredentialRequestOptions = mergeCredentialRequestOptions;
const tslib_1 = require("tslib");
const base64url_1 = require("./base64url");
const errors_1 = require("./errors");
const helpers_1 = require("./helpers");
const webauthn_errors_1 = require("./webauthn.errors");
Object.defineProperty(exports, "identifyAuthenticationError", { enumerable: true, get: function () { return webauthn_errors_1.identifyAuthenticationError; } });
Object.defineProperty(exports, "identifyRegistrationError", { enumerable: true, get: function () { return webauthn_errors_1.identifyRegistrationError; } });
Object.defineProperty(exports, "isWebAuthnError", { enumerable: true, get: function () { return webauthn_errors_1.isWebAuthnError; } });
Object.defineProperty(exports, "WebAuthnError", { enumerable: true, get: function () { return webauthn_errors_1.WebAuthnError; } });
/**
* 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}
*/
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;
}
}
}
exports.WebAuthnAbortService = WebAuthnAbortService;
/**
* 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
*/
exports.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}
*/
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 = tslib_1.__rest(options
// Convert challenge from base64url to ArrayBuffer
, ["challenge", "user", "excludeCredentials"]);
// Convert challenge from base64url to ArrayBuffer
const challenge = (0, base64url_1.base64UrlToUint8Array)(challengeStr).buffer;
// Convert user.id from base64url to ArrayBuffer
const user = Object.assign(Object.assign({}, userOpts), { id: (0, base64url_1.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: (0, base64url_1.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}
*/
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 = tslib_1.__rest(options
// Convert challenge from base64url to ArrayBuffer
, ["challenge", "allowCredentials"]);
// Convert challenge from base64url to ArrayBuffer
const challenge = (0, base64url_1.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: (0, base64url_1.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}
*/
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: (0, base64url_1.bytesToBase64URL)(new Uint8Array(credential.response.attestationObject)),
clientDataJSON: (0, base64url_1.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}
*/
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: (0, base64url_1.bytesToBase64URL)(new Uint8Array(assertionResponse.authenticatorData)),
clientDataJSON: (0, base64url_1.bytesToBase64URL)(new Uint8Array(assertionResponse.clientDataJSON)),
signature: (0, base64url_1.bytesToBase64URL)(new Uint8Array(assertionResponse.signature)),
userHandle: assertionResponse.userHandle
? (0, base64url_1.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}
*/
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 !!((0, helpers_1.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}
*/
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 webauthn_errors_1.WebAuthnUnknownError('Empty credential response', response),
};
}
if (!(response instanceof PublicKeyCredential)) {
return {
data: null,
error: new webauthn_errors_1.WebAuthnUnknownError('Browser returned unexpected credential type', response),
};
}
return { data: response, error: null };
}
catch (err) {
return {
data: null,
error: (0, webauthn_errors_1.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}
*/
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 webauthn_errors_1.WebAuthnUnknownError('Empty credential response', response),
};
}
if (!(response instanceof PublicKeyCredential)) {
return {
data: null,
error: new webauthn_errors_1.WebAuthnUnknownError('Browser returned unexpected credential type', response),
};
}
return { data: response, error: null };
}
catch (err) {
return {
data: null,
error: (0, webauthn_errors_1.identifyAuthenticationError)({
error: err,
options,
}),
};
}
}
exports.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',
};
exports.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}
*/
function mergeCredentialCreationOptions(baseOptions, overrides) {
return deepMerge(exports.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}
*/
function mergeCredentialRequestOptions(baseOptions, overrides) {
return deepMerge(exports.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}
*/
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) {
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 : exports.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) {
user.name = `${user.id}:${friendlyName}`;
}
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 ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
return {
data: null,
error: new errors_1.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 errors_1.AuthError('rpId is required for WebAuthn authentication'),
};
}
try {
if (!browserSupportsWebAuthn()) {
return {
data: null,
error: new errors_1.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 ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
return {
data: null,
error: new errors_1.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 errors_1.AuthError('rpId is required for WebAuthn registration'),
};
}
try {
if (!browserSupportsWebAuthn()) {
return {
data: null,
error: new errors_1.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 ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
return {
data: null,
error: new errors_1.AuthUnknownError('Unexpected error in register', error),
};
}
}
}
exports.WebAuthnApi = WebAuthnApi;
//# sourceMappingURL=webauthn.js.map