# car-auth

Shared Parslee OAuth2 PKCE + token/keychain core for [Common Agent
Runtime](https://github.com/Parslee-ai/car).

## What it does

One implementation of Parslee sign-in, used by two surfaces:

- **`car-cli`** — `car auth login parslee` (loopback redirect flow).
- **`car-server`** — the `auth.*` JSON-RPC surface that CAR Host.app's
  signup/sign-in GUI drives.

| Function | Purpose |
|----------|---------|
| `pkce_verifier` / `pkce_challenge` / `new_state` | PKCE S256 + CSRF `state` |
| `authorize_url` | Build the `/connect/authorize` URL |
| `exchange_code` | Redeem `code` + verifier at `/connect/token` |
| `store_tokens` / `clear_tokens` / `access_token` | OS-keychain persistence |
| `api_base` | Resolve API base (override → stored → default) |
| `fetch_status` | `/connect/session` for the signed-in token |

## Keychain contract

Tokens land in the default `"car"` keychain service under
`PARSLEE_ACCESS_TOKEN` / `PARSLEE_REFRESH_TOKEN` /
`PARSLEE_ACCESS_TOKEN_EXPIRES_AT` / `PARSLEE_API_BASE` — the exact
keys `car-inference` reads at request time for `parslee/*`
(`ModelSource::Proprietary` / `OAuth2Pkce`). Persistence goes through
[`car-secrets`](../car-secrets).

## Trust boundary

The crate is deliberately headless: **no browser-open, no loopback
listener**. Those are surface-specific — the CLI runs a localhost
listener; the daemon hands the authorize URL to the in-process GUI,
which carries the PKCE verifier + `state` between `auth.start` and
`auth.complete` (same trust boundary as the keychain the token lands
in). Onboarding/consent for brand-new accounts is handled entirely by
Parslee's existing hosted web flow during the browser hand-off — this
crate (and CAR generally) never touches consent, by design.
