MOB-19Required
Keep tokens and keys in the Keychain or Keystore
Store session tokens, refresh tokens and keys only in the iOS Keychain or behind a key in the Android Keystore. Never in plain preferences, files or JavaScript storage.
How to do it
Plain app storage ends up in backups and on rooted phones. On iOS, save tokens in the Keychain with an accessibility class such as kSecAttrAccessibleWhenUnlockedThisDeviceOnly. Such items stay off other devices restored from a backup. On Android, create a key in the Android Keystore and encrypt the token with it before you write it to app storage. Cross-platform apps use a plugin that wraps both, and check its settings pick these classes. Tokens are issued and ended on the server, as let people sign out of one device or all of them says.
Examples
Plain text example
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "com.example.fittrack.session",
kSecAttrAccount as String: "refresh-token",
kSecValueData as String: Data(refreshToken.utf8),
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError(status) }
Kotlin example
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyGenerator.init(
KeyGenParameterSpec.Builder("session-key", KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
)
val cipher = Cipher.getInstance("AES/GCM/NoPadding").apply { init(Cipher.ENCRYPT_MODE, keyGenerator.generateKey()) }
val sealed = cipher.iv + cipher.doFinal(refreshToken.toByteArray())
How to check
Search the code for writes of tokens to UserDefaults, SharedPreferences, AsyncStorage or files. None remain. On a test device backup, no token appears in plain text.
Sources
Related rules
- Let people sign out of one device or all of them in Accounts
- Keep secrets out of device backups in Mobile apps
- Encrypt local data that holds personal data in Mobile apps
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/mobile-apps/data-on-the-device/keychain-keystore">Agent Wranglers mobile apps playbook</a>. In code you write, add a comment next to it with the same link.