# Authentication

> The Haszno API uses different authentication mechanisms depending on the endpoint and deployment type. This page explains each one and what it allows today.

## Mechanisms at a glance

| Mechanism | Header / source | Usable on haszno.com? | Use for |
|---|---|---|---|
| None | — | Yes | Public demo GETs (synthetic data) |
| HTTP Basic | `Authorization: Basic <base64(demo:password)>` | Yes, with team-issued password | Full access to the shared demo deployment |
| API Bearer token | `Authorization: Bearer haszno_<token>` | Only on `GET /api/auth/me` (see below) | Checking a token's user, shop and scope |
| `x-api-key` | `x-api-key: <key>` or `?api_key=<key>` | No | Self-hosted deployments only |
| Session cookie | Cookie `haszno_session` | Yes | The [account and session endpoints](protected-endpoints.md#auth-account); created by `/api/auth/register` or `/api/auth/login` |

> [!WARNING]
> A Bearer token is not a general API credential yet. Today the only endpoint that checks it is `GET /api/auth/me`, on `haszno.com` and on self-hosted instances alike. See [API Bearer tokens](#api-bearer-tokens) for the full picture.

## Public demo — no credentials needed

A set of GET endpoints return synthetic demo data without any credentials. If you omit the `Authorization` header entirely, the response is served unauthenticated. If you send an `Authorization` header, the request goes through the HTTP Basic wall instead: invalid credentials get `401`, and a valid Basic header returns the deployment's shared dataset rather than the synthetic data.

Endpoints available without credentials on `https://haszno.com` **Public** **Read-only**:

| Path | Returns |
|---|---|
| `GET /api/categories` | Category taxonomy (14 categories) |
| `GET /api/products` | Product list with pricing and market data |
| `GET /api/products/supplier-counts` | Product counts per supplier |
| `GET /api/suppliers` | Supplier list |
| `GET /api/stats` | Dashboard summary numbers |
| `GET /api/settings` | Pricing rules (demo defaults) |
| `GET /api/impact` | Profit impact attribution summary |
| `GET /api/orders` | Order list (always `[]` on the demo) |
| `GET /api/proposals` | Price proposals (always `[]` on the demo) |
| `GET /api/import-list` | Import list staging items |
| `GET /api/peers/invites` | Peer invite codes (always `[]` on the demo) |
| `GET /api/peers/links` | Peer links (always `[]` on the demo) |
| `GET /api/health` | Liveness check |
| `GET /api/geo` | Visitor country code |
| `GET /api/blog` and `GET /api/blog/:slug` | Published market insight posts (real data) |
| `GET /llms.txt` | LLM-readable site summary |

The `/api/settings` and `/api/impact` responses include a `"demo": true` field. Other demo endpoints (categories, products, suppliers, stats, orders, proposals, peers, import-list) do not include this flag.

## HTTP Basic auth — protected demo access

The hosted demo at `https://haszno.com` has an outer authentication wall that protects endpoints not on the public list above, except a few that carry their own protection, such as the [account and session endpoints](protected-endpoints.md#auth-account). This wall uses HTTP Basic auth.

Credentials **Protected**:

```http
username: demo
password: <contact the Haszno team>
```

This is a **shared credential** for the demo deployment. It is not per-shop and not a production API. Contact the Haszno team to receive the password.

Send the credential as a standard `Authorization: Basic` header:

```bash
curl --fail-with-body \
  --user 'demo:<password>' \
  'https://haszno.com/api/rules'
```

Or with the `HASZNO_BASIC` environment variable used by the CLI and MCP server:

```bash
export HASZNO_BASIC='demo:<password>'
haszno rules list --json
```

With HTTP Basic access you can call all endpoints that require authentication, including write operations (order ingestion, rule management, feed imports, settings updates).

## API Bearer tokens

API Bearer tokens are designed for agents, CLI, and MCP server access. They are the intended long-term mechanism for per-shop programmatic access.

### Token format

Tokens are issued as `haszno_<43 characters>`, where the 43 characters are base64url (`A–Z`, `a–z`, `0–9`, `-`, `_`). The full string starting with `haszno_` is the token value you send.

```http
Authorization: Bearer haszno_<your-token>
```

### Creating a token

1. Register an account at `https://haszno.com` (via `POST /api/auth/register`) or log in (`POST /api/auth/login`). Both return a session cookie.
2. With the session cookie, call `POST /api/auth/tokens` with a JSON body `{"name": "…", "scope": "read"}` (`scope` is `read` or `write`, default `read`) to issue a token.
3. Copy the `token` field from the response — it is shown exactly once and stored only as a hash.

You can also list active tokens (`GET /api/auth/tokens`) and revoke them (`DELETE /api/auth/tokens/:id`) with the same session cookie. A Bearer token cannot be used to manage tokens; these three endpoints accept only the session cookie.

### What Bearer tokens work for today

> [!WARNING]
> Today `GET /api/auth/me` is the only endpoint that validates a Bearer token. It returns the token's user, shop and `tokenScope`. No other route accepts a Bearer token as its credential yet.

| Deployment | Bearer token result |
|---|---|
| `haszno.com` (hosted demo) | `GET /api/auth/me` returns the token's user and shop. Every other protected path returns `401` from the HTTP Basic wall. |
| Self-hosted with `HASZNO_API_KEY` set | `GET /api/auth/me` works. Every other path still needs `x-api-key`; the token alone gets `401`. |
| Self-hosted without any key guard | All routes are open; the token is not needed. |

Planned: per-shop Bearer tokens on `haszno.com` once multi-tenancy is out of developer preview.

## x-api-key — self-hosted only

On a self-hosted Haszno instance, you can set `HASZNO_API_KEY` as an environment variable. All requests that include this value in the `x-api-key` header or as a `?api_key=` query parameter are accepted without a session.

This mechanism is **not active on `haszno.com`**. The environment variable is not set on the hosted deployment.

## Keeping credentials safe

- Store credentials in environment variables, not in source code or committed config files.
- Use `HASZNO_BASIC` for the demo password and `HASZNO_API_KEY` for the API key. The CLI and MCP server read these automatically.
- Run `haszno login` to save credentials to `~/.config/haszno/config.json` (created with `chmod 600`). Both the CLI and MCP server read from this file.
- Never pass a credential as a command-line argument in a shared shell environment — it appears in `ps` output and shell history.
- List active tokens with `GET /api/auth/tokens`; revoke unused ones with `DELETE /api/auth/tokens/:id`.

## Error responses

| Status | Meaning |
|---|---|
| `401` | The `Authorization` header is present but the credentials are missing, malformed, or invalid. The account endpoints also return `401` when they need a session and none is valid. Fix the credential and retry. |
| `403` | The request was rejected by the CSRF middleware. This occurs on state-changing requests that originate from an unexpected `Origin`. Not relevant for API or CLI clients that do not use cookie sessions. |

Error bodies always follow the shape `{ "error": "Hungarian error message" }`. See [Errors and rate limits](errors.md) for the full error reference.

> [!NOTE]
> API error messages are currently returned in Hungarian regardless of the `Accept-Language` header or the documentation language setting. This will change before general availability.
