Sign3 SDK Integration Guide for Flutter
The Sign3 SDK is a fraud prevention toolkit designed to assess device security, detecting potential risks such as rooted devices, VPN connections, or remote access, and much more. By providing insights into the device's safety, it enhances security measures against fraudulent activities and ensures a robust protection system.
Adding Sign3 SDK to Your Project
Add the latest version to your pubspec.yaml file.
- We continuously enhance our fraud library with new features, bug fixes, and security updates. To stay protected against evolving fraud risks, we recommend updating to the latest SDK version.
-
Visit latest_version for the latest version and check the Changelog for more details.
dependencies: flutter_intelligence_sign3: ^2.1.5
-
Setup For Android
Create .env file
-
In the root directory of your Flutter project, create a .env file and add the following content to it
SIGN3_USERNAME=provided in credential doc SIGN3_PASSWORD=provided in credential doc SIGN3_REPO_URL=https://sign3.jfrog.io/artifactory/intelligence-generic-local/
Using Project Level Gradle Dependency
- In the Android folder of your Flutter project, open the project-level
build.gradlefile, add the following line, and sync the project. You can collect the username and password from the credentials document.
def envProperties = new Properties()
def envFile = rootProject.file("../.env")
if (envFile.exists()) {
envFile.withInputStream { stream ->
envProperties.load(stream)
}
println ".env file loaded successfully."
} else {
println "Error: .env file not found."
}
allprojects {
repositories {
def sign3RepoUrl = envProperties['SIGN3_REPO_URL'] ?: ""
def sign3Username = envProperties['SIGN3_USERNAME'] ?: ""
def sign3Password = envProperties['SIGN3_PASSWORD'] ?: ""
if (!sign3RepoUrl.isEmpty() && sign3RepoUrl.startsWith("https://") && !sign3Username.isEmpty() && !sign3Password.isEmpty()) {
maven {
url sign3RepoUrl
credentials {
username = sign3Username
password = sign3Password
}
}
} else {
println "Error: Invalid repository URL or missing credentials in .env file."
}
}
}
Initializing the SDK
- Create an
FlutterApplicationclass in the Android folder of your Flutter project, initialize the Sign3 SDK in theonCreate()method of the class, and add the Flutter Application class to yourAndroidManifest.xmlfile. - This SDK needs 2 required permissions for Android to achieve higher accuracy.
- ACCESS_FINE_LOCATION
- READ_PHONE_STATE
- Use the ClientID and Client Secret shared with the credentials document.
For Kotlin
class MyApplication : FlutterApplication() {
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val sign3IntelligencePlugin = Sign3IntelligencePlugin()
OptionsBuilder.build(
clientId = "<SIGN3_CLIENT_ID>",
secret = "<SIGN3_CLIENT_SECRET>",
env = OptionsBuilder.ENV_DEV, // For Prod: Options.ENV_PROD, For Dev: Options.ENV_DEV
sslPinning = false // Optional: If you want SSL pinning in API calls, default value is false.
)
sign3IntelligencePlugin.initAsync(this)
}
}
}
For Java
public class MyApplication extends FlutterApplication {
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Sign3IntelligencePlugin sign3IntelligencePlugin = new Sign3IntelligencePlugin();
OptionsBuilder.INSTANCE.build(
"<SIGN3_CLIENT_ID>",
"<SIGN3_CLIENT_SECRET>",
OptionsBuilder.ENV_DEV, // For Prod: Options.ENV_PROD, For Dev: Options.ENV_DEV
false); // Optional: If you want SSL pinning in API calls, default value is false.
sign3IntelligencePlugin.initAsync(this);
}
}
}
For AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Uncomment below line if minSdk is lower than 23 -->
<!-- <uses-sdk tools:overrideLibrary="com.sign3.intelligence.flutter_intelligence_sign3"/> -->
<!-- Optional: Add any of the below location permission to get the location data from sdk -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Optional: This permission is taken to calculate sim affinity to the device -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:name=".MyApplication">
<!-- Add other components like activities here -->
</application>
</manifest>
Setup For iOS
Requirements
- iOS 15.0 or higher
Recommended Permissions & Capabilities
To improve the accuracy and completeness of Device Intelligence signals, we strongly recommend enabling the following permissions and capabilities in your iOS app. While the SDK will function without them, enabling more permissions allows for richer signal enrichment and more reliable device fingerprinting.
- Access WiFi Information entitlement — Enables collection of network-related signals
- Location permission — Allows location-based signal enrichment
- iCloud (CloudKit) — Enables iCloud-based identifiers for improved device continuity
Note: If any of the above permissions or capabilities are unavailable, the corresponding values will not be collected, which may reduce the reliability of Device Intelligence. We recommend enabling as many as possible based on your use case.
Setup Instructions
1. Access WiFi Information Entitlement
In Xcode, open your project → select your app target → Signing & Capabilities → click + Capability → add Access WiFi Information.
2. Location Permission
Add the following keys to your ios/Runner/Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to enhance device intelligence and security.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We use your location to enhance device intelligence and security.</string>
3. iCloud (CloudKit) Setup
iCloud must be enabled and configured with a CloudKit container for the SDK to collect iCloud-based signals. To enable it:
- Open your Flutter project's iOS module in Xcode (
ios/Runner.xcworkspace). - Select the Runner target.
- Go to the Signing & Capabilities tab.
- Click + Capability and add iCloud.
- Under the iCloud capability, check the CloudKit checkbox.
- Under Containers, click the + button and either:
- Select an existing iCloud container, or
- Create a new one using the format
iCloud.<your.bundle.identifier>(e.g.,iCloud.com.example.myapp).
- Ensure the container is selected (checkbox ticked) for your app.
⚠️ Important: Make sure your Apple Developer account has iCloud enabled for the App ID associated with your bundle identifier. You can verify this in the Apple Developer Portal under Identifiers → your App ID → Capabilities.
NOTE: If the listed permissions are unavailable for the application, the corresponding values will not be collected, potentially limiting the reliability of Device Intelligence. We recommend enabling as many permissions as possible based on your use case to enhance the accuracy and completeness of the data collected.
Initializing the SDK
- Initialize the SDK in your
AppDelegateclass within theapplication(_:didFinishLaunchingWithOptions:)method. - Use the
ClientIDandClient Secretshared with the credentials document.
import flutter_intelligence_sign3
@main
@objc class AppDelegate: FlutterAppDelegate {
static func register(with registrar: any FlutterPluginRegistrar) {
}
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
if #available(iOS 15.0, *) {
let flutterIntelligenceSign3Plugin = FlutterIntelligenceSign3Plugin()
OptionsBuilder.build(
clientId: "<CLIENT_ID>",
secret: "<CLIENT_SECRET>",
env: OptionsBuilder.ENV_DEV,
sslPinning: false) // Optional: If you want SSL pinning in API calls, default value is false.
flutterIntelligenceSign3Plugin.initAsync()
}
return super.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
}
}
RECOMMENDED: After every flutter version upgrade, please run the following commands inside the
iosdirectory of your project to fetch latest dependency:
cd ios
pod deintegrate
pod install
Get Session ID
- The Session ID is the unique identifier of a user's app session and serves as a reference point when retrieving the device result for that session.
- The Session ID follows the OS lifecycle management, in line with industry best practices. This means that a user's session remains active as long as the device maintains it, unless the user terminates the app or the device runs out of memory and has to kill the app.
Future<void> getSessionID() async {
var sessionId = await Sign3Intelligence.getSessionId();
}
Fetch Device Intelligence Result
- To fetch the device intelligence data refer to the following code snippet.
- IntelligenceResponse and IntelligenceError models are exposed by the SDK.
Future<void> getIntelligence() async {
try {
var sign3IntelligenceResponse = await Sign3Intelligence.getIntelligence();
// Do something with the response
} catch (e) {
// Handle the error message
}
}
Optional
- You can add optional parameters like UserId, Phone Number, etc., at any time and update the instance of Sign3Intelligence.
- Once the options are updated, they get reset. Clients need to explicitly update the options again to ingest them, or else the default value of OTHERS in userEventType will be sent to the backend.
- You need to call getIntelligence() function whenever you update the options.
- To update the Sign3Intelligence instance with optional parameters, including additional attributes, you can use the following examples.
UpdateOptions getUpdatedOptions() {
Map<String, String> additionalAttributes = {
"TRANSACTION_ID": "<TRANSACTION_ID>",
"DEPOSIT": "<AMOUNT>",
"WITHDRAWAL": "<AMOUNT>",
"METHOD": "UPI/CARD/NET_BANKING/WALLET",
"STATUS": "SUCCESS/FAILURE",
"CURRENCY": "USD/INR/GBP/etc.",
"TIMESTAMP": DateTime.now().millisecondsSinceEpoch.toString(),
};
UpdateOptions updateOptions = UpdateOptionsBuilder()
.setPhoneNumber("<phone_number>")
.setUserId("<user_id>")
.setPhoneInputType(PhoneInputType.GOOGLE_HINT)
.setOtpInputType(OtpInputType.AUTO_FILLED)
.setUserEventType(UserEventType.TRANSACTION)
.setMerchantId("<merchant_id>")
.setAdditionalAttributes(additionalAttributes)
.build();
return updateOptions;
}
Future<void> updateOptions() async {
await Sign3Intelligence.updateOptions(getUpdatedOptions());
}
Future<void> getIntelligence() async {
try {
var sign3IntelligenceResponse = await Sign3Intelligence.getIntelligence();
// Do something with the response
} catch (e) {
// Handle the error message
}
}
Sample Device Result Response
Successful Intelligence Response
{
"requestId": "403ad427-5018-47b9-b6e8-790e17a78201",
"newDevice": false,
"deviceId": "43fccb70-d64a-4c32-a251-f07c082d7034",
"vpn": false,
"proxy": false,
"emulator": true,
"cloned": false,
"geoSpoofed": false,
"rooted": false,
"ip": "106.219.161.71",
"remoteAppProviders": false,
"remoteAppProvidersCount": 3,
"mirroredScreen": false,
"hooking": true,
"factoryReset": true,
"appTampering": true,
"sessionRiskScore": 99.50516,
"deviceRiskScore": 99.50516,
"clientUserIds": [
"difansd23r32",
"2390ksdfaksd"
],
"gpsLocation": {
"address": "F2620, Block F, Sushant Lok III, Sector 57, Gurugram, Haryana 122011, India",
"adminArea": "Haryana",
"countryCode": "IN",
"countryName": "India",
"featureName": "F2620",
"latitude": "28.420385999999997",
"locality": "Gurugram",
"longitude": "77.088926",
"postalCode": "122011",
"subAdminArea": "Gurgaon Division",
"subLocality": "Sector 57"
},
"ipDetails": {
"country": "IN",
"fraudScore": 27.0,
"city": "New Delhi",
"isp": null,
"latitude": 28.60000038,
"region": "National Capital Territory of Delhi",
"asn": "",
"longitude": 77.19999695
},
"simInfo": {
"simIds": [
{
"simSlotIndex": 0,
"carrierName": "Android",
"id": 1,
"eSim": true
}
],
"totalSimUsed": 10
},
"appAnalytics": {
"affinity": {
"entertainment": 0.5,
"gaming": 0.6,
"productivity": 0.8,
"food_and_drink": 0.4
}
},
"deviceMeta": {
"cpuType": "Qualcomm Technologies, Inc SM7325",
"product": "I2126i",
"androidVersion": "14",
"iOSVersion": "18.2"
"storageAvailable": "29340553216",
"storageTotal": "111156146176",
"model": "I2126",
"screenResolution": "1080x2316",
"brand": "iQOO",
"totalRAM": "7679795200"
},
"additionalData": {},
"factoryResetTime": 1743419662000,
"appliedRules": {
"rules": {
"102 : Screen mirrored": 0,
"105 : Unusual Behaviour": "",
"109 : Transaction into black listed account": "",
"110 : Blacklist phone number": "",
"55 : VPN enabled": 0,
"58 : Remote access apps installed": "",
"6 : Professional profiles exists": 0
},
"totalScore": 50.0
},
"genuineInstall": false,
"developerOptionsEnabled": true,
"usbDebugging": true,
"wirelessDebugging": false,
"unsecuredWifi": false,
"harmfulAppDetected": false,
"blacklistedDevice": false,
"keyloggerDetected": false,
"ruleAction": {
"action": "WARN",
"name": "VPN enabled",
"description": "A VPN connection was detected on this device.",
"message": "For security reasons, please disable your VPN and try again."
}
}
Error Response
{
"requestId": "7e12d131-2d90-4529-a5c7-35f457d86ae6",
"errorMessage": "Sign3 Server Error"
}
Intelligence Response
| Field | Type | Description | Default Value |
|---|---|---|---|
| requestId | string | A unique identifier for the specific request. | "" |
| newDevice | boolean | Indicates if the device is new. | false |
| deviceId | string | A unique identifier for the device. | "" |
| vpn | boolean | Indicates whether a VPN is active on the device. | false |
| proxy | boolean | Indicates whether a proxy server is in use. | false |
| emulator | boolean | Indicates if the app is running on an emulator. | false |
| remoteAppProviders | boolean | Indicates whether any remote applications are installed on the device. | false |
| mirroredScreen | boolean | Indicates if the device's screen is being mirrored. | false |
| cloned | boolean | Indicates if the user is using a cloned instance of the app. | false |
| geoSpoofed | boolean | Indicates if the device's location is being faked. | false |
| rooted | boolean | Indicates if the device has been modified for root access. | false |
| sessionRiskScore | float | A score representing the risk level of the session. | 0.0 |
| hooking | boolean | Indicates if the app has been altered by malicious code. | false |
| factoryReset | boolean | Indicates if a suspicious factory reset has been performed. | false |
| factoryResetTime | long | Timestamp of last factory reset. If not detected, the default is -1 |
-1 (if not detected) |
| appTampering | boolean | Indicates if the app has been modified in an unauthorized way. | false |
| clientUserIds | array of strings | An array of user IDs assigned by the client that a device has seen till now. | [] |
| gpsLocation | object | Details of the device's current GPS location, including latitude, longitude, and address information. | {} |
| ip | string | The current IP address of the device. | "" |
| ipDetails | object | Object added to capture IP-related information and fraudScore related to IP address. | {} |
| simInfo | object | It will contain information like total sims used in a phone in its lifecycle, current sim+slot details. | {} |
| remoteAppProvidersCount | number | The number of remote application providers detected on the device. | 0 |
| deviceRiskScore | float | The risk score of the device. Note: sessionRiskScore is derived from the latest state of the device but deviceRiskScore also factors in the historical state of the device (whether a device was rooted in any of the past sessions). | 0.0 |
| deviceMeta | object | Contains all device-related information such as brand, model, screen resolution, total storage, etc. | {} |
| appAnalytics | object | An object containing an affinity field, which holds key-value pairs where each key is a category (e.g., entertainment, tech, gaming), and the value is a floating-point number between 0 and 1 representing the user's affinity score for that category. Higher scores indicate stronger interest, and lower scores suggest less interest. These scores are based on the apps installed on the user's device. | {} |
| additionalData | object | Reserved for any extra or custom data not present in the IntelligenceResponse, providing a customized response based on specific requirements. | {} |
| appliedRules | object | Returns the list of applied rules alongside the decision output, enabling the app to take immediate action (e.g., allow, warn, block) based on the exact rules fired. | {} |
| genuineInstall | boolean | Indicates whether the application is installed from a trusted and official source. | false |
| developerOptionsEnabled | boolean | Indicates whether Developer Options are enabled on the device. | false |
| usbDebugging | boolean | Indicates whether USB debugging is currently enabled on the device. | false |
| wirelessDebugging | boolean | Indicates whether wireless (ADB over Wi-Fi) debugging is enabled on the device. | false |
| unsecuredWifi | boolean | Indicates whether the device is connected to an unsecured or potentially risky Wi-Fi network. | false |
| harmfulAppDetected | boolean | Indicates whether potentially harmful or risky applications are detected on the device. | false |
| blacklistedDevice | boolean | Indicates whether the device is identified as blacklisted based on internal risk evaluation. | false |
| keyloggerDetected | boolean | Indicates whether potential keylogging behavior or related risks are detected on the device. | false |
| ruleAction | object | Returns the triggered rule details along with the recommended action (e.g., allow, warn, block), enabling the app to take immediate action based on the specific rule that was fired. | {} |