Skip to content
AIQGM

Where a session token should live on iOS

UserDefaults is convenient, and it's a plain property list. Tokens belong in the Keychain, with an accessibility class chosen on purpose.

We still find auth tokens in UserDefaults in apps we review. It's a property list inside the app container: it ends up in unencrypted device backups and is readable on a jailbroken phone.

The Keychain is encrypted by the system, and the accessibility attribute decides when the item can be read and whether it may leave the device:

Swiftswift
func saveToken(_ token: String, for account: String) throws {
    let base: [String: Any] = [
        kSecClass as String:       kSecClassGenericPassword,
        kSecAttrService as String: "com.example.app.session",
        kSecAttrAccount as String: account,
    ]
    SecItemDelete(base as CFDictionary)          // replace any previous value

    var item = base
    item[kSecValueData as String] = Data(token.utf8)
    // Readable after the first unlock (so background refresh works),
    // never restored onto a different device from a backup.
    item[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly

    let status = SecItemAdd(item as CFDictionary, nil)
    guard status == errSecSuccess else { throw KeychainError.unhandled(status) }
}

ThisDeviceOnly means a user restoring a backup to a new phone signs in again. That's usually what you want for a session token, and usually not what you want for a user's saved preferences. It's a product decision, so we raise it rather than pick silently.

This comes from our mobile apps work.

Tell us what you're working on.

A few paragraphs is plenty. We'll read it, reply by email, and if it looks like a fit, set up a 30-minute Google Meet with an engineer.