> ## Documentation Index
> Fetch the complete documentation index at: https://developer.lofty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> lofty-cli supports four authentication methods. They are evaluated in priority order at every command invocation: the first one that yields a valid access token wins.

`lofty-cli` supports four authentication methods. They are evaluated **in priority order** at every command invocation: the first one that yields a valid access token wins. Pick the method that fits how you are running the CLI.

| Method                   | Best for                          | Configured via                                                   |
| ------------------------ | --------------------------------- | ---------------------------------------------------------------- |
| **Token URL**            | CI/CD, automation with session ID | `LOFTY_TOKEN_URL` + `LOFTY_SESSION_ID`                           |
| **Browser OAuth (PKCE)** | Interactive developer use         | `lofty-cli auth login-browser`                                   |
| **Client Credentials**   | Server-to-server                  | `LOFTY_CLIENT_ID` + `LOFTY_CLIENT_SECRET` + `LOFTY_CUSTOMER_KEY` |
| **Direct Token**         | One-off testing, CI injection     | `LOFTY_ACCESS_TOKEN`                                             |

<Note>
  Tokens obtained by Token URL, Browser OAuth, and Client Credentials are cached locally and refreshed automatically when they expire.
</Note>

<AccordionGroup>
  <Accordion title="Token URL (highest priority)" icon="link">
    Use this when an upstream system can mint a session ID and exchange it for tokens through a custom endpoint.

    ```bash theme={null}
    export LOFTY_TOKEN_URL="https://your-auth-server.example.com/token"
    export LOFTY_SESSION_ID="ses_xxx"
    lofty-cli auth login-session
    ```

    You can also pass the values inline:

    ```bash theme={null}
    lofty-cli auth login-session --tokenurl https://... --sessionid ses_xxx
    ```

    Tokens are cached at `~/.config/tokens/lofty-session-cache.json` and refreshed transparently.
  </Accordion>

  <Accordion title="Browser OAuth (recommended for humans)" icon="browser" defaultOpen>
    The simplest method for interactive use. The CLI launches your default browser, you log in once, and the token is stored locally.

    ```bash theme={null}
    lofty-cli auth login-browser
    ```

    The flow uses OAuth 2.0 with [PKCE](https://oauth.net/2/pkce/) — no client secret is needed on your machine.
  </Accordion>

  <Accordion title="OAuth client credentials" icon="server">
    Use this for server-to-server scenarios where there is no human to complete a browser flow. Implements the [OAuth 2.0 Client Credentials Grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4) with an additional resource owner identifier.

    <Steps>
      <Step title="Register an OAuth application">
        Sign in to the [Lofty Developer Portal](https://api.lofty.com/vendor/frontend/static/index.html#/login) to create an application and obtain your `client_id` and `client_secret`.
      </Step>

      <Step title="Configure client credentials">
        ```bash theme={null}
        export LOFTY_CLIENT_ID="your-client-id"
        export LOFTY_CLIENT_SECRET="your-client-secret"
        ```
      </Step>

      <Step title="Specify the target account">
        The customer key identifies which Lofty account's data the CLI will access (the resource owner). Each Lofty user has a unique API key — find it in the Lofty CRM under **Settings > Integrations > API**.

        ```bash theme={null}
        export LOFTY_CUSTOMER_KEY="your-customer-key"
        ```
      </Step>

      <Step title="Exchange for an access token">
        ```bash theme={null}
        lofty-cli auth login
        ```
      </Step>
    </Steps>

    <Note>
      Unlike a standard Client Credentials Grant where the client acts on its own behalf, Lofty requires `LOFTY_CUSTOMER_KEY` to specify which account to access. The token is scoped to that account's data and permissions.
    </Note>
  </Accordion>

  <Accordion title="Direct access token" icon="key">
    When another system has already obtained an access token (e.g. a CI job that ran `auth login` upstream), inject it directly:

    ```bash theme={null}
    export LOFTY_ACCESS_TOKEN="eyJhbGciOi..."
    export LOFTY_REFRESH_TOKEN="dGhpcyBpcyBh..."   # optional
    ```

    No `auth login*` command is required; the token is used as-is.
  </Accordion>
</AccordionGroup>

## Verifying authentication

| Command                 | Purpose                                                               |
| ----------------------- | --------------------------------------------------------------------- |
| `lofty-cli auth status` | Show which method resolved and where credentials came from            |
| `lofty-cli auth test`   | Make a real API call (typically `GET /me`) to confirm the token works |
| `lofty-cli auth show`   | Print the active customer key (masked by default)                     |

## Logging out

```bash theme={null}
lofty-cli auth logout
```

This deletes the Token URL cache, the PKCE token, the OAuth refresh cache, and the saved customer key. It does **not** revoke server-side sessions — for that, rotate the credential at its source.

## Where credentials live on disk

| Path                                        | Created by           | Contents                            |
| ------------------------------------------- | -------------------- | ----------------------------------- |
| `~/.config/lofty-cli/customer-key`          | `auth set`           | Plain-text customer key (chmod 600) |
| `~/.config/tokens/lofty-session-cache.json` | `auth login-session` | Token URL response                  |
| `~/.config/tokens/lofty-pkce-cache.json`    | `auth login-browser` | Browser OAuth tokens                |
| `~/.config/tokens/lofty-oauth-cache.json`   | `auth login`         | Client-credentials tokens           |

<Warning>
  Never commit any of these files to source control. Use `auth logout` or remove the directory to wipe state.
</Warning>

## Troubleshooting

If `auth test` fails, see **[Troubleshooting](/cli/troubleshooting)** for the common causes (clock skew, expired refresh token, wrong customer key, missing scope).
