# How ipatool Uses the macOS Keychain to Securely Store Apple ID Credentials

> Learn how ipatool securely stores and retrieves Apple ID credentials using the macOS Keychain and go-keychain. Secrets are encrypted at rest, never exposed in plain text.

- Repository: [Majd/ipatool](https://github.com/majd/ipatool)
- Tags: internals
- Published: 2026-09-06

---

**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`](https://github.com/majd/ipatool/blob/main/pkg/keychain/keychain.go), which declares three essential operations:

```go
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.go`](https://github.com/majd/ipatool/blob/main/keychain_set.go) implements `Set` to create encrypted entries
- [`keychain_get.go`](https://github.com/majd/ipatool/blob/main/keychain_get.go) implements `Get` to retrieve decrypted bytes
- [`keychain_remove.go`](https://github.com/majd/ipatool/blob/main/keychain_remove.go) implements `Remove` to delete items

These methods delegate to a `Keyring` wrapper ([`pkg/keychain/keyring.go`](https://github.com/majd/ipatool/blob/main/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`](https://github.com/majd/ipatool/blob/main/cmd/common.go). The CLI constructs a `Keychain` instance using `newKeychain()`, passing the optional `--keychain-passphrase` flag to support non-interactive environments:

```go
kc, err := newKeychain(machine, logger, interactive)

```

After successful authentication with Apple's servers, [`pkg/appstore/appstore_login.go`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_login.go) serializes the `Account` struct—containing the Apple ID, password hash, and session tokens—and persists it:

```go
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`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_account_info.go). This file calls:

```go
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`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_revoke.go) handles cleanup:

```go
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`](https://github.com/majd/ipatool/blob/main/cmd/root.go). When provided, [`cmd/common.go`](https://github.com/majd/ipatool/blob/main/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

```bash
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

```go
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

```go
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 `Keychain` interface in [`pkg/keychain/keychain.go`](https://github.com/majd/ipatool/blob/main/pkg/keychain/keychain.go) ensures 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), and `Remove` (revoke) in [`appstore_login.go`](https://github.com/majd/ipatool/blob/main/appstore_login.go), [`appstore_account_info.go`](https://github.com/majd/ipatool/blob/main/appstore_account_info.go), and [`appstore_revoke.go`](https://github.com/majd/ipatool/blob/main/appstore_revoke.go) respectively.
- **Non-interactive support**: The `--keychain-passphrase` flag in [`cmd/root.go`](https://github.com/majd/ipatool/blob/main/cmd/root.go) enables 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`](https://github.com/majd/ipatool/blob/main/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`](https://github.com/majd/ipatool/blob/main/cmd/root.go) and implemented in [`cmd/common.go`](https://github.com/majd/ipatool/blob/main/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`](https://github.com/majd/ipatool/blob/main/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.