Implementing Remote Wipe for Corporate Data in Mobile Apps
An employee lost their phone. The app contains corporate correspondence, documents, and session tokens. The IT department wants to click a button in the console and delete all corporate data without touching personal photos. They have 15 minutes before the phone reaches the wrong hands.
What Exactly Needs to Be Deleted and How Quickly
Remote Wipe is not a single operation but a cascade. First, understand what constitutes "corporate data":
- SQLite databases and Shared Preferences with tokens and cache
- Files in internal storage (
filesDir,cacheDir) - Data in Keychain / Android Keystore — certificates, encryption keys
- Push tokens (FCM/APNs) tied to the user
- Local copies of documents in external storage (if present)
- Active sessions — they must be invalidated on the server
Files cannot be deleted from disk without a server command — the device might be offline. Therefore, a command queue with guaranteed delivery is necessary.
Architecture of Reliable Remote Wipe
The most common anti-pattern: implementing wipe through a standard FCM push token. Problems: FCM doesn't guarantee delivery, a message might arrive in 6 hours or not at all, and on iOS a background push won't wake an app if forcefully closed by the user.
A reliable scheme looks different:
-
Server marks the device as "pending wipe" in the database (flag
wipe_requested_at) -
With every API request the server returns
X-Wipe-Required: truein the header (or 403 with codeWIPE_REQUIRED) - App on startup performs a health-check request and checks this flag
- FCM/APNs sends a command as an additional signal — not the primary one
// Interceptor for OkHttp — check every response
class WipeCheckInterceptor(private val wipeManager: WipeManager) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
if (response.header("X-Wipe-Required") == "true") {
wipeManager.scheduleImmediateWipe()
}
return response
}
}
If a device was offline for a long time, it receives the command on the first request.
The Deletion Process Itself
On Android:
class WipeManager(private val context: Context) {
fun performWipe() {
// 1. Invalidate tokens on server (fire-and-forget)
authRepository.revokeAllTokens()
// 2. Delete SharedPreferences
context.getSharedPreferences("corp_prefs", Context.MODE_PRIVATE)
.edit().clear().commit() // commit(), not apply() — synchronous
// 3. Delete files
context.filesDir.deleteRecursively()
context.cacheDir.deleteRecursively()
// 4. Delete keys from Keystore
val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
keyStore.aliases().toList().filter { it.startsWith("corp_") }.forEach {
keyStore.deleteEntry(it)
}
// 5. Clean the database
context.deleteDatabase("corp_database")
// 6. Notify server of successful wipe
auditRepository.reportWipeCompleted(deviceId)
// 7. Restart app to login screen
restartToLoginScreen()
}
}
Important: apply() in SharedPreferences is asynchronous. If the app crashes after it — data might remain. Use only commit().
On iOS similarly, but via UserDefaults.removePersistentDomain() and SecItemDelete() for Keychain:
func performWipe() {
// Keychain
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "com.company.corp"]
SecItemDelete(query as CFDictionary)
// UserDefaults
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
// Core Data
try? FileManager.default.removeItem(at: coreDataStoreURL)
}
Problem: Wipe During App Use
If the user is actively working when the command arrives, don't just delete the database. First close all active transactions, shut down database connections, stop background tasks (WorkManager.cancelAllWork() on Android, BGTaskScheduler on iOS), then delete the data.
Without this, Android 12+ encounters SQLiteDatabaseLockedException, and wipe doesn't complete fully.
Execution Audit
After each wipe, the server must receive confirmation with a timestamp. If confirmation doesn't arrive within N hours — repeat the command on the next connection. Wipe operation logs are stored on the server, not on the device.
Timeline
Basic implementation (without Work Profile, app only): 2–3 days. With Android Enterprise support and MDM console integration — from 5 days.







