> For the complete documentation index, see [llms.txt](https://docs.k9.io/key9-identity/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.k9.io/key9-identity/jsonair/3-using-the-api/3.1-authentication.md).

# 3.1 Authentication

JSONAir uses a two-token model. You authenticate with a long-lived **Personal Access Token (PAT)** to obtain a short-lived **JWT** (JSON Web Token). The JWT is then used as a Bearer token on all API requests.

***

## Step 1 — Exchange Your PAT for a JWT

**Endpoint:** `POST /api/v1/jsonair/auth/token`

**Request body:**

```json
{
  "token": "your-plain-text-pat"
}
```

**Successful response (200 OK):**

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600
}
```

`expires_in` is the JWT lifetime in seconds. This is controlled by the server's `JWT_TOKEN_EXPIRE` environment variable (set in minutes, returned here as seconds).

**Failed response (401 Unauthorized):**

```json
{
  "error": "Session expired or invalid"
}
```

***

## Step 2 — Use the JWT on API Requests

Include the JWT in the `Authorization` header of every subsequent request:

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

***

## Token Expiry and Re-Authentication

When a JWT expires, the API returns a `401`. Your client should catch this and repeat Step 1 to obtain a new JWT before retrying the original request.

***

## Rate Limiting

The `/auth/token` endpoint is rate limited to **5 requests per minute per IP address**. Exceeding this returns:

```
HTTP 429 Too Many Requests
```

```json
{
  "error": "Too many requests"
}
```

***

## Security Notes

* Your PAT is never stored in plain text. The server computes an HMAC-SHA256 hash of the PAT using the `TOKEN_HMAC_SECRET` and compares it against the hash stored in the database.
* The JWT is signed with HMAC-SHA256 using the server's `JWT_TOKEN_SECRET`. Tokens with unexpected signing methods or missing claims are rejected.
* Keep your PAT secure — treat it like a password. It should only ever appear in the agent's environment variables, never in source code or logs.
