# How IPATool Handles Expired Password Tokens During Login

> Learn how IPATool detects expired password tokens during login by mapping authentication failures to appstore.ErrPasswordTokenExpired and forcing re-authentication.

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

---

**IPATool detects expired password tokens by mapping Apple’s authentication failures to the exported error `appstore.ErrPasswordTokenExpired`, which CLI commands check using `errors.Is()` to force immediate re-authentication.**

When interacting with Apple’s App Store programmatically, session management is critical for maintaining authenticated access. In the `majd/ipatool` repository, the authentication layer implements a fail-fast strategy for expired credentials, specifically targeting password token lifecycle management. This article examines how IPATool stores authentication tokens, detects expiration conditions, and surfaces these errors to compel users to re-run `ipatool login`.

## Authentication Flow and Token Storage

During the initial authentication sequence, IPATool obtains a **password token** from Apple’s servers and persists it for subsequent requests.

### Login and SAP Action Signing

The `Login` function in [`pkg/appstore/appstore_login.go`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_login.go) (lines 58-73) constructs an XML payload and signs it using the **SAP action signer**. This signed request is sent to Apple’s authentication endpoint, which returns a `PasswordToken` in the response.

### Keychain Persistence

Immediately after receiving the token, the code stores it in the system keychain via `t.keychain.Set("account", …)` as shown in lines 69-71 of [`pkg/appstore/appstore_login.go`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_login.go). This ensures the token persists across CLI invocations and remains securely stored outside of application memory.

## Detecting Expiration in App Store Responses

When Apple’s servers reject a request due to token expiry (typically via HTTP 401/403 responses indicating the password token is invalid), IPATool interprets this as a *password-token-expired* condition.

The response parser maps these HTTP failures to the exported error `appstore.ErrPasswordTokenExpired`. This error is defined in the `appstore` package and serves as the canonical signal for expired credentials across the entire codebase.

## CLI Command Error Handling

All CLI commands that interact with the App Store—including `download`, `purchase`, `list-versions`, and `get-version-metadata`—implement explicit checks for token expiration.

In [`cmd/download.go`](https://github.com/majd/ipatool/blob/main/cmd/download.go) (lines 57-60) and [`cmd/purchase.go`](https://github.com/majd/ipatool/blob/main/cmd/purchase.go) (lines 45-48), the commands use `errors.Is(err, appstore.ErrPasswordTokenExpired)` to detect the condition. When identified, the CLI prints a clear message stating "Your session has expired – please log in again" and exits with a non-zero status code, forcing the user to execute `ipatool login` to generate a fresh token.

## Code Implementation Examples

### Authenticating and Handling Expiry

The following pattern demonstrates how the `appstore` package handles login and token expiration detection:

```go
import (
    "errors"
    "github.com/majd/ipatool/v2/pkg/appstore"
)

func loginAndUse(email, password string) error {
    // Attempt login
    out, err := appstore.Login(appstore.LoginInput{
        Email:    email,
        Password: password,
    })
    if err != nil {
        if errors.Is(err, appstore.ErrPasswordTokenExpired) {
            // Token expired – ask the user to log in again
            return fmt.Errorf("session expired – please run `ipatool login`")
        }
        return err
    }

    // Use the returned account (contains a fresh PasswordToken)
    _ = out.Account
    return nil
}

```

### Command-Level Error Detection

High-level CLI commands implement retry loops that specifically catch expiration errors:

```go
func runDownload(...) error {
    for {
        err := appstore.Download(...)
        if err != nil && errors.Is(err, appstore.ErrPasswordTokenExpired) {
            fmt.Fprintln(os.Stderr, "Your session has expired. Run `ipatool login` and try again.")
            return err
        }
        // handle other errors …
        break
    }
    return nil
}

```

### Unit Testing Token Expiration

The test suite in [`pkg/appstore/appstore_login_test.go`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_login_test.go) verifies this behavior by simulating server responses:

```go
It("returns password token expired error", func() {
    // Simulate a server response that indicates an expired token
    mockServer.Returns(401, `{"error":"password token is expired"}`)
    err := appstore.SomeOperation(...)
    Expect(err).To(MatchError(appstore.ErrPasswordTokenExpired))
})

```

## Summary

- IPATool stores the **password token** in the system keychain via [`pkg/appstore/appstore_login.go`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_login.go) after signing requests with the SAP action signer.
- Expired tokens trigger the exported error **`appstore.ErrPasswordTokenExpired`** when Apple returns authentication failures.
- CLI commands in [`cmd/download.go`](https://github.com/majd/ipatool/blob/main/cmd/download.go), [`cmd/purchase.go`](https://github.com/majd/ipatool/blob/main/cmd/purchase.go), and related files check for this error using **`errors.Is()`** to detect session expiry.
- The implementation follows a **fail-fast** approach, requiring users to explicitly re-run `ipatool login` rather than attempting automatic token refresh.

## Frequently Asked Questions

### What error does IPATool return when a password token expires?

IPATool returns the exported error variable **`appstore.ErrPasswordTokenExpired`**. This error is mapped from HTTP 401/403 responses where Apple indicates the password token is no longer valid, allowing the CLI and library consumers to detect expiration conditions programmatically.

### How does IPATool store authentication tokens between commands?

After successful authentication in [`pkg/appstore/appstore_login.go`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_login.go) (lines 69-71), IPATool stores the token in the system keychain using `t.keychain.Set("account", …)`. This persists the credentials securely across separate CLI invocations without requiring repeated password entry.

### Which CLI commands check for expired password tokens?

All App Store interaction commands check for expired tokens, specifically `download`, `purchase`, `list-versions`, and `get-version-metadata`. Each command implements the check using `errors.Is(err, appstore.ErrPasswordTokenExpired)` as shown in [`cmd/download.go`](https://github.com/majd/ipatool/blob/main/cmd/download.go) (lines 57-60) and [`cmd/purchase.go`](https://github.com/majd/ipatool/blob/main/cmd/purchase.go) (lines 45-48).

### Does IPATool automatically refresh expired password tokens?

No. According to the source code in `majd/ipatool`, the tool adopts a **fail-fast** strategy. When a token expires, the operation aborts immediately and the CLI prompts the user to run `ipatool login` again. This design ensures explicit user consent for re-authentication rather than attempting automated credential refresh cycles.