@@ -71,19 +71,19 @@ declare global {
7171 * }
7272 * ```
7373 *
74- * Conditional UI (Autofill) Support:
74+ * Conditional mediation (Autofill) Support:
7575 *
7676 * ```js
77- * // Check if browser supports conditional UI
77+ * // Check if browser supports conditional mediation
7878 * const supportsConditionalUI = await FRWebAuthn.isConditionalUISupported();
7979 *
8080 * if (supportsConditionalUI) {
81- * // The authenticate() method automatically handles conditional UI
81+ * // The authenticate() method automatically handles conditional mediation
8282 * // when the server indicates support via conditionalWebAuthn: true
8383 * // in the metadata. No additional code changes needed.
8484 * await FRWebAuthn.authenticate(step);
8585 *
86- * // For conditional UI to work in the browser, add autocomplete="webauthn"
86+ * // For conditional mediation to work in the browser, add autocomplete="webauthn"
8787 * // to your username input field:
8888 * // <input type="text" name="username" autocomplete="webauthn" />
8989 * }
@@ -123,12 +123,21 @@ abstract class FRWebAuthn {
123123 }
124124
125125 /**
126- * Checks if the browser supports conditional UI (autofill) for WebAuthn.
126+ * Checks if the browser supports WebAuthn.
127+ *
128+ * @return boolean indicating if WebAuthn is available
129+ */
130+ public static isWebAuthnSupported ( ) : boolean {
131+ return ! ! window . PublicKeyCredential ;
132+ }
133+
134+ /**
135+ * Checks if the browser supports conditional mediation (autofill) for WebAuthn.
127136 *
128137 * @return Promise<boolean> indicating if conditional mediation is available
129138 */
130- public static async isConditionalUISupported ( ) : Promise < boolean > {
131- if ( ! window . PublicKeyCredential ) {
139+ public static async isConditionalMediationSupported ( ) : Promise < boolean > {
140+ if ( ! this . isWebAuthnSupported ( ) ) {
132141 return false ;
133142 }
134143
@@ -144,52 +153,49 @@ abstract class FRWebAuthn {
144153
145154 /**
146155 * Populates the step with the necessary authentication outcome.
147- * Automatically handles conditional UI if indicated by the server metadata.
156+ * Automatically handles conditional mediation if indicated by the server metadata.
148157 *
149158 * @param step The step that contains WebAuthn authentication data
159+ * @param optionsTransformer Augments the derived options with custom behaviour
150160 * @return The populated step
151161 */
152- public static async authenticate ( step : FRStep ) : Promise < FRStep > {
162+ public static async authenticate (
163+ step : FRStep ,
164+ optionsTransformer : ( options : CredentialRequestOptions ) => CredentialRequestOptions = (
165+ options ,
166+ ) => options ,
167+ ) : Promise < FRStep > {
153168 const { hiddenCallback, metadataCallback, textOutputCallback } = this . getCallbacks ( step ) ;
154169 if ( hiddenCallback && ( metadataCallback || textOutputCallback ) ) {
155- let outcome : ReturnType < typeof this . getAuthenticationOutcome > ;
156- let credential : PublicKeyCredential | null = null ;
170+ const options : CredentialRequestOptions = { } ;
157171
158172 try {
159- let publicKey : PublicKeyCredentialRequestOptions ;
160-
161173 if ( metadataCallback ) {
162174 const meta = metadataCallback . getOutputValue ( 'data' ) as WebAuthnAuthenticationMetadata ;
163175 const mediation = meta . mediation as CredentialMediationRequirement ;
164176
165177 if ( mediation === 'conditional' ) {
166- const isConditionalSupported = await this . isConditionalUISupported ( ) ;
167- if ( ! isConditionalSupported ) {
178+ const isConditionalMediationSupported = await this . isConditionalMediationSupported ( ) ;
179+ if ( ! isConditionalMediationSupported ) {
168180 const e = new Error (
169- 'Conditional UI was requested, but is not supported by this browser.' ,
181+ 'Conditional mediation was requested, but is not supported by this browser.' ,
170182 ) ;
171183 e . name = WebAuthnOutcomeType . NotSupportedError ;
172184 throw e ;
173185 }
174186 }
175187
176- publicKey = this . createAuthenticationPublicKey ( meta ) ;
177-
178- credential = await this . getAuthenticationCredential ( { publicKey, mediation } ) ;
179- outcome = this . getAuthenticationOutcome ( credential ) ;
188+ options . publicKey = this . createAuthenticationPublicKey ( meta ) ;
189+ options . mediation = mediation ;
180190 } else if ( textOutputCallback ) {
181191 const metadata = this . extractMetadata ( textOutputCallback . getMessage ( ) ) ;
182-
183192 if ( metadata ) {
184- publicKey = this . createAuthenticationPublicKey (
193+ options . publicKey = this . createAuthenticationPublicKey (
185194 metadata as WebAuthnAuthenticationMetadata ,
186195 ) ;
187196 } else {
188- publicKey = parseWebAuthnAuthenticateText ( textOutputCallback . getMessage ( ) ) ;
197+ options . publicKey = parseWebAuthnAuthenticateText ( textOutputCallback . getMessage ( ) ) ;
189198 }
190-
191- credential = await this . getAuthenticationCredential ( { publicKey } ) ;
192- outcome = this . getAuthenticationOutcome ( credential ) ;
193199 } else {
194200 throw new Error ( 'No Credential found from Public Key' ) ;
195201 }
@@ -204,6 +210,12 @@ abstract class FRWebAuthn {
204210 throw error ;
205211 }
206212
213+ const credential : PublicKeyCredential | null = await this . getAuthenticationCredential (
214+ optionsTransformer ( options ) ,
215+ ) ;
216+ const outcome : ReturnType < typeof this . getAuthenticationOutcome > =
217+ this . getAuthenticationOutcome ( credential ) ;
218+
207219 if ( metadataCallback ) {
208220 const meta = metadataCallback . getOutputValue ( 'data' ) as WebAuthnAuthenticationMetadata ;
209221 if ( meta ?. supportsJsonResponse && credential && 'authenticatorAttachment' in credential ) {
@@ -379,7 +391,7 @@ abstract class FRWebAuthn {
379391 options : CredentialRequestOptions ,
380392 ) : Promise < PublicKeyCredential | null > {
381393 // Feature check before we attempt authenticating
382- if ( ! window . PublicKeyCredential ) {
394+ if ( ! this . isWebAuthnSupported ( ) ) {
383395 const e = new Error ( 'PublicKeyCredential not supported by this browser' ) ;
384396 e . name = WebAuthnOutcomeType . NotSupportedError ;
385397 throw e ;
@@ -457,7 +469,7 @@ abstract class FRWebAuthn {
457469 options : PublicKeyCredentialCreationOptions ,
458470 ) : Promise < PublicKeyCredential | null > {
459471 // Feature check before we attempt registering a device
460- if ( ! window . PublicKeyCredential ) {
472+ if ( this . isWebAuthnSupported ( ) ) {
461473 const e = new Error ( 'PublicKeyCredential not supported by this browser' ) ;
462474 e . name = WebAuthnOutcomeType . NotSupportedError ;
463475 throw e ;
@@ -534,7 +546,7 @@ abstract class FRWebAuthn {
534546 challenge : Uint8Array . from ( atob ( challenge ) , ( c ) => c . charCodeAt ( 0 ) ) . buffer ,
535547 timeout,
536548 } ;
537- // For conditional UI , allowCredentials can be omitted.
549+ // For conditional mediation , allowCredentials can be omitted.
538550 // For standard WebAuthn, it may or may not be present.
539551 // Only add the property if the array is not empty.
540552 if ( allowCredentialsValue && allowCredentialsValue . length > 0 ) {
0 commit comments