# Authentication

> Create workspace-scoped service accounts, exchange client credentials, and operate short-lived bearer tokens with least privilege.

Authenticated Hodoflow APIs use an OAuth2-style service-account
`client_credentials` exchange. There is no user-password API login, no
long-lived bearer API key, and no refresh token.

An owner or admin creates the service account in the intended workspace,
selects an active **run-as member**, and grants the smallest applicable scopes.
The client ID and secret are then exchanged for a short-lived bearer token at
`POST /api/v1/oauth/token`.

For the complete reproducible path, begin with
[Make your first API request](/get-started/first-api-request).

## Lifecycle at a glance

1. Select the target workspace in the Hodoflow application.
2. Create a specifically named service account.
3. Choose an active run-as member whose access should bound the integration.
4. Grant `api_read`, `api_write`, or `webhooks` only as required.
5. Capture the client secret once into your normal secret manager.
6. Exchange credentials and cache the token for slightly less than `expires_in`.
7. Send `Authorization: Bearer $TOKEN` on protected requests.
8. Rotate with an overlap window, then promote the staged pair.
9. Revoke or deactivate immediately when access is no longer needed.

## Token exchange

The canonical endpoint accepts JSON credentials or HTTP Basic authentication
and requires `grant_type: client_credentials`. A successful response contains:

```json
{
  "access_token": "<redacted JWT>",
  "token_type": "bearer",
  "expires_in": 900
}
```

The lifetime is configurable; `900` seconds is the default. Trust the returned
value instead of hard-coding it. Cache one token per service account, refresh
slightly early, and coordinate concurrent refreshes so a fleet does not stampede
the exchange endpoint. See [Exchange credentials](/authentication/token-exchange).

## Runtime authorization

The bearer token identifies a service account, but it does not freeze mutable
authorization until expiration. On every request Hodoflow reloads and validates:

- the active service account and its current scopes;
- the stored organization and workspace;
- the selected run-as membership and its active state; and
- the requested object's own authorization policies.

This makes deactivation, revocation, scope removal, workspace changes, and
membership changes effective for already minted tokens. A `404` can mean an
object is not visible to the run-as member; the API does not reveal filtered
object existence.

## Least-privilege scopes

| Scope | Purpose |
|---|---|
| `api_read` | Safe JSON:API methods and GraphQL `factQuery` |
| `api_write` | Unsafe JSON:API create/update operations |
| `webhooks` | Webhook ingestion for triggers selecting this exact service account |

Scopes do not replace the run-as member's authorization. The service account
must pass both checks. Read [Scopes and run-as permissions](/authentication/scopes-and-permissions)
before granting write or webhook access.

## Secret handling

Never put a client secret or bearer token in a URL, source file, committed
`.env`, shell history, browser storage, analytics event, screenshot, generated
share link, or support message. Use environment variables only as a handoff from
your normal process secret store. Redact credentials and request bodies before
logging; keep the non-secret `x-request-id` for correlation.

For safe rollover and emergency shutdown, continue with
[Rotate and revoke credentials](/authentication/rotation-and-revocation).
