How ipatool Uses the macOS Keychain to Securely Store Apple ID Credentials
ipatool stores Apple ID credentials in the macOS Keychain as encrypted generic password items, using the go-keychain library with a configurable passphrase to protect data at rest, and retrieves them via a Go interface abstraction that never exposes secrets in plain text files or environment variables.
The open-source tool majd/ipatool is a command-line utility for searching, purchasing, and downloading iOS apps. Because it requires Apple ID authentication, the project implements a secure credential management system that delegates all secret storage to the native macOS Keychain instead of configuration files or environment variables.
Keychain Architecture and Abstraction
The project isolates platform-specific cryptography behind a clean Go interface. This design allows the application logic to remain agnostic about the underlying macOS security APIs while ensuring all sensitive data is encrypted at rest.
The Keychain Interface
The contract is defined in pkg/keychain/keychain.go, which declares three essential operations:
type Keychain interface {
Get(key string) ([]byte, error)
Set(key string, data []byte) error
Remove(key string) error
}
Any component requiring credential storage interacts with this interface, never with the OS directly.
The Keyring Implementation
Concrete operations live in separate files under pkg/keychain/:
keychain_set.goimplementsSetto create encrypted entrieskeychain_get.goimplementsGetto retrieve decrypted byteskeychain_remove.goimplementsRemoveto delete items
These methods delegate to a Keyring wrapper (pkg/keychain/keyring.go) that translates method calls into native macOS Keychain operations via the third-party library github.com/byteness/go-keychain. The library writes data as generic password items, automatically applying the user's passphrase for encryption.
Storing Apple ID Credentials
When a user executes ipatool login, the credential storage flow begins in cmd/common.go. The CLI constructs a Keychain instance using newKeychain(), passing the optional --keychain-passphrase flag to support non-interactive environments:
kc, err := newKeychain(machine, logger, interactive)
After successful authentication with Apple's servers, pkg/appstore/appstore_login.go serializes the Account struct—containing the Apple ID, password hash, and session tokens—and persists it:
err = t.keychain.Set("account", data)
The Set method writes the bytes as a generic password entry labeled under the application's namespace, encrypted by the macOS Keychain using the supplied passphrase.
Retrieving Stored Credentials
Commands that require authentication, such as ipatool download or ipatool purchases, invoke pkg/appstore/appstore_account_info.go. This file calls:
data, err := t.keychain.Get("account")
The Get method retrieves the encrypted item from the macOS Keychain, decrypts it using the system's secure enclave (prompting for the passphrase if necessary), and returns the raw bytes. The application then unmarshals the data back into the Account struct for API requests.
Revoking and Deleting Credentials
To remove stored secrets, users run ipatool revoke. The implementation in pkg/appstore/appstore_revoke.go handles cleanup:
err := t.keychain.Remove("account")
This triggers the underlying DeleteItem routine in the go-keychain library, permanently erasing the generic password entry from the user's Keychain and ensuring no residual data remains on disk.
Non-Interactive Environments and Passphrase Handling
For CI/CD pipelines or automated scripts, the tool supports a --keychain-passphrase flag defined in cmd/root.go. When provided, cmd/common.go forwards this value to the Keyring initialization, unlocking the Keychain without user interaction. If the flag is omitted in a non-interactive shell, the command aborts with a clear error to prevent hanging prompts.
Practical Code Examples
Logging in via CLI with Explicit Passphrase
ipatool login \
--apple-id my@example.com \
--password secret123 \
--keychain-passphrase mySecurePassphrase
This command chains newKeychain() with the provided passphrase, then calls keychain.Set("account", …) to encrypt and store the credentials.
Programmatic Credential Retrieval
import (
"github.com/majd/ipatool/v2/pkg/appstore"
"github.com/majd/ipatool/v2/pkg/keychain"
)
// Initialize the keychain with a specific label
kc := keychain.New(keychain.Args{
Keyring: keychain.NewKeyring(),
Label: "ipatool",
})
// Create client
client := appstore.New(appstore.Args{
Keychain: kc,
})
// Retrieve decrypted account info
account, err := client.AccountInfo()
if err != nil {
log.Fatalf("Failed to read credentials: %v", err)
}
fmt.Printf("Authenticated as: %s\n", account.AppleID)
Revoking Credentials Programmatically
if err := client.Revoke(); err != nil {
log.Fatalf("Failed to remove credentials: %v", err)
}
fmt.Println("Apple ID credentials removed from keychain")
Summary
- No plaintext storage: The
Keychaininterface inpkg/keychain/keychain.goensures passwords never touch disk unencrypted. - macOS-native encryption: All data is stored as generic password items via
github.com/byteness/go-keychain, leveraging the OS security architecture. - Three-operation lifecycle:
Set(login),Get(usage), andRemove(revoke) inappstore_login.go,appstore_account_info.go, andappstore_revoke.gorespectively. - Non-interactive support: The
--keychain-passphraseflag incmd/root.goenables automated workflows without compromising security.
Frequently Asked Questions
Does ipatool store my Apple ID password in plain text?
No. According to the source code in pkg/appstore/appstore_login.go, the password is serialized into a byte slice and passed to keychain.Set("account", data), which writes it as an encrypted generic password item in the macOS Keychain using the go-keychain library. The tool never writes credentials to configuration files, logs, or environment variables.
How do I use ipatool in CI/CD pipelines without interactive prompts?
Use the --keychain-passphrase flag defined in cmd/root.go and implemented in cmd/common.go. Supplying this flag allows the Keyring to unlock the macOS Keychain programmatically. If the flag is omitted in a non-interactive environment, the tool exits immediately rather than hanging on a GUI prompt.
Where exactly are the credentials stored on my Mac?
The credentials are stored in the user's default macOS Keychain as a generic password item with the service label "ipatool". The actual encryption and file system placement are managed by the operating system's security daemon, not by ipatool itself. This location is accessible via the Keychain Access utility or the security command-line tool.
How do I completely remove stored credentials from my system?
Run ipatool revoke from the CLI, or programmatically call the Revoke() method on the app store client. This invokes keychain.Remove("account") in pkg/appstore/appstore_revoke.go, which deletes the underlying Keychain item via the go-keychain library's DeleteItem function, ensuring the secret is irretrievably removed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →