# Authentication



CCP authenticates through OAuth 2.0 against "Sign in with cluster.app". On macOS, the session is stored in an owner-only (`0600`) file at `~/.ccp/session.json`; on Linux and Windows it's stored in the OS keyring. See [Session storage](#session-storage) to change this.

## Login [#login]

```bash
ccp auth login
# opens your default browser to approve the login, then:
# ◼ Logged in as you@example.com.
```

On a desktop machine, `ccp auth login` opens your browser and completes the
login through a PKCE-protected localhost callback. Over SSH, Mosh, or on a
headless Unix host (no `DISPLAY`/`WAYLAND_DISPLAY`), it automatically falls
back to device authorization instead — it prints a verification URL and code
to approve from any browser, including one on another machine.

Force either mode explicitly with `--browser` or `--device` (mutually
exclusive):

```bash
ccp auth login --device    # print a verification URL + code
ccp auth login --browser   # force the local browser + callback flow
```

If the browser fails to launch (or opens without completing the flow),
`ccp auth login` prints a same-machine authorization URL you can open
manually. When that failure happens during automatic (non-forced) browser
login, CCP also falls back to device login for you; a startup failure with
`--browser` does not fall back — retry or pass `--device`.

`ccp auth login` refuses to overwrite an existing saved session. If a session
is already saved, it errors and points you at `ccp auth print-access-token`
for normal use. Only the account owner should intentionally replace a saved
session — do so with `ccp auth login --replace-session`, which requires an
interactive terminal and an exact typed confirmation (`replace CCP session`).

## Scripted Login [#scripted-login]

`ccp auth login` reads no stdin, so it's not directly scriptable for CI or
ephemeral agent VMs. For non-interactive use, authenticate once on a
workstation and export the resulting token via `CCP_SESSION_TOKEN` — see
[Headless Mode](/docs/ccp/headless#authentication).

## Logout [#logout]

```bash
ccp auth logout
```

## Session storage [#session-storage]

Where CCP keeps your session depends on the platform:

* **macOS** — a `0600`, owner-only file at `~/.ccp/session.json` (default). macOS binds the Keychain's access ACL to the exact signed binary that wrote the item, so a rebuilt or unsigned `ccp` is treated as a new app and prompts for your login password on every read and write. The file avoids that friction. The tradeoff is a plaintext token at rest, protected by file permissions rather than the encrypted Keychain.
* **Linux / Windows** — the OS keyring (secret service / Credential Manager). There is no automatic fallback to the file: the selected backend is authoritative, so a keyring write failure is reported as an error rather than silently falling through to the file.

Override the default with the `CCP_AUTH_STORE` environment variable:

| Value                        | Effect                                             |
| ---------------------------- | -------------------------------------------------- |
| `file`                       | Use the `~/.ccp/session.json` file on any platform |
| `keychain` (alias `keyring`) | Use the OS keyring on any platform                 |

If you used CCP on macOS before the file became the default, your session lives only in the Keychain. After upgrading, run `ccp auth login` once to populate the file — or set `CCP_AUTH_STORE=keychain` to keep reading from the Keychain.

## Access Tokens [#access-tokens]

Export your access token for use in scripts or API calls:

```bash
# Print as export statement (use with eval)
eval $(ccp auth export-access-token)

# Print raw token to stdout
ccp auth print-access-token
```

## Shell Wrapper [#shell-wrapper]

For convenience, add the shell wrapper to auto-export tokens:

```bash
# Print the wrapper function
ccp auth shell-setup

# Add to your ~/.zshrc:
ccp() {
  case "$1:$2" in
    auth:export-access-token)
      eval $(command ccp "$@")
      ;;
    *)
      command ccp "$@"
      ;;
  esac
}
```

## Dev VM Credentials [#dev-vm-credentials]

The `ccp` running **inside** a dev VM no longer relies on a snapshot of your
session. Instead, dev VMs get a VM-scoped CCP identity: infra-api mints a
short-lived (five-minute) access token for the VM and keeps it renewed
automatically while the VM is running, paused, or resumable, so the in-VM
`ccp` stays authenticated without you re-running anything. The underlying
client secret never enters the guest.

There is no `ccp auth sync`/`ccp auth desync` command anymore — that manual
snapshot/removal flow has been retired in favor of this automatic renewal.

## Auth Subcommands [#auth-subcommands]

| Command                                | Description                                                              |
| -------------------------------------- | ------------------------------------------------------------------------ |
| `ccp auth login [--browser\|--device]` | Log in via browser PKCE (desktop) or device authorization (SSH/headless) |
| `ccp auth login --replace-session`     | Interactively replace an existing saved session (account owner only)     |
| `ccp auth logout`                      | Log out and clear credentials                                            |
| `ccp auth export-access-token`         | Print `export TOKEN="..."` for eval                                      |
| `ccp auth print-access-token`          | Print raw JWT to stdout                                                  |
| `ccp auth shell-setup`                 | Print shell wrapper for `~/.zshrc`                                       |
