Customer App (Android / iOS) — Security Implementation List
Date: January 31, 2026
Last Updated: February 2026 — Session timeout 15 days; registration duplicate-field messages; referral sanitizer and safe-error context
Scope: customer_app (Flutter — Android primary, iOS secondary)
Reference: project-docs/docs/development/security-audit.md §3 Customer App
This document compares the current customer app code with the security audit and lists what has been implemented and what remains.
1. Summary: Implementation Status
| Audit item | Status | Implementation |
|---|---|---|
| Token storage | ✅ Done | Flutter Secure Storage; SharedPreferences fallback + migration |
| Logout | ✅ Done | Calls POST /logout before clearing local state |
| Error messages | ✅ Done | getSafeErrorMessage(); generic exceptions in api_service |
| API URL | ✅ Done | AppConfig.baseUrl from --dart-define=BASE_URL; fallback to prod |
| Certificate pinning | ✅ Done | Intermediate CA pinning (E7, R12, YE1, YE2, YR1, YR2); disabled in debug |
| HTTP timeout | ✅ Done | 30s for most; 120s for upload/calculate-cost |
| Google Maps key | ✅ Done | local.properties (Android); MapsConfig.xcconfig (iOS) |
| Root/jailbreak detection | ✅ Done | flutter_jailbreak_detection; SecurityService; warning dialog |
| Session timeout | ✅ Done | 15 days idle logout via SessionService |
| Registration duplicate messages | ✅ Done | Backend returns which of username/email/mobile is already in use; app shows message as-is |
| Debug logging | ✅ Done | Gated with kDebugMode; no response body or payment IDs in logs |
| Referral code sanitization | ✅ Done | referral_sanitizer.dart: only alphanumeric, underscore, hyphen; max 64 chars. Used for deep-link and register. |
| Safe error (referral context) | ✅ Done | safe_error.dart context 'referral' for referral/profile load failures |
2. Still Pending (Lower Priority)
| Item | Priority | Notes |
|---|---|---|
| Location privacy toggle | Medium | Add settings toggle to disable location for shop discovery |
| App integrity checks | Low | Google Play Integrity API (Android) / App Attest (iOS) |
| Biometric for sensitive actions | Low | Optional fingerprint/face for payment on shared devices |
3. Implemented Details
3.1 Token Storage
Implementation: api_service.dart uses FlutterSecureStorage as primary; SharedPreferences fallback with migration on first run. main.dart AuthWrapper reads token from secure storage first, then fallback.
Files: lib/services/api_service.dart, lib/main.dart
3.2 Logout
Implementation: api_service.logout() calls POST /logout with Bearer token before clearing local state. Clears both secure storage and SharedPreferences.
Files: lib/services/api_service.dart
3.3 Error Messages
Implementation: lib/utils/safe_error.dart provides getSafeErrorMessage(err, context). All auth, payment, upload, profile, and file screens use it. api_service throws generic exceptions only.
Files: lib/utils/safe_error.dart, lib/services/api_service.dart, all screen files
3.4 Debug Logging
Implementation: print statements removed or gated with kDebugMode and debugPrint. Razorpay success/error handlers do not log payment IDs in release.
Files: lib/services/api_service.dart, lib/services/razorpay_service.dart, screens
3.5 HTTP Timeout
Implementation: All API calls use 30s timeout (120s for multipart upload and calculate-cost).
Files: lib/services/api_service.dart
3.6 API URL Configuration
Implementation: lib/config/app_config.dart reads BASE_URL from --dart-define; falls back to production URL if empty.
Build: flutter run --dart-define=BASE_URL=https://...
3.7 Certificate Pinning
Implementation: lib/services/http_client_factory.dart creates HttpClient with SecurityContext using Let's Encrypt intermediate certificates. Default: intermediate CA pinning (E7, R12, YE1, YE2, YR1, YR2). Optional: leaf fingerprint pinning via --dart-define=CERT_PINS=.... Disabled in debug mode.
Files: lib/config/letsencrypt_certs.dart, lib/services/http_client_factory.dart
Update script: scripts/update_letsencrypt_certs.ps1 (run when Let's Encrypt rotates intermediates)
3.8 Google Maps API Key
Implementation:
- Android:
build.gradle.ktsreadsMAPS_API_KEYfromlocal.properties; injects into manifest placeholder. - iOS:
MapsConfig.xcconfigdefinesMAPS_API_KEY;AppDelegate.swiftprovides it to GMSServices.
Files: android/app/build.gradle.kts, android/local.properties, ios/Flutter/MapsConfig.xcconfig, ios/Runner/AppDelegate.swift
See: customer_app/GOOGLE_MAPS_SETUP.md
3.9 Root/Jailbreak Detection
Implementation: flutter_jailbreak_detection package. lib/services/security_service.dart provides SecurityService.hasElevatedRisk. dashboard_screen.dart shows warning dialog on elevated risk.
Note: Plugin requires a local patch for AGP compatibility (namespace + JVM target). See customer_app/SECURITY_SETUP.md.
3.10 Session Timeout
Implementation: lib/services/session_service.dart tracks activity. 15 days idle triggers callback; dashboard_screen.dart clears session and navigates to login. User interactions call SessionService.instance.touch().
Files: lib/services/session_service.dart, lib/screens/dashboard_screen.dart
3.11 Registration duplicate-field messages
Implementation: When registration fails because username, email, or mobile number is already in use, the backend returns a specific message (e.g. "This username is already in use." or "This email and mobile number are already in use."). The customer app passes this through via api_service.dart (throws response body on 400/409) and safe_error.dart (allows "already in use" messages). See Changelog.
3.12 Referral code sanitization
Implementation: lib/utils/referral_sanitizer.dart exposes sanitizeReferralCode(String?): only alphanumeric, underscore, hyphen; max 64 chars. Used in api_service.register() and in main.dart for deep-link referral so codes from links or user input cannot inject unsafe characters.
Files: lib/utils/referral_sanitizer.dart, lib/services/api_service.dart, lib/main.dart
3.13 Safe error (referral context)
Implementation: getSafeErrorMessage(err, 'referral') returns a user-friendly message for referral/profile load failures ("Could not load your referral code. Pull down to refresh or tap Retry."). Used on Refer & Earn and profile screens.
Files: lib/utils/safe_error.dart, lib/screens/refer_and_earn_screen.dart
4. What's Already OK
| Item | Status |
|---|---|
| HTTPS | ✅ baseUrl uses https:// |
| Razorpay key | ✅ Comes from backend key_id, not hardcoded |
| Location permissions | ✅ Usage descriptions in Info.plist |
| Login timeout | ✅ 30s on login |
| Role check | ✅ Login screen checks role == 'customer' |
| File picker | ✅ Uses file_picker (user selects files) |
5. References
- Security audit:
project-docs/docs/development/security-audit.md§3 - Setup guide:
customer_app/SECURITY_SETUP.md - Google Maps:
customer_app/GOOGLE_MAPS_SETUP.md - Shopkeeper app (reference):
shopkeeper_app/lib/services/api_service.dart
Last updated: February 2026