# Authentication quickstart (Auth server + Resource API)

This document describes how to obtain and refresh `Bearer` tokens for Clams APIs using the public
auth server (`clams-authd`).

## Base URLs

- Auth server: `https://auth.clams.tech`
- Resource API: see the REST docs (`../api/`)

## Token overview

- Access tokens: EdDSA-signed JWTs with `aud` in `{svc,rates,feedback}` and a short lifetime:
  `exp = iat + 300` seconds (5 minutes).
- Refresh tokens: opaque base64url strings (43 chars), 90-day TTL, rotated on refresh.
  Reusing an old refresh token revokes the session and returns `401` (`E401-AUTH-FAILED`).

## Human login flow (CLI/MCP)

This is the recommended flow for user-interactive clients (CLI, desktop, MCP):

1. Create a browser login session.
2. Open the returned `browser_url` in a browser and complete login.
3. Poll the login session status until `completed`.
4. Exchange the returned `aud=svc` access token for a refresh token and audience-scoped access
   token.
5. Call resource APIs with `Authorization: Bearer <access_token>`.

### 1) Start a login session

```bash
curl -sS -X POST \
  -H 'content-type: application/json' \
  -d '{}' \
  https://auth.clams.tech/v1/web/login/session/start
```

Response shape:

- `login_session_id`: UUID v7
- `browser_url`: open this in the user's browser
- `expires_in`: seconds until expiry (currently 600)

### 2) Poll the session status

```bash
LOGIN_SESSION_ID='...'

curl -sS \
  "https://auth.clams.tech/v1/web/login/session/${LOGIN_SESSION_ID}"
```

Possible responses (tagged by `status`):

- `pending`
- `completed` (includes `login.access_token`, plus `user_id`, `session_id`, `subject_id`)
- `failed` (includes `reason`)
- `expired`

### 3) Exchange the `svc` access token

When the session status is `completed`, take `login.access_token` and exchange it for an
audience-scoped access token plus a refresh token:

```bash
SVC_ACCESS_TOKEN='...'

curl -sS -X POST \
  -H 'content-type: application/json' \
  -d @- \
  https://auth.clams.tech/v1/token/exchange <<'JSON'
{
  "subject_token": "'"$SVC_ACCESS_TOKEN"'",
  "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "audience": "rates"
}
JSON
```

The response includes:

- `access_token` (JWT; `aud=rates`)
- `refresh_token` (opaque; store it securely)
- `user_id`, `session_id`, `audience`

## Refresh flow (rotating refresh token)

Access tokens expire in 5 minutes. Refresh with the refresh token grant:

```bash
REFRESH_TOKEN='...'

curl -sS -X POST \
  -H 'content-type: application/json' \
  -d @- \
  https://auth.clams.tech/v1/token <<'JSON'
{
  "grant_type": "refresh_token",
  "refresh_token": "'"$REFRESH_TOKEN"'",
  "audience": "rates"
}
JSON
```

Important:

- Refresh is rotating: replace your stored refresh token with the returned `refresh_token`.
- Refresh is rate limited per session; exceeding the budget returns `429 E429-REFRESH-RATE-LIMIT`
  with a `Retry-After` hint.

## Machine-to-machine (client credentials)

### 1) Provision an OAuth client

Create an OAuth client via `POST /v1/oauth-clients` (requires an authenticated bearer token,
typically an `aud=svc` access token).

```bash
ADMIN_ACCESS_TOKEN='...'

curl -sS -X POST \
  -H 'content-type: application/json' \
  -H "authorization: Bearer ${ADMIN_ACCESS_TOKEN}" \
  -d '{"audience":"rates"}' \
  https://auth.clams.tech/v1/oauth-clients
```

This returns `client_id` and `client_secret`. Treat the secret like a password.

### 2) Request an access token

```bash
CLIENT_ID='...'
CLIENT_SECRET='...'

curl -sS -X POST \
  -H 'content-type: application/json' \
  -d @- \
  https://auth.clams.tech/v1/token <<'JSON'
{
  "grant_type": "client_credentials",
  "client_id": "'"$CLIENT_ID"'",
  "client_secret": "'"$CLIENT_SECRET"'",
  "audience": "rates"
}
JSON
```

The response includes `access_token`, `token_type` (always `bearer`), and `expires_in` (seconds).

## Logout and introspection helpers

These endpoints require `Authorization: Bearer <access_token>`:

- `GET /v1/auth/whoami` returns `{ user_id, subject_id }`.
- `POST /v1/auth/logout` revokes the current session.
- `POST /v1/auth/logout/all` revokes all sessions for the subject.

## Offline verification

Services validate access tokens offline using:

- JWKS: `GET /.well-known/jwks.json`
- Discovery: `GET /v1/.well-known/openid-configuration`

Minimum checks:

- `iss` matches the expected issuer (`https://auth.clams.tech`)
- `aud` matches the expected audience (`svc`, `rates`, or `feedback`)
- signature validates against JWKS
- `exp` is not expired (5-minute access token lifetime)

## Troubleshooting

- `401` on resource APIs:
  - token expired (refresh and retry)
  - wrong audience for the endpoint (`aud` mismatch)
  - missing `Authorization: Bearer ...` header
- `401` on refresh:
  - refresh token was rotated and an old token was reused (session is revoked)
- `429` on refresh:
  - wait for the `Retry-After` interval and retry

