# API Contract: Desk Auth & RBAC

**Service**: Ligbox Ops Platform API (VM122)  
**Base URL (LAN)**: `http://10.10.10.122:8080`  
**Base URL (público)**: `https://api.ops.ligbox.com.br`  
**Human Auth**: `Authorization: Bearer <JWT>`  
**Machine Auth**: `X-Webhook-Secret` (webhooks only)  
**Internal Auth**: `X-Ops-Internal-Token` (worker audit cycle)

---

## POST /api/v1/auth/login

Público. Não requer JWT.

```http
POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json

{
  "username": "root",
  "password": "805353"
}
```

### Response 200

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 28800,
  "username": "root",
  "role": "super_admin",
  "display_name": "Roger"
}
```

### Response 401

```json
{ "detail": "invalid credentials" }
```

### Response 429

```json
{ "detail": "too many login attempts" }
```

---

## GET /api/v1/auth/me

```http
GET /api/v1/auth/me HTTP/1.1
Authorization: Bearer <token>
```

### Response 200

```json
{
  "username": "mini",
  "role": "technician",
  "display_name": "Suporte",
  "active": true,
  "last_login_at": "2026-06-10T12:00:00+00:00"
}
```

---

## GET /api/v1/desk/tickets (protegido)

```http
GET /api/v1/desk/tickets HTTP/1.1
Authorization: Bearer <token>
```

### Sem token → 401

```json
{ "detail": "not authenticated" }
```

### noc → 200 com dados mascarados

`company_profile.tax_id` e morada omitidos/mascarados.

---

## PATCH /api/v1/desk/tickets/{id}

```http
PATCH /api/v1/desk/tickets/11 HTTP/1.1
Authorization: Bearer <token>
Content-Type: application/json

{
  "status": "closed",
  "assigned_to": "mini"
}
```

| Role | Resultado |
|------|-----------|
| super_admin, ops_lead | 200 |
| technician (assigned ou unassigned) | 200 |
| technician (assigned to other) | 403 |
| noc | 403 |

---

## Webhook (inalterado — sem JWT)

```http
POST /api/v1/webhooks/onboard HTTP/1.1
Content-Type: application/json
X-Webhook-Secret: <WEBHOOK_SECRET>

{"event":"account.created","domain":"test.ligbox","session_id":"x"}
```

JWT no lugar do secret → **401** (webhooks não aceitam Bearer).

---

## Health (público)

```http
GET /health HTTP/1.1
```

Sempre 200 sem auth (Traefik healthcheck).

---

## Role test matrix (curl)

```bash
API="http://10.10.10.122:8080"
TOKEN_ROOT=$(curl -sf -X POST "$API/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"root","password":"805353"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# Deve falhar sem token
curl -sf "$API/api/v1/desk/tickets" && echo UNEXPECTED || echo "401 OK"

# Deve funcionar com token
curl -sf -H "Authorization: Bearer $TOKEN_ROOT" "$API/api/v1/desk/tickets" | head -c 80

TOKEN_NOC=$(curl -sf -X POST "$API/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"noc","password":"805353"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# noc não pode fechar ticket
curl -sf -X PATCH -H "Authorization: Bearer $TOKEN_NOC" \
  -H "Content-Type: application/json" \
  -d '{"status":"closed"}' \
  "$API/api/v1/desk/tickets/1" && echo UNEXPECTED || echo "403 OK"
```

---

## Error codes

| HTTP | Significado |
|------|-------------|
| 401 | Sem token, token inválido/expirado, credenciais login erradas |
| 403 | Token válido mas role insuficiente |
| 429 | Rate limit login |
