Skip to main content

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 itemStatusImplementation
Token storage✅ DoneFlutter Secure Storage; SharedPreferences fallback + migration
Logout✅ DoneCalls POST /logout before clearing local state
Error messages✅ DonegetSafeErrorMessage(); generic exceptions in api_service
API URL✅ DoneAppConfig.baseUrl from --dart-define=BASE_URL; fallback to prod
Certificate pinning✅ DoneIntermediate CA pinning (E7, R12, YE1, YE2, YR1, YR2); disabled in debug
HTTP timeout✅ Done30s for most; 120s for upload/calculate-cost
Google Maps key✅ Donelocal.properties (Android); MapsConfig.xcconfig (iOS)
Root/jailbreak detection✅ Doneflutter_jailbreak_detection; SecurityService; warning dialog
Session timeout✅ Done15 days idle logout via SessionService
Registration duplicate messages✅ DoneBackend returns which of username/email/mobile is already in use; app shows message as-is
Debug logging✅ DoneGated with kDebugMode; no response body or payment IDs in logs
Referral code sanitization✅ Donereferral_sanitizer.dart: only alphanumeric, underscore, hyphen; max 64 chars. Used for deep-link and register.
Safe error (referral context)✅ Donesafe_error.dart context 'referral' for referral/profile load failures

2. Still Pending (Lower Priority)

ItemPriorityNotes
Location privacy toggleMediumAdd settings toggle to disable location for shop discovery
App integrity checksLowGoogle Play Integrity API (Android) / App Attest (iOS)
Biometric for sensitive actionsLowOptional 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.kts reads MAPS_API_KEY from local.properties; injects into manifest placeholder.
  • iOS: MapsConfig.xcconfig defines MAPS_API_KEY; AppDelegate.swift provides 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

ItemStatus
HTTPSbaseUrl 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