URL: /cli/auth

---
title: "auth"
description: "Authenticate with squirrelscan for cloud features and publishing"
---

The `auth` command manages authentication for [cloud features](/cloud) - cloud rules, browser rendering, and publishing reports. Logging in grants 1,000 free credits on new accounts.

There are two ways to authenticate:

- **`squirrel auth login`** — interactive browser sign-in, tied to your user. Best for local development.
- **`SQUIRREL_API_TOKEN` env var** — a long-lived, org-scoped API key. Best for CI, headless agents, and automation. See [API token](#api-token-ci--headless).

## Subcommands

- [login](#login) - Sign in to squirrelscan
- [logout](#logout) - Sign out and revoke token
- [status](#status) / [whoami](#whoami) - Show the active credential (source, scopes, org)

---

## API token (CI / headless)

For CI and headless use, set the `SQUIRREL_API_TOKEN` environment variable to an
**org API key** created in the [dashboard](https://app.squirrelscan.com/settings/api-keys).
No `squirrel auth login` is required — the token is read straight from the
environment.

```bash
SQUIRREL_API_TOKEN=sq_xxxxxxxxxxxx squirrel audit https://example.com
```

API keys are org-scoped (audits debit the org's credit pool) and carry
[scopes](#scopes). They are sent as `Authorization: Bearer <token>`, the same
transport as a login session — the API tells them apart by prefix.

### Credential precedence

When the CLI needs to authenticate a cloud call it resolves **one** credential,
highest priority first:

1. **`SQUIRREL_API_TOKEN`** (if set and non-empty) — **authoritative**. It
   **overrides a logged-in session**, and is **fail-closed**: if the token is
   invalid (revoked, expired, or minted for a different environment) the CLI
   **errors** — it does **not** silently fall back to your login session. This
   keeps CI predictable and avoids the "I exported a token but it used my
   personal session" surprise (the same convention as `gh`, the Stripe CLI, and
   AWS).
2. **Logged-in session** (`squirrel auth login`) — used only when
   `SQUIRREL_API_TOKEN` is unset or empty.
3. **Unauthenticated** — local / deterministic-only audits.

The env token is **never** written to `~/.squirrel/settings.json`. An empty or
whitespace-only `SQUIRREL_API_TOKEN` is treated as unset (it falls through to
the login session).

<Note>
When `SQUIRREL_API_TOKEN` is set, `squirrel auth login` and `squirrel auth
logout` still operate on your *login session* — but the env var wins at runtime
until you unset it. Both commands print a note when the env var is shadowing the
session.
</Note>

### Environment-aware prefixes

Keys carry an environment segment so a dev key can't accidentally hit production
(and vice-versa) — the API rejects cross-environment keys with a clear error.

| Prefix | Environment |
|--------|-------------|
| `sq_…` | production |
| `sq_dev_…` | development |
| `sqcli_…` | legacy user login token (browser flow) |

### Scopes

API keys carry per-resource scopes (`resource:action`). A key is only allowed to
call endpoints covered by its scopes; a request outside its scopes returns a
clear error naming the missing scope.

| Scope | Grants |
|-------|--------|
| `audits:write` | Run audits, submit cloud renders |
| `audits:read` | Read published reports |
| `credits:read` | Read credit balance + ledger |
| `org:read` | Read org details |
| `org:write` | Manage org (invites, settings) |

Login sessions are unscoped (full access) — scopes only apply to API keys.

### GitHub Actions

```yaml
- name: Audit
  env:
    SQUIRREL_API_TOKEN: ${{ secrets.SQUIRREL_API_TOKEN }}
  run: squirrel audit https://example.com --yes
```

Store the key as a repository or organization secret — never commit it. A `CI`
preset key (audits + credits read) is enough for most pipelines.

---

## login

Authenticate with squirrelscan using device authorization flow.

### Usage

```bash
squirrel auth login [options]
```

### Options

| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--device-name` | `-d` | Name for this device | hostname |

### Example

```bash
squirrel auth login
```

Opens browser for authentication. After completing login:
```
✓ Authenticated as you@example.com
  Device: macbook-pro
  Expires: Feb 2, 2027
```

### Troubleshooting

**`Couldn't reach the auth server …`** — the API was unreachable (it's down, or
you're pointed at a local/dev server that isn't running). The error names the
server it tried. To target production, unset or set the override:

```bash
SQUIRREL_API_SERVER=https://api.squirrelscan.com squirrel auth login
```

**Session expired / cloud unavailable during an audit** — if your *login token*
has expired (or the API can't be reached), `squirrel audit` keeps working with
**local checks only** and prints the reason on the `Account` line. Cloud
features (renders, AI summary, tech detection) are skipped cleanly — nothing
fails. In an interactive terminal you'll be asked whether to continue
local-only; agents, CI, piped output and `--yes` proceed automatically. Run
`squirrel auth login` to restore cloud features.

**`SQUIRREL_API_TOKEN was rejected …`** — an [API token](#api-token-ci--headless)
is fail-closed: when set, an invalid token (revoked, expired, or wrong
environment) **errors the command** rather than degrading to local-only. Fix or
rotate the key in the [dashboard](https://app.squirrelscan.com/settings/api-keys),
or unset `SQUIRREL_API_TOKEN` to use your login session instead.

---

## logout

Sign out and revoke the current authentication token.

### Usage

```bash
squirrel auth logout
```

### Example

```bash
squirrel auth logout
```

```
✓ Signed out successfully
```

---

## status

Show the active credential — its **source** (login session vs `SQUIRREL_API_TOKEN`
env var), and, for API keys, the org and granted **scopes**.

### Usage

```bash
squirrel auth status [options]
```

### Options

| Option | Description |
|--------|-------------|
| `--json` | Output as JSON |

### Example

Logged-in session:

```bash
squirrel auth status
```

```
Authenticated as you@example.com
  Source: logged-in session
  Device: macbook-pro
  Expires: Feb 2, 2027
  Credits: 950
```

With an API key via `SQUIRREL_API_TOKEN` (note the `Source` line and the scopes):

```
Authenticated as ci@acme.com
  Source: SQUIRREL_API_TOKEN env var
  Org: Acme Inc
  Key: CI – production
  Scopes: audits:write, credits:read
  Key env: production
  Credits: 12,400
```

When an env token shadows a logged-in session, `status` notes which session is
being overridden.

JSON output:

```bash
squirrel auth status --json
```

```json
{
  "authenticated": true,
  "source": "env",
  "user": { "id": "u_123", "email": "ci@acme.com", "name": "CI Bot" },
  "token": { "deviceName": null, "expiresAt": null },
  "apiKey": {
    "name": "CI – production",
    "scopes": ["audits:write", "credits:read"],
    "keyEnv": "production"
  },
  "org": { "id": "org_123", "name": "Acme Inc" }
}
```

<Note>
**`--json` schema additions.** The `user` and `token` objects are unchanged. New
fields are **additive**: `source` (`"env"` | `"login"`), and — when authenticated
with an API key — `apiKey` (name, scopes, keyEnv) and `org` (id, name); plus
`shadowedLoginEmail` when an env token is overriding a login session. One value
change: `token.expiresAt` is `null` for env tokens (it was always a string for
login sessions). Parse defensively (`expiresAt` may be `null`).
</Note>

---

## whoami

Alias for [`status`](#status) — the conventional name for "who am I
authenticated as". Same output and `--json` flag.

```bash
squirrel auth whoami
```

---

## Related

- [Dashboard](/dashboard) - Manage reports online
- [report --publish](/cli/report#publishing) - Publish reports
