aad_b2c_webview icon indicating copy to clipboard operation
aad_b2c_webview copied to clipboard

Add Native Support for Phone/OTP Login in Custom UI

Open smithemely opened this issue 1 month ago • 0 comments

The AADB2CBase.custom widget provides an excellent developer experience for creating native email/password authentication flows. However, this simplicity does not extend to phone and OTP-based login.

To implement a custom UI for phone login, the developer must manually write JavaScript to interact with the webview's HTML. This includes parsing <select> elements for country codes, handling OTP-specific alert elements, and programmatically selecting <option> tags. This approach is less robust and requires more boilerplate than the existing email/password flow.


To create a more consistent and streamlined developer experience, I propose enhancing the custom UI functionality with built-in support for phone and OTP flows. This would involve:

  1. Extending HtmlParseType: Introduce new types to natively recognize phone-related HTML elements, such as HtmlParseType.select for country code dropdowns.

  2. Enhancing ActionController:

    • Add a new method to the controller that allows selecting a dropdown <option> by its index. This is crucial because Azure B2C's HTML can contain duplicate value attributes for different country codes, making selection by value unreliable.
    • Provide a way to natively listen for and handle common phone/OTP validation alerts (e.g., claimVerificationServerError).

This would empower developers to build custom phone/OTP UIs with the same level of ease as the current email/password implementation, abstracting away the underlying JavaScript.


Below are examples of the manual JavaScript execution currently required, which this feature would ideally eliminate.

Example 1: Manually parsing country codes from a <select> element

final result = await actionController.runJavaScriptReturningResult('''
  (() => {
    const select = document.getElementById('countryCode');
    if (!select) return '[]';

    const options = Array.from(select.options)
      .map((option, originalIndex) => ({
        index: originalIndex,
        code: option.value,
        name: option.text,
        isSelected: option.selected,
        disabled: option.disabled
      }))
      .filter(option => !option.disabled);
    return JSON.stringify(options);
  })();
''');

Example 2: Manually listening for Phone Input and OTP error alerts

await actionController.getCustomAlerts([
  FlutterJsCustomAlert(
    type: JsDocumentType.elementById,
    code: 'claimVerificationServerError',
    conditions: {'aria-hidden': 'false'},
  ),
  FlutterJsCustomAlert(
    type: JsDocumentType.byClassName,
    code: '.verificationErrorText.error',
    conditions: {'aria-hidden': 'false'},
  ),
]);

Example 3: Manually selecting a country code by index

await actionController.runJavaScript(
  "document.getElementById('countryCode').selectedIndex = ${country.index};",
);

By adding first-class support for these interactions, the package would become significantly more powerful and easier to use for a common authentication scenario.

smithemely avatar Nov 12 '25 22:11 smithemely