# How the HTTP Client Factory with CookieJar Manages Session State in ipatool

> Discover how ipatool's HTTP client factory and CookieJar manage session state by persisting authentication cookies across invocations. Learn the technical details of this Go implementation.

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

---

**The HTTP client factory in `majd/ipatool` creates reusable clients that automatically persist authentication cookies across command-line invocations by wiring a custom `CookieJar` into Go's `http.Client` and explicitly calling `Save()` after each request.**

The `majd/ipatool` repository implements a robust session management system that allows the CLI to maintain authenticated state with Apple's App Store between runs. By leveraging a generic HTTP client factory combined with a persistent CookieJar, the tool eliminates the need to re-authenticate on every execution while keeping sensitive session data securely stored on disk.

## Understanding the HTTP Client Factory Architecture

The session management system centers on the generic `NewClient` factory defined in **[`pkg/http/client.go`](https://github.com/majd/ipatool/blob/main/pkg/http/client.go)**. This factory accepts a `CookieJar` implementation through the `Args` struct and injects it directly into the underlying Go `http.Client` via the `Jar` field.

The `CookieJar` interface itself is defined in **[`pkg/http/cookiejar.go`](https://github.com/majd/ipatool/blob/main/pkg/http/cookiejar.go)** and extends Go's standard `http.CookieJar` by adding a critical `Save()` method. This extension enables the persistent storage mechanism that standard library cookie jars lack.

```go
// Conceptual interface from pkg/http/cookiejar.go
type CookieJar interface {
    http.CookieJar  // Embeds standard Add/SetCookies methods
    Save() error    // Custom persistence hook
}

```

When `NewClient` instantiates the client, it sets `Jar: args.CookieJar`, ensuring that all outgoing requests automatically include stored cookies and that any `Set-Cookie` headers from responses are captured by the jar.

## Persistent Cookie Storage Implementation

The concrete cookie persistence logic resides in **[`cmd/common.go`](https://github.com/majd/ipatool/blob/main/cmd/common.go)** within the `newCookieJar` helper function. This function creates a jar that targets a specific file path in the user's configuration directory.

By default, cookies serialize to a file named `cookies` located at `$HOME/.config/ipatool/cookies`. This location ensures that session state survives process termination and remains available for subsequent CLI invocations.

```go
// Simplified concept from cmd/common.go
func newCookieJar(m machine.Machine) http.CookieJar {
    configDir := m.ConfigDir()  // Returns $HOME/.config/ipatool
    jarPath := filepath.Join(configDir, "cookies")
    // Returns a CookieJar implementation that reads/writes to jarPath
    return http.NewCookieJar(jarPath)
}

```

## The Four-Phase Session Lifecycle

The HTTP client factory manages session state through a predictable lifecycle that guarantees consistency between network operations and disk storage.

### 1. Construction Phase

During client initialization, the factory receives a `CookieJar` implementation via the `CookieJar` field in the `Args` parameter. The factory immediately wires this jar into the HTTP client configuration, establishing the foundation for automatic cookie handling.

```go
// From pkg/http/client.go conceptual implementation
func NewClient[T any](args Args) Client[T] {
    return &client[T]{
        httpClient: &http.Client{
            Jar: args.CookieJar,  // Automatic cookie injection
            // ... other configuration
        },
        cookieJar: args.CookieJar,  // Stored for later Save() calls
    }
}

```

### 2. Request Transmission

When the client executes a request via the `Send` method, Go's standard HTTP client automatically consults the attached `Jar`. Any cookies previously stored for the target domain are automatically added to the request headers before transmission to Apple's servers.

### 3. Response Processing

Upon receiving the HTTP response, the standard library automatically invokes the jar's storage methods to capture any `Set-Cookie` headers present in the response. This updates the in-memory cookie store with fresh session tokens or authentication cookies.

### 4. Persistence Phase

After the response completes processing within the `Send` method, the client explicitly invokes `c.cookieJar.Save()`. This writes the current state of the cookie jar to the configured disk location, ensuring that authentication tokens persist beyond the current process lifetime.

## Practical Implementation Examples

### Creating a Client with Persistent Session State

To build a client that maintains authentication across multiple CLI runs, combine the factory with the configuration helper:

```go
import (
    "github.com/majd/ipatool/pkg/http"
    "github.com/majd/ipatool/pkg/machine"
)

func buildAuthenticatedClient() http.Client[MyResult] {
    // Initialize machine abstraction for path resolution
    m := machine.New()
    
    // Create the persistent jar targeting $HOME/.config/ipatool/cookies
    jar := newCookieJar(m)
    
    // Instantiate client with session support
    return http.NewClient[MyResult](http.Args{
        CookieJar: jar,
    })
}

```

### Executing Authenticated Requests

Once constructed, the client automatically handles cookie transmission and updates:

```go
client := buildAuthenticatedClient()

req := http.Request{
    Method: "GET",
    URL:    "https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate",
    Headers: map[string]string{
        "Accept": "application/json",
    },
    ResponseFormat: http.ResponseFormatJSON,
}

result, err := client.Send(req)
if err != nil {
    // Handle network or authentication errors
    log.Fatal(err)
}

// Subsequent requests automatically include session cookies

```

### Verifying Persisted Sessions

You can inspect the serialized session data directly to verify persistence:

```bash

# View stored cookies (Go gob-encoded format)

cat $HOME/.config/ipatool/cookies

```

## Summary

- **[`pkg/http/client.go`](https://github.com/majd/ipatool/blob/main/pkg/http/client.go)** implements the `NewClient` factory that injects a `CookieJar` into the standard HTTP client.
- **[`pkg/http/cookiejar.go`](https://github.com/majd/ipatool/blob/main/pkg/http/cookiejar.go)** defines the extended interface adding the `Save()` method for persistence.
- **[`cmd/common.go`](https://github.com/majd/ipatool/blob/main/cmd/common.go)** provides `newCookieJar`, which configures disk storage at `$HOME/.config/ipatool/cookies`.
- The `Send` method automatically persists cookies after each request via `c.cookieJar.Save()`.
- Session state survives process restarts, enabling persistent authentication with Apple's App Store.

## Frequently Asked Questions

### Where does ipatool store session cookies?

`ipatool` stores session cookies in a file named `cookies` within the user's configuration directory, specifically at `$HOME/.config/ipatool/cookies`. This path is determined by the `machine.Machine` interface implementation in [`cmd/common.go`](https://github.com/majd/ipatool/blob/main/cmd/common.go), which resolves the appropriate config directory for the operating system.

### How does the CookieJar interface differ from Go's standard library?

While Go's standard `http.CookieJar` interface only defines methods for setting and retrieving cookies during active HTTP transactions, the `ipatool` implementation in [`pkg/http/cookiejar.go`](https://github.com/majd/ipatool/blob/main/pkg/http/cookiejar.go) embeds this interface and adds a `Save() error` method. This additional method enables explicit serialization of the cookie store to disk, a capability the standard library lacks.

### When exactly are cookies saved to disk?

Cookies persist immediately after each HTTP request completes within the `Send` method of the client defined in [`pkg/http/client.go`](https://github.com/majd/ipatool/blob/main/pkg/http/client.go). The implementation explicitly calls `c.cookieJar.Save()` after processing the response, ensuring that any authentication tokens received during the request are written to disk before the function returns.

### Can I use a custom CookieJar implementation?

Yes, the `NewClient` factory accepts any implementation of the `CookieJar` interface through the `Args` parameter. As long as the custom implementation satisfies both the embedded `http.CookieJar` interface and the additional `Save()` method, it will integrate seamlessly with the client's session management workflow.