Skip to content

security: migrate iOS secrets from UserDefaults to Keychain#41

Open
juliosuas wants to merge 1 commit intoIntent-Lab:mainfrom
juliosuas:fix/security-warnings-and-keychain
Open

security: migrate iOS secrets from UserDefaults to Keychain#41
juliosuas wants to merge 1 commit intoIntent-Lab:mainfrom
juliosuas:fix/security-warnings-and-keychain

Conversation

@juliosuas
Copy link
Copy Markdown

Problem

API keys and tokens (Gemini API key, OpenClaw gateway token, hook token) are stored in UserDefaults, which is:

  • Unencrypted on disk
  • Readable from device backups (iTunes/Finder)
  • Accessible to other apps on jailbroken devices

This was flagged in Issue #1 (API key leaking) and is also relevant to PR #8 (in-app secrets management).

Fix

New: KeychainManager.swift

Thin wrapper around Security.framework — 59 lines, zero dependencies:

  • get(_:) / set(_:value:) / delete(_:) / deleteAll()
  • Uses kSecClassGenericPassword with a service identifier
  • Update-first strategy (cheaper than delete+add)

Changed: SettingsManager.swift

  • Secrets (API keys, tokens) → Keychain (encrypted, hardware-backed)
  • Settings (host, port, toggles) → UserDefaults (unchanged, non-sensitive)
  • Auto-migration: on first launch after update, moves existing UserDefaults secrets to Keychain and deletes the old entries

Migration Behavior

Existing user What happens
Has secrets in UserDefaults Auto-migrated to Keychain on first launch, then deleted from UserDefaults
New install Secrets go directly to Keychain
Reset all settings Both Keychain and UserDefaults are cleared

Files Changed

  • Settings/KeychainManager.swift (new)
  • Settings/SettingsManager.swift (modified)

Test plan

  • Fresh install: enter API key in Settings → verify it persists across app restarts
  • Existing install: verify secrets auto-migrate (check "Migrated ... to Keychain" in logs)
  • Reset all settings → verify API key is cleared
  • Verify UserDefaults plist no longer contains API keys (inspect with plutil)

🤖 Generated with Claude Code

API keys and tokens were stored in UserDefaults, which is unencrypted
and readable from device backups. This is a security risk flagged in
Issue #1.

Changes:
- New KeychainManager.swift: thin wrapper around Security.framework
  for storing/retrieving sensitive strings in the iOS Keychain
- SettingsManager now stores geminiAPIKey, openClawHookToken, and
  openClawGatewayToken in Keychain instead of UserDefaults
- One-time automatic migration: existing UserDefaults secrets are
  moved to Keychain on first launch, then deleted from UserDefaults
- Non-sensitive settings (host, port, toggles) remain in UserDefaults

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
juliosuas pushed a commit to juliosuas/VisionClaw that referenced this pull request Apr 6, 2026
Mirrors the iOS Keychain migration (PR Intent-Lab#41). API keys and tokens were
stored in plain SharedPreferences (unencrypted XML on disk).

Changes:
- Add androidx.security:security-crypto dependency
- SettingsManager now uses EncryptedSharedPreferences (AES256-GCM)
  for geminiAPIKey, openClawHookToken, and openClawGatewayToken
- Non-sensitive settings (host, port, toggles) remain in plain prefs
- One-time auto-migration from plain prefs to encrypted on first launch
- resetAll() clears both encrypted and plain storage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant