otpless_headless_flutter 2.0.0 copy "otpless_headless_flutter: ^2.0.0" to clipboard
otpless_headless_flutter: ^2.0.0 copied to clipboard

Standalone SDK for Otpless Headless functionality.

OTPLESS Flutter Headless SDK #

The new Headless Authentication SDK offers faster performance, greater reliability, and enhanced security. For a smoother authentication and integration experience, we strongly recommend migrating by removing the old SDK and following the steps below.

Install OTPLESS SDK Dependency #

pub package

Installation #

dependencies: otpless_headless_flutter: ^<latest_version>
flutter pub get

Toolchain requirements (2.0.0+) #

The underlying native SDKs (otpless-headless-sdk 0.9.0, OtplessBM/Core 2.3.2) pull in transitive dependencies that require newer toolchains than the pre-2.0 releases needed:

  • Android: Android Gradle Plugin 8.9.1+ and compileSdkVersion 36+. The 0.9.0 Android SDK transitively depends on androidx.core:core:1.18.0, which enforces this minimum. Update android/settings.gradle and android/app/build.gradle in your consuming app accordingly.
  • iOS: deployment target 13.0+ (unchanged). CocoaPods with OtplessBM/Core 2.3.2 on the trunk. If you use OtplessChannelType.GOOGLE_SDK or FACEBOOK_SDK, add the matching subspec (OtplessBM/GoogleSupport, OtplessBM/FacebookSupport) to your ios/Podfile.

Platform support matrix #

Dart method Android iOS
initialize
setResponseCallback
setDevLogging
start
startOnetap (OneTap discovery) — renamed from startBackground (new in 2.0)
commitResponse
isSdkReady
sendUserAuthEvent (new in 2.0)
setMfaEnabled
initSession / getActiveSession / logoutSession
startInBackground (silent regular auth) ❌ (no SDK equivalent)
isWhatsAppInstalledForAndroid ❌ returns false
initTrueCaller ❌ returns false (no iOS Truecaller SDK)
checkSimBindingStatus / clearSimBinding / setSimBindingEnabled ❌ (no SDK equivalent)
closeDialogIfOpen ❌ (no SDK equivalent)
Deep-link handling / Passkey / Facebook SDK register via manifest / no-op client wires up in AppDelegate / SceneDelegate (see below)

Initialize the SDK #

Import #

import 'package:otpless_headless_flutter/otpless_flutter.dart';
final _otplessHeadlessPlugin = Otpless();
@override
void initState() {
  super.initState();
  _otplessHeadlessPlugin.initialize("YOUR_APP_ID");
  _otplessHeadlessPlugin.setResponseCallback(onOtplessResponse);
}

Initiate Authentication #

Phone Auth #

Request #

void startWithPhone(String phoneNumber) {
    final Map<String, dynamic> args = {
        "phone": "phoneNumber",
        "countryCode": "countryCode",
    };
    _otplessHeadlessPlugin.start(onOtplessResponse, args);
}

Verify #

void verifyPhoneOtp(String phoneNumber, String otp) {
    final Map<String, dynamic> args = {
        "phone": "phoneNumber",
        "countryCode": "countryCode",
        "otp": "otp",
    };
    _otplessHeadlessPlugin.start(onOtplessResponse, args);
}

Response Handling #

void onOtplessResponse(dynamic result) {
  _otplessHeadlessPlugin.commitResponse(result);

  final responseType = result['responseType'];

  switch (responseType) {
    case "SDK_READY":
      debugPrint("SDK is ready");
      break;

    case "FAILED":
      debugPrint("SDK initialization failed");
      // Handle SDK initialization failure
      break;

    case "INITIATE":
      if (result["statusCode"] == 200) {
        debugPrint("Headless authentication initiated");
        final authType = result["response"]["authType"]; // This is the authentication type
        if (authType == "OTP") {
         // Take user to OTP verification screen
        } else if (authType == "SILENT_AUTH") {
          // Handle Silent Authentication initiation by showing 
          // loading status for SNA flow.
        }
      } else {
        // Handle initiation error. 
        // To handle initiation error response, please refer to the error handling section.
        if (Platform.isAndroid) {
          handleInitiateErrorAndroid(result["response"]);
        } else if (Platform.isIOS) {  
          handleInitiateErrorIOS(result["response"]);
        }
      }
      break;

    case "OTP_AUTO_READ":
      // OTP_AUTO_READ is triggered only in ANDROID devices for WhatsApp and SMS.
        final otp = result["response"]["otp"];
        debugPrint("OTP Received: $otp");
      break;

    case "VERIFY":
      final authType = result["response"]["authType"];
      if (authType == "SILENT_AUTH") {
        if (result["statusCode"] == 9106) {
            // Silent Authentication and all fallback authentication methods in SmartAuth have failed.
            //  The transaction cannot proceed further. 
            // Handle the scenario to gracefully exit the authentication flow 
        } else {
            // Silent Authentication failed. 
            // If SmartAuth is enabled, the INITIATE response 
            // will include the next available authentication method configured in the dashboard.
        }
      } else {
        // To handle verification failed response, please refer to the error handling section.
        if (Platform.isAndroid) {
          handleVerifyErrorAndroid(result["response"]);
        } else if (Platform.isIOS) {  
          handleVerifyErrorIOS(result["response"]);
        }
      }
      break;

    case "DELIVERY_STATUS":
        // This function is called when delivery is successful for your authType.
        final authType = result["response"]["authType"];
        // It is the authentication type (OTP, MAGICLINK, OTP_LINK) for which the delivery status is being sent
        final deliveryChannel = result["response"]["deliveryChannel"];
        // It is the delivery channel (SMS, WHATSAPP, etc) on which the authType has been delivered
        break;

    case "ONETAP":
      final token = result["response"]["token"];
      if (token != null) {
        debugPrint("OneTap Data: $token");
        // Process token and proceed
      }
      break;

    case "FALLBACK_TRIGGERED":
        // A fallback occurs when an OTP delivery attempt on one channel fails,  
        // and the system automatically retries via the subsequent channel selected on Otpless Dashboard.  
        // For example, if a merchant opts for SmartAuth with primary channal as WhatsApp and secondary channel as SMS,
        // in that case, if OTP delivery on WhatsApp fails, the system will automatically retry via SMS.
        // The response will contain the deliveryChannel to which the OTP has been sent.
        final newDeliveryChannel = result["response"]["deliveryChannel"];
        if (newDeliveryChannel != null) {
            // This is the deliveryChannel to which the OTP has been sent
        }
      break;

    default:
      debugPrint("Unknown response type: $responseType");
      break;
  }

}

Android manifest update #

Add Network Security Config inside your android/app/src/main/AndroidManifest.xml file into your

android:networkSecurityConfig="@xml/otpless_network_security_config"

Ios info.plist update #

Add the following block to your ios/Runner/info.plist file (Only required if you are using the SNA feature):

<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
	<key>NSExceptionDomains</key>
	<dict>
		<key>80.in.safr.sekuramobile.com</key>
		<dict>
			<key>NSIncludesSubdomains</key>
			<true/>
			<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
			<true/>
			<key>NSTemporaryExceptionMinimumTLSVersion</key>
			<string>TLSv1.1</string>
		</dict>
		<key>partnerapi.jio.com</key>
		<dict>
			<key>NSIncludesSubdomains</key>
			<true/>
			<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
			<true/>
			<key>NSTemporaryExceptionMinimumTLSVersion</key>
			<string>TLSv1.1</string>
		</dict>
	</dict>
</dict>


iOS AppDelegate / SceneDelegate integration #

The plugin does not wrap iOS URL handlers, Facebook SDK registration, or WebAuthn. If your merchant flow requires them, add the following in your host iOS app.

In ios/Runner/AppDelegate.swift:

import OtplessBM

override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    if Otpless.shared.isOtplessDeeplink(url: url) {
        Task { await Otpless.shared.handleDeeplink(url) }
        return true
    }
    return super.application(app, open: url, options: options)
}

For SceneDelegate apps, add to SceneDelegate.swift:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    for context in URLContexts where Otpless.shared.isOtplessDeeplink(url: context.url) {
        Task { await Otpless.shared.handleDeeplink(context.url) }
    }
}

Facebook SDK (only if using FACEBOOK_SDK channel) #

Add OtplessBM/FacebookSupport subspec to ios/Podfile. Then wire it up in AppDelegate.swift:

override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Otpless.shared.registerFBApp(application, didFinishLaunchingWithOptions: launchOptions)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    Otpless.shared.registerFBApp(app, open: url, options: options)
    return super.application(app, open: url, options: options)
}

Passkey / WebAuthn #

Otpless.shared.authorizeViaPasskey(withRequest:windowScene:) is not exposed through this Flutter plugin. If your flow needs it, call it from Swift with a resolved UIWindowScene.

Migration from 1.x to 2.0 #

Breaking changes #

  • startBackground(callback, config) is renamed to startOnetap(callback, config). The old name implied a background OTP flow, but the method actually presents the OneTap / verified-contact sheet. Replace every call site.
  • iOS parity for startOnetap and sendUserAuthEvent. These now execute on both platforms instead of silently returning false on iOS. If your app relied on iOS being a no-op, add a Platform.isAndroid guard on your side.
  • New responseType values may arrive in the callback: AUTH_TERMINATED, MFA_FACTOR_COMPLETED, AUTO_FLOW_ACTION (Android only). Callers that switch exhaustively on responseType must add cases.
  • OtplessAuthConfig constructor gains an optional deviceFingerprintMode parameter (defaults to DeviceFingerprintMode.none). Backwards compatible for positional callers.

New features #

  • setMfaEnabled(bool) — enable MFA. Watch for MFA_FACTOR_COMPLETED in your response handler.
  • initSession(appId) / getActiveSession() / logoutSession() — JWT-based session persistence.
  • startInBackground(callback, requestMap) — Android-only silent variant of start() that suppresses OTP_AUTO_READ intermediates.
  • Android-only: checkSimBindingStatus(), clearSimBinding(), setSimBindingEnabled(bool), closeDialogIfOpen().
  • Extra keys accepted on the start() / startInBackground() request map: tid, code, extras (Map<String, String>), requestId, deviceFingerprintMode.

Bug fix #

  • sendUserAuthEvent(...): the providerInfo optional parameter was previously dropped due to an inverted null check on the plugin side. Fixed in 2.0.0; downstream analytics receive it now.

Note #

For complete documentation and other login feature explore, follow the following guide here: installation guide here

Author #

OTPLESS, developer@otpless.com

3
likes
140
points
607
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Standalone SDK for Otpless Headless functionality.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on otpless_headless_flutter

Packages that implement otpless_headless_flutter