{"version":3,"file":"HeadlessWebAuthnClient.mjs","sources":["../../../../../../../core/src/HeadlessClients/HeadlessWebAuthnClient.ts"],"sourcesContent":["import * as webauthnJson from '@github/webauthn-json';\n\nimport { IConsumerSubscriptionService, IDFPProtectedAuthProvider, INetworkClient } from '..';\nimport {\n  IHeadlessWebAuthnClient,\n  StytchProjectConfigurationInput,\n  WebAuthnAuthenticateResponse,\n  WebAuthnAuthenticateStartOptions,\n  WebAuthnAuthenticateStartResponse,\n  WebAuthnRegisterResponse,\n  WebAuthnRegisterStartOptions,\n  WebAuthnRegisterStartResponse,\n  WebAuthnUpdateOptions,\n  WebAuthnUpdateResponse,\n} from '../public';\nimport { logger, omitUser, WithUser } from '../utils';\nimport { validateInDev } from '../utils/dev';\n\nexport class HeadlessWebAuthnClient<TProjectConfiguration extends StytchProjectConfigurationInput>\n  implements IHeadlessWebAuthnClient<TProjectConfiguration>\n{\n  register: (options: WebAuthnRegisterStartOptions) => Promise<WebAuthnRegisterResponse<TProjectConfiguration>>;\n\n  authenticate: (\n    options: WebAuthnAuthenticateStartOptions,\n  ) => Promise<WebAuthnAuthenticateResponse<TProjectConfiguration> | null>;\n\n  constructor(\n    public _networkClient: INetworkClient,\n    private _subscriptionService: IConsumerSubscriptionService<TProjectConfiguration>,\n    private dfpProtectedAuth: IDFPProtectedAuthProvider,\n  ) {\n    this.register = this._subscriptionService.withUpdateSession(\n      async (options?: WebAuthnRegisterStartOptions): Promise<WebAuthnRegisterResponse<TProjectConfiguration>> => {\n        if (options) {\n          validateInDev('stytch.webauthn.register', options, {\n            domain: 'optionalString',\n            authenticator_type: 'optionalString',\n            is_passkey: 'optionalBoolean',\n            session_duration_minutes: 'number',\n            override_id: 'optionalString',\n            override_name: 'optionalString',\n            override_display_name: 'optionalString',\n            use_base64_url_encoding: 'optionalBoolean',\n          });\n        }\n\n        const startResp = await this._networkClient.fetchSDK<WebAuthnRegisterStartResponse>({\n          url: '/webauthn/register/start',\n          method: 'POST',\n          body: {\n            domain: options?.domain ?? window.location.hostname,\n            authenticator_type: options?.authenticator_type ?? undefined,\n            return_passkey_credential_options: options?.is_passkey,\n            override_id: options?.override_id,\n            override_name: options?.override_name,\n            override_display_name: options?.override_display_name,\n            user_agent: navigator.userAgent,\n            use_base64_url_encoding: options?.use_base64_url_encoding,\n          },\n        });\n\n        const publicKeyCredentialCreationOptions = startResp.public_key_credential_creation_options;\n        const publicKey = JSON.parse(publicKeyCredentialCreationOptions);\n\n        const credential = await webauthnJson.create({\n          publicKey: publicKey,\n        });\n\n        const resp = await this._networkClient.fetchSDK<WithUser<WebAuthnRegisterResponse<TProjectConfiguration>>>({\n          url: '/webauthn/register',\n          method: 'POST',\n          body: {\n            public_key_credential: JSON.stringify(credential),\n            session_duration_minutes: options?.session_duration_minutes,\n          },\n        });\n\n        return omitUser(resp);\n      },\n    );\n\n    this.authenticate = this._subscriptionService.withUpdateSession(\n      async (\n        options: WebAuthnAuthenticateStartOptions,\n      ): Promise<WebAuthnAuthenticateResponse<TProjectConfiguration> | null> => {\n        validateInDev('stytch.webauthn.authenticate', options, {\n          domain: 'optionalString',\n          session_duration_minutes: 'number',\n          is_passkey: 'optionalBoolean',\n          signal: 'optionalObject',\n          conditional_mediation: 'optionalBoolean',\n          disable_input_check: 'optionalBoolean',\n        });\n        const { dfp_telemetry_id, captcha_token } = await this.dfpProtectedAuth.getDFPTelemetryIDAndCaptcha();\n\n        if (options.conditional_mediation) {\n          if (!(await this.browserSupportsAutofill())) {\n            logger.error('Browser does not support WebAuthn autofill');\n            return null;\n          }\n\n          if (!options.disable_input_check && !this.checkEligibleInputs()) {\n            return null;\n          }\n        }\n\n        const isLoggedIn = !!this._subscriptionService.getSession();\n\n        const endpoint = isLoggedIn ? '/webauthn/authenticate/start/secondary' : '/webauthn/authenticate/start/primary';\n\n        const startResp = await this._networkClient.fetchSDK<WebAuthnAuthenticateStartResponse>({\n          url: endpoint,\n          method: 'POST',\n          body: {\n            domain: options.domain ?? window.location.hostname,\n            return_passkey_credential_options: options?.is_passkey,\n          },\n        });\n\n        const publicKeyCredentialRequestOptions = startResp.public_key_credential_request_options;\n        const abortController = new AbortController();\n        const credReqOptions = {\n          publicKey: JSON.parse(publicKeyCredentialRequestOptions),\n          signal: options.signal ?? abortController.signal,\n        };\n        const conditionalMediationCredReqOption = {\n          ...credReqOptions,\n          mediation: 'conditional' as CredentialMediationRequirement,\n        };\n        const credential = await webauthnJson.get(\n          options.conditional_mediation ? conditionalMediationCredReqOption : credReqOptions,\n        );\n\n        const authenticationData = await this._networkClient.retriableFetchSDK<\n          WithUser<WebAuthnAuthenticateResponse<TProjectConfiguration>>\n        >({\n          url: '/webauthn/authenticate',\n          method: 'POST',\n          body: {\n            public_key_credential: JSON.stringify(credential),\n            session_duration_minutes: options.session_duration_minutes,\n            dfp_telemetry_id,\n            captcha_token,\n          },\n          retryCallback: this.dfpProtectedAuth.retryWithCaptchaAndDFP,\n        });\n\n        return omitUser(authenticationData);\n      },\n    );\n  }\n\n  async update(options: WebAuthnUpdateOptions): Promise<WebAuthnUpdateResponse> {\n    validateInDev('stytch.webauthn.update', options, {\n      webauthn_registration_id: 'string',\n      name: 'string',\n    });\n\n    const url = '/webauthn/update/' + options.webauthn_registration_id;\n    return await this._networkClient.fetchSDK<WebAuthnUpdateResponse>({\n      url: url,\n      method: 'PUT',\n      body: {\n        name: options.name,\n      },\n    });\n  }\n\n  async browserSupportsAutofill(): Promise<boolean> {\n    return (await window.PublicKeyCredential?.isConditionalMediationAvailable?.()) ?? false;\n  }\n\n  private checkEligibleInputs = (): boolean => {\n    // Check for an <input> with \"webauthn\" in its `autocomplete` attribute\n    const eligibleInputs = document.querySelectorAll(\"input[autocomplete*='webauthn']\");\n    // WebAuthn autofill requires at least one valid input\n    if (eligibleInputs.length < 1) {\n      logger.error('No <input> with `\"webauthn\"` in its `autocomplete` attribute was detected');\n      return false;\n    }\n    return true;\n  };\n}\n"],"names":["HeadlessWebAuthnClient","register","authenticate","_networkClient","_subscriptionService","dfpProtectedAuth","withUpdateSession","options","startResp","fetchSDK","url","method","body","domain","window","location","hostname","authenticator_type","undefined","return_passkey_credential_options","is_passkey","override_id","override_name","override_display_name","user_agent","navigator","userAgent","use_base64_url_encoding","publicKeyCredentialCreationOptions","public_key_credential_creation_options","publicKey","JSON","parse","credential","webauthnJson","resp","public_key_credential","stringify","session_duration_minutes","omitUser","dfp_telemetry_id","captcha_token","getDFPTelemetryIDAndCaptcha","conditional_mediation","browserSupportsAutofill","logger","error","disable_input_check","checkEligibleInputs","isLoggedIn","getSession","endpoint","publicKeyCredentialRequestOptions","public_key_credential_request_options","abortController","AbortController","credReqOptions","signal","conditionalMediationCredReqOption","mediation","authenticationData","retriableFetchSDK","retryCallback","retryWithCaptchaAndDFP","update","webauthn_registration_id","name","PublicKeyCredential","isConditionalMediationAvailable","eligibleInputs","document","querySelectorAll","length"],"mappings":"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"}