# @deque/axe-auth

CLI for authenticating with Deque services via the OAuth 2.0 Authorization Code + PKCE flow (RFC 6749, RFC 7636, RFC 8252 §7.3). Tokens are persisted to the OS keychain so subsequent invocations can refresh silently.

## Installation

```sh
npm install -g @deque/axe-auth
```

Or run directly:

```sh
npx @deque/axe-auth
```

## Usage

```sh
axe-auth <command> [options]
```

Commands:

| Command  | Description                                                                     |
| -------- | ------------------------------------------------------------------------------- |
| `login`  | Open a browser, complete the OAuth flow, persist tokens to the OS keychain.     |
| `logout` | Revoke the stored refresh token server-side and clear the local keychain entry. |
| `token`  | Print a currently-valid access token to stdout, refreshing silently if needed.  |

Run `axe-auth <command> --help` for command-specific options.

### Common configuration

`axe-auth` discovers its OAuth coordinates by calling `<server>/api/sso-config` on the axe server. Users only supply (or default to) the axe server URL — never the underlying Keycloak URL, realm, or client ID.

| Flag                         | Env var          | Notes                                                                                                                                                                                                            |
| ---------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--server`                   | `AXE_SERVER_URL` | axe server URL. Defaults to `https://axe.deque.com` (SaaS prod) when neither flag nor env var is set, so SaaS users pass no flags at all. Customers on other deployments override with their own axe server URL. |
| `--allow-insecure-issuer`    | —                | Permit non-loopback http URLs (default is https only; loopback http is always allowed). Applies to `login` only; `token` and `logout` use the policy persisted at login.                                         |
| `--no-allow-insecure-issuer` | —                | Force `allowInsecureIssuer=false` for the new `login` (and the entry it persists). Mutually exclusive with `--allow-insecure-issuer`. `token` and `logout` ignore this flag.                                     |

`axe-auth` stores one set of credentials per machine. On a successful `login`, the discovered issuer / client / insecure-issuer values are persisted alongside the tokens, so subsequent `axe-auth token` and `axe-auth logout` invocations work flag-free — a typical scripted call is just `$(axe-auth token)`.

There is no concurrent multi-issuer support. Logging in to a second deployment overwrites the previous tokens; an interactive prompt confirms the switch before destroying the existing session, and `--force` skips the prompt.

### Exit codes

| Code | Meaning                                                                                                                                                                                                                                                              |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`  | Success.                                                                                                                                                                                                                                                             |
| `1`  | Not authenticated: `axe-auth token` with no stored credentials, or stored credentials are unusable (corrupt, version-mismatch, expired without a usable refresh token, or refresh rejected by the server). Branch on this in scripts that need to trigger a `login`. |
| `2`  | Usage or runtime error: unknown command, bad flag, missing required configuration, OAuth flow failure, or keychain failure. Details written to stderr.                                                                                                               |
| `3`  | Cancelled: `axe-auth login` declined at the re-authentication prompt. Distinct from `1` so scripts can tell "needs login" from "user bailed."                                                                                                                        |

### Examples

```sh
# First-time login on Deque SaaS prod (opens your browser, no flags needed)
axe-auth login

# First-time login on a non-SaaS-prod deployment
axe-auth login --server https://axe.customer.dequecloud.com

# Pull a fresh access token for use in shell substitution
docker run -e AXE_ACCESS_TOKEN="$(axe-auth token)" axe-mcp-server

# Sign out
axe-auth logout
```

## Architecture

See [`docs/architecture.md`](./docs/architecture.md) for the system architecture: components, per-verb data flow, communication security, and persisted data.

Deeper design notes:

- [`oauth-flow.md`](./docs/oauth-flow.md) — protocol-level walkthrough of the OAuth 2.0 + PKCE flow.
- [`callback-server.md`](./docs/callback-server.md) — `startCallbackServer` API and RFC 8252 conformance.
- [`callback-page.md`](./docs/callback-page.md) — HTML response design, branding, and CSP rationale.

## Caveats

- **`axe-auth token` exposes the access token in the shell process list and terminal scrollback.** When used as `$(axe-auth token)` the token briefly appears in the parent process's argument list (observable via `ps`); printed directly to a terminal it also persists in the scrollback buffer (iTerm2, Terminal.app, tmux all retain output by default), which can outlast the token's TTL on a shared machine. OAuth access tokens are short-lived (typically minutes), which limits exposure compared to a static API key. Prefer redirecting into a file (`axe-auth token > /tmp/tok && chmod 600 /tmp/tok`) or directly into an env var (`export AXE_ACCESS_TOKEN=$(axe-auth token)`) on platforms where this matters.
- **Linux keychain support is untested.** `@napi-rs/keyring` requires a working D-Bus Secret Service (GNOME Keyring, KWallet, etc.). Users on headless or minimal-desktop Linux environments may see `KEYRING_UNAVAILABLE`; a file-backed `TokenStore` fallback is tracked as a follow-up (internal issue #464).
