safety_net_flutter

Flutter plugin wrapping SafetyNet (iOS) and SafetyNetAndroid for report-only root/jailbreak/tamper/debugger detection on both platforms.


License
MIT

Documentation

safety_net_flutter

A Flutter plugin wrapping SafetyNet (iOS) and SafetyNetAndroid (Android) behind one Dart API: root/jailbreak/tamper/debugger detection for Flutter apps.

Like both native libraries it wraps, this plugin never auto-reacts. It only reports what it finds — your app decides what to do with the result (block a screen, log it, step up authentication, etc.).

What each platform actually runs

Platform Underlying check
iOS SafetyNet.shared.check() — the full scored pipeline: jailbreak signals, debugger/tracing, code-signature integrity, proxy/VPN. See the SafetyNet README for the full signal list and scoring thresholds.
Android SafetyNetAndroid.checkRoot() + SafetyNetAndroid.checkDebugger() + SafetyNetAndroid.checkProxy(), combined into one result. See the SafetyNetAndroid README for the full check list.

The Dart-facing result is intentionally a minimal, unified shape rather than exposing each platform's full native richness (e.g. iOS's numeric ThreatLevel isn't surfaced) — apps that need finer-grained platform-specific data should call the native SDKs directly on that platform.

class SafetyNetCheckResult {
  final bool isCompromised;
  final List<String> reasons; // raw check names that fired, e.g. "jailbreakDetected"
}

Installation

Published on pub.dev. Add it to your app's pubspec.yaml:

dependencies:
  safety_net_flutter: ^1.2.0

Then:

flutter pub get

For local development against an unreleased version of this plugin instead, use a git dependency pinned to a branch/commit (this repo's git tags lag behind pub.dev releases, so don't pin to a tag expecting it to match the latest published version):

dependencies:
  safety_net_flutter:
    git:
      url: https://github.com/DipakPanchasara/SafetyNetFlutter.git

iOS dependency resolution

SafetyNet and SafetyNetObjC are both published on the CocoaPods trunk, so no extra Podfile setup is needed regardless of which iOS plugin resolution your app uses:

  • Swift Package Manager (Flutter's modern default, ios/Flutter/Generated.xcconfig referencing Package.swift): this plugin's own Package.swift already declares the SafetyNet git dependency (https://github.com/DipakPanchasara/SafetyNet.git, from 2.1.0), and SPM resolves it transitively.
  • CocoaPods (ios/Podfile): this plugin's own podspec already declares s.dependency 'SafetyNet', which CocoaPods now resolves straight from the trunk — just run pod install from ios/, no extra pod lines needed in your own Podfile.

No other iOS setup is required — see "Do I need to touch AppDelegate?" below.

Android dependency resolution

The Android side depends on SafetyNetAndroid via Maven Central (io.github.dipakpanchasara:safetynet-android). As long as your app's repository list includes mavenCentral() — true by default in virtually every Flutter/Gradle project — no further setup is required.

Usage

import 'package:safety_net_flutter/safety_net_flutter.dart';

Future<void> checkDeviceSecurity() async {
  final result = await SafetyNetFlutter.check();

  if (result.isCompromised) {
    // Your call: log it, show a warning, require step-up auth, etc.
    // SafetyNetFlutter itself never blocks UI or logs the user out.
    print('Device flagged: ${result.reasons}');
  }
}

A common pattern is running the check once at app launch, before runApp():

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final result = await SafetyNetFlutter.check();
  if (result.isCompromised) {
    // record/report as appropriate for your app
  }

  runApp(const MyApp());
}

Play Integrity API (Android-only)

import 'package:safety_net_flutter/safety_net_flutter.dart';

// Call once (e.g. at app launch), well before you'll need a token.
// Per Google's guidance this has non-trivial latency — typically under
// 10s, budget up to a minute.
await SafetyNetFlutter.prepareIntegrityCheck(yourCloudProjectNumber);

// Later, when you need a token for a specific action:
final token = await SafetyNetFlutter.requestIntegrityToken(requestHash);
// Send `token` to your server — it is opaque and is NOT itself a verdict.
// Your server must decode it against Google
// (POST https://playintegrity.googleapis.com/v1/{PACKAGE_NAME}:decodeIntegrityToken)
// and enforce the verdict there. Never trust a client-side read of this token.

Bridges SafetyNetAndroid's IntegrityTokenProvider — see the SafetyNetAndroid README for cloudProjectNumber setup and the full server-side verification contract.

Both functions reject on iOS — Play Integrity is a Google Play Services API with no iOS equivalent. This is a normal PlatformException (code UNSUPPORTED_PLATFORM), not a crash — handle it like any other Dart error, or gate the call yourself since you already know it's Android-only:

import 'dart:io' show Platform;

if (Platform.isAndroid) {
  await SafetyNetFlutter.prepareIntegrityCheck(yourCloudProjectNumber);
}

Do I need to touch AppDelegate?

No manual AppDelegate edit is needed to register this plugin. Flutter's GeneratedPluginRegistrant (generated automatically at build time) registers every plugin listed in your pubspec.yaml, including this one — that's true whether your AppDelegate is Swift or Objective-C, and regardless of whether you use SPM or CocoaPods for the iOS side.

Two things worth knowing that are independent of Flutter's plugin registration:

  • SafetyNet's anti-ptrace protection (AntiDebugBridge.m's constructor) runs automatically the moment the SafetyNet dynamic library is loaded into the process — this happens at process launch, before Dart or Flutter even start, and needs no wiring on your part.
  • If you specifically want the check result available as early as possible in the app lifecycle (e.g. before the first frame renders), call SafetyNetFlutter.check() at the top of Dart's main() as shown above — there's no AppDelegate-level equivalent needed for this, since the check itself is a Dart-initiated platform channel call.

Example app

See example/ for a minimal app that runs the check on launch and displays isCompromised and reasons.

cd example
flutter run

Repository layout

  • lib/ — Dart-facing API (SafetyNetFlutter.check(), prepareIntegrityCheck()/requestIntegrityToken(), SafetyNetCheckResult, platform interface, method channel implementation).
  • ios/ — iOS plugin implementation (Swift), wrapping SafetyNet.
  • android/ — Android plugin implementation (Kotlin), wrapping SafetyNetAndroid.
  • example/ — demo app exercising the plugin on both platforms.