iOS
authentication tokens
NSUserDefaults
Keychain
security

Storing authentication tokens on iOS - NSUserDefaults vs Keychain?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

For authentication tokens on iOS, the correct default answer is Keychain, not NSUserDefaults. UserDefaults is fine for ordinary preferences and non-sensitive flags, but authentication tokens are security-sensitive credentials and should be stored in the system facility designed for secrets.

Why UserDefaults Is the Wrong Default

UserDefaults is a lightweight key-value store intended for settings such as:

  • theme preference
  • onboarding completion flag
  • last selected tab

It is not designed as a secure credential vault. If you store access tokens there, you are treating a convenience API like a secrets store, which is the wrong threat model.

The issue is not that UserDefaults is unusable for all data. The issue is that tokens deserve stronger protection.

Why Keychain Fits Tokens

Keychain is built for small sensitive values such as:

  • passwords
  • authentication tokens
  • private keys
  • refresh tokens

It supports access-control and device-protection options that align with secret storage more closely than plain app preferences do.

A Basic Keychain Example

The Security framework API is lower-level than UserDefaults, but the pattern is still manageable.

swift
1import Foundation
2import Security
3
4func saveToken(_ token: String, account: String) -> OSStatus {
5    let data = Data(token.utf8)
6
7    let query: [String: Any] = [
8        kSecClass as String: kSecClassGenericPassword,
9        kSecAttrAccount as String: account,
10        kSecValueData as String: data
11    ]
12
13    SecItemDelete(query as CFDictionary)
14    return SecItemAdd(query as CFDictionary, nil)
15}
16
17func loadToken(account: String) -> String? {
18    let query: [String: Any] = [
19        kSecClass as String: kSecClassGenericPassword,
20        kSecAttrAccount as String: account,
21        kSecReturnData as String: true,
22        kSecMatchLimit as String: kSecMatchLimitOne
23    ]
24
25    var result: AnyObject?
26    let status = SecItemCopyMatching(query as CFDictionary, &result)
27
28    guard status == errSecSuccess,
29          let data = result as? Data,
30          let token = String(data: data, encoding: .utf8) else {
31        return nil
32    }
33
34    return token
35}

This is the general pattern for saving and loading a token securely on iOS.

What About UserDefaults Plus Encryption?

People sometimes ask whether encrypting the token and then storing it in UserDefaults is good enough. It is better than raw storage, but it still usually means you are rebuilding part of a secret-storage system that Keychain already provides.

If the app truly has secret data, the simpler and safer answer is usually:

  • store the token in Keychain
  • store non-sensitive session flags in UserDefaults

That keeps the responsibilities clear.

Access Pattern Design Matters Too

Storage is not the whole story. You should also decide:

  • when the token is read
  • when it is cleared on logout
  • whether a refresh token and access token are stored separately
  • how the app behaves after device restart or lock state changes

Even correct Keychain storage can become sloppy if the surrounding authentication flow is not thought through carefully.

Common Pitfalls

The most common mistake is storing access tokens in UserDefaults just because it is easier to code. Ease of use is not the right criterion for credential storage.

Another issue is leaving tokens behind after logout. Whether the value lives in Keychain or elsewhere, the app should explicitly delete it when the session ends.

A third pitfall is storing too much security-sensitive state in one place without naming or access discipline. Separate accounts, services, and token types clearly.

Finally, do not overcomplicate routine preference storage by putting everything in Keychain. Use the secure tool for secrets and the simple tool for ordinary app settings.

Summary

  • Authentication tokens on iOS belong in Keychain, not NSUserDefaults.
  • 'UserDefaults is for preferences and non-sensitive app state.'
  • Keychain is designed for storing small secret values such as tokens and passwords.
  • Clear tokens explicitly on logout and structure token storage intentionally.
  • Use each storage mechanism for the kind of data it was designed to protect.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.