This document describes the mobile client security mechanisms implemented in the BhuMitra Flutter application to prevent application tampering, protect sensitive tokens, and secure local user data.
The client implements security measures across three areas:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BhuMitra Flutter App β
βββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββ€
β Native Security β Network Protections β Data Safety β
βββββββββββββββββββββΌββββββββββββββββββββββββββββββΌβββββββββββββββββββββββ€
β β’ Play Integrity β β’ Custom x-app-id signature β β’ AES-256 Storage β
β β’ Debug Bypass β β’ Token Refresh Interceptor β β’ Dotenv Isolation β
β β’ Ad-Block Alert β β’ Session Displacement Guardβ β’ Keychain Storage β
βββββββββββββββββββββ΄ββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββ
To ensure that the application is running on a genuine, unmodified Android device and was installed from the official Google Play Store, BhuMitra integrates the native Google Play Integrity API.
- Request Nonce: The client requests a single-use token (nonce) from the backend
/api/integrity/nonceendpoint. - Request Native Token: Using a Flutter
MethodChannel(app.ankit.bhumitra/integrity), the app calls native Android code passing the nonce and the GCP Project Number (211057057184):final String? integrityToken = await _channel.invokeMethod<String>( 'requestIntegrityToken', { 'nonce': nonce, 'cloudProjectNumber': _cloudProjectNumber, }, );
- Submit Verification: The native code returns a Google-signed integrity token, which the client sends to
/api/integrity/verifyfor validation. - Bypass Check: In debug runs (
kDebugMode) or on non-Android platforms, the check returnstrueautomatically to simplify local development.
All network communication with the backend is managed by ApiService (api_service.dart) using a custom Dio HTTP Client:
Every outgoing request automatically injects the signature:
- Header Name:
x-app-id - Header Value:
BhuMitra_Secure_Alpha_2026_X9z7Pq2Km(constructed dynamically in the binary from character codes).
The request interceptor automatically retrieves the access token from storage and appends it:
onRequest: (options, handler) async {
final token = await _storage.read(key: 'access_token');
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
return handler.next(options);
}If an endpoint returns 401 Unauthorized, the client checks the error message:
SESSION_DISPLACED: The account has been logged in on another device. The client deletes all local tokens and redirects the user to the login screen.- Expired Token: If the token expired normally, the interceptor attempts to refresh it using the stored
refresh_token. If successful, it retries the original request. If the refresh token is also expired, the session is cleared.
BhuMitra does not store authentication keys or subscription credentials in plaintext.
- Secure Storage: We use the
flutter_secure_storagepackage to write tokens directly to the device's secure system container.- iOS: Stored in the native Apple Keychain service.
- Android: Stored using
EncryptedSharedPreferences(AES-256 GCM encryption, keys managed via Android Keystore).
- Hive Cache Encryption: Non-sensitive settings and measurements are stored in Hive boxes, while keys remain isolated.
- Dotenv: Environment credentials (such as the base URL) are loaded via
flutter_dotenvfrom a local.envasset file.
BhuMitra Pro's revenue model depends on ads for FREE tier users. The app implements a client-side detection mechanism (ad_block_detector.dart):
- Connection Check: Attempts to connect to Google Mobile Ads test domains.
- Ad Placement Test: Checks if ad container heights are collapsed.
- UI Blocking: If an ad blocker is detected, the app displays a full-screen overlay requesting the user to disable the blocker (ad_block_overlay.dart).