Skip to main content

zally_flutter_sdk 1.2.0 copy "zally_flutter_sdk: ^1.2.0" to clipboard
zally_flutter_sdk: ^1.2.0 copied to clipboard

Continuous Authentication for Flutter. Wraps the native zally iOS and Android SDKs behind one Dart API.

zally Flutter SDK #

Continuous Authentication for Flutter. One Dart API over the native zally iOS and Android SDKs.

zally learns how a person holds and touches their device, then keeps verifying that it is still them. This package carries the Dart API and the two native bridges. The native SDKs themselves are fetched at build time, from Maven Central on Android and from a public GitHub release on iOS.

Prefer to own the integration yourself, without a package dependency? The same capability ships as a drag-and-drop bridge at zally-flutter-sdk-bridge.

Requirements #

  • Flutter 3.41.0 or later. That release carries the fix for CVE-2026-27704, a path traversal in the pub client, and is the floor at which Swift Package Manager support is dependable for plugins.
  • Android: minSdk 23, compileSdk 36, Java 17, Kotlin 2.2.21 or later.
  • iOS: deployment target 17.0, Xcode 16 or later.
  • An identity provider that signs a JWT for the signed-in user, set up under Authentication configuration in the zally platform. See The client token.

Install #

flutter pub add zally_flutter_sdk

While this package is in beta, pub add will not resolve to it by default. Ask for the prerelease explicitly:

flutter pub add zally_flutter_sdk:^1.0.0-beta.1

Android #

Set the app's floors in android/app/build.gradle.kts if they are lower than the plugin needs:

android {
    compileSdk = 36
    defaultConfig { minSdk = 23 }
}

Nothing else. The native SDK resolves from Maven Central, which the Flutter app template already declares, and it merges its own sensor permissions and ProGuard rules. Add android.permission.INTERNET if your app does not already have it.

Optionally declare your app UUID in android/app/src/main/AndroidManifest.xml, inside <application>:

<meta-data android:name="com.zally.fluttersdk.APP_UUID" android:value="your-app-uuid" />

This is recommended. See Android touch capture timing.

iOS #

Set the deployment target to 17.0 in Xcode, or in ios/Podfile:

platform :ios, '17.0'

Swift Package Manager, the default since Flutter 3.44, needs nothing else. The native xcframework resolves from a public GitHub release.

CocoaPods apps need one more line, because the native pod is not on the CocoaPods trunk registry and CocoaPods cannot otherwise find it. In ios/Podfile, inside your target 'Runner' block:

pod 'zally-sdk', '3.2.0',
  :podspec => 'https://raw.githubusercontent.com/zally-ltd/zally-ios-sdk-package/3.2.0/zally-sdk.podspec'

Pin the tag path as shown. Pointing at main lets a native release change what your build resolves without you changing anything. The version must match the one this package pins; a mismatch fails pod install rather than reaching a build.

Then add a motion usage description to ios/Runner/Info.plist:

<key>NSMotionUsageDescription</key>
<string>This app uses motion sensors for continuous authentication.</string>

Use #

import 'package:zally_flutter_sdk/zally_flutter_sdk.dart';

final zally = ZallySdk.instance;

Future<void> onSignedIn() async {
  try {
    await zally.initialize(
      appUUID: 'your-app-uuid',
      getClientToken: auth.fetchZallyToken, // a function, not a token
    );
    await zally.start();
  } on ZallyException catch (e) {
    // e.code is E_INIT, E_AUTH or E_START. e.authReason is set for E_AUTH.
  }
}

final subscription = zally.userStateStream.listen((UserState state) {
  if (state.isImposter) {
    // step up authentication
  }
});

Future<void> onSignedOut() => zally.stop();

Call initialize() after the user signs in. Call stop() on sign out.

API #

Member Returns Throws
initialize({appUUID, getClientToken, baseUrl}) Future<void> E_MISSING_APP_UUID, E_INVALID_BASE_URL, E_INIT, E_AUTH, E_UNSUPPORTED_EDITION, E_UNSUPPORTED_PLATFORM
start() Future<void> E_START
stop() Future<void> E_STOP
isInitialized() Future<bool>
isRunning() Future<bool>
getUserState() Future<UserState?>
userStateStream Stream<UserState>

ZallyException carries code, message, and for E_AUTH an authReason of invalidToken, networkUnavailable, tokenExchangeFailed, notInitialized or internal.

UserState carries trainingState (unknown, untrained, training, trained), zIdStatus (user, imposter, inactive, unknown), the raw native strings, and the booleans isUser, isImposter, isOutOfTraining, isInactive.

baseUrl points the SDK at a non production zally stack. It works only on the internal edition of this package, which zally's own apps use; on the published package it throws E_UNSUPPORTED_EDITION. The public native SDKs do not carry the API it needs.

initialize() is safe to call more than once. Concurrent calls share one native call. A later call, after a re-login or a hot restart, swaps in the new token provider and re-runs authorization against the already initialized native SDK.

Things to know #

The client token #

getClientToken returns a JWT that your own identity provider signs for the signed-in user. zally does not issue it. zally checks it against the JWKS URL, issuer and audience set under Authentication configuration in the zally platform, then exchanges it for zally tokens that the native SDK manages itself. If a token your app already holds passes these checks, you can return that one.

Field What zally checks
signature RS256, ES256 or PS256, with a key from your JWKS URL. The header carries kid and alg
iss Equals your expected issuer
aud Includes your expected audience
sub A stable ID for the user. A new sub is a new person, and their training starts over
iat Within the last hour. An older token is rejected even if exp has not passed
exp Not yet passed

The native SDK calls getClientToken once during initialize(), for the first exchange. It does not call it on every request, because it refreshes its zally tokens without you. It calls it again only when zally's refresh token expires or is rejected, which can be hours later and in the background. By then a token fetched once at sign in is more than an hour old, so pass a function that fetches a current token on each call.

// Wrong. Every call returns the same token. Once it is an hour old,
// re-authentication fails.
final token = await auth.fetchZallyToken();
await zally.initialize(appUUID: id, getClientToken: () async => token);

// Right. Each call fetches a token issued within the last hour.
await zally.initialize(appUUID: id, getClientToken: auth.fetchZallyToken);

This package does not fetch a token up front. It waits for the native SDK to ask, then calls your getClientToken and returns the answer.

If your provider returns null, an empty string, throws, or takes longer than 25 seconds, the plugin answers the SDK with no token and prints a [zally] warning. The SDK reports E_AUTH with reason invalidToken on the initial exchange, or stops quietly on a background refresh. Watch for that warning in your logs.

isInitialized and isRunning differ by platform #

Both reflect what each native SDK exposes, which is not the same thing.

isInitialized() is true on iOS only once authorization has succeeded. On Android it is true once initialize() completes, which happens before authorization. Do not treat it as "ready to authenticate" on Android.

isRunning() is authoritative on iOS, where the SDK reports sensors and authorization together. The Android SDK exposes no running state, so this reflects whether start() last succeeded and will not notice the SDK stopping on its own. Never treat it as proof that data is flowing.

isImposter is true before there is a verdict #

On both platforms isImposter is true while zIdStatus is unknown, which is the state right after start() and during training. Read it as "not proven to be the user". Use zIdStatus == ZIdStatus.imposter for an explicit imposter verdict.

Android touch capture timing #

The Android SDK attaches touch capture when an Activity resumes. A Dart initialize() call runs after your first screen is already on screen, so without the com.zally.fluttersdk.APP_UUID manifest entry, touch capture begins the next time the app comes to the foreground. Sensor streaming is unaffected either way. With the manifest entry the plugin initializes the SDK before the first resume and capture attaches immediately on start().

If the manifest value and the appUUID passed to initialize() disagree, the plugin logs a warning and keeps the manifest value.

Keystroke dynamics are not available in Flutter #

The native SDKs observe platform text fields to measure typing rhythm. Flutter draws its own text fields, so there is nothing native to observe, on either platform. Touch, gesture and motion signals are unaffected. This is a known platform limitation, not a defect in this package.

Hot restart #

Plugin registration lives in the Flutter engine and survives a Dart hot restart. Dart main() runs again and calls initialize() again. Both native bridges treat that as a no-op on an already initialized SDK and re-run authorization with the new provider.

Android Kotlin Gradle Plugin warning #

Building an app that uses this package prints a Flutter warning that the plugin applies the Kotlin Gradle Plugin. That is deliberate and safe today. Flutter's current app template still sets android.builtInKotlin=false, so a plugin without the Kotlin Gradle Plugin would fail to compile in apps generated today. This package migrates when Flutter flips that default.

Native SDK versions #

Platform Pinned Source
Android com.zally:zally-sdk:3.3.0 Maven Central
iOS zally-ios-sdk-package 3.2.0 GitHub release, via Swift Package Manager or the podspec URL above

Moving a pin is a release of this package, so one package version always resolves one native version on both platforms.

Example #

cd example
flutter run \
  --dart-define=ZALLY_APP_UUID=... \
  --dart-define=ZALLY_TOKEN_URL=https://your-backend/zally-token

The token URL is fetched with GET each time the SDK asks for a token. The body is the JWT, or JSON with a top-level token field. Pass -PzallyAppUuid=... to Gradle to exercise the early initialization path. The screen shows every call, every token request with a timestamp, and the live user state.

License #

Apache-2.0. The native zally SDKs carry their own licenses.

1
likes
160
points
353
downloads

Documentation

Documentation
API reference

Publisher

verified publisherzally.com

Weekly Downloads

Continuous Authentication for Flutter. Wraps the native zally iOS and Android SDKs behind one Dart API.

Repository (GitHub)
View/report issues

Topics

#authentication #biometrics #security

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on zally_flutter_sdk

Packages that implement zally_flutter_sdk