# Autenticación

> OAuth2 con PKCE para hablar con la API de Ghosty Studio en nombre de una persona — scopes, tokens, errores y cómo obtener un client_id.

URL: https://www.ghosty.studio/docs/api/autenticacion

La API es **por persona**: tu aplicación actúa en nombre de alguien que le dio permiso. El mecanismo es OAuth2 (`authorization_code` con PKCE), el mismo que usa la app móvil de Ghosty.

## Endpoints

| Ruta | Qué hace |
|---|---|
| `GET /oauth2/authorize` | Pantalla de consentimiento. Devuelve un `code` al `redirect_uri`. |
| `POST /oauth2/token` | Canjea `code` o `refresh_token` por tokens. |
| `POST /oauth2/revoke` | Revoca un token (RFC 7009). Siempre `200`. |
| `GET /oauth2/proveedores` | Lista los proveedores de login activos (Google, Apple). Público. |

## Scopes

| Scope | Permite |
|---|---|
| `profile` | Saber quién es (id y correo). |
| `agents:read` | Listar agentes, leer conversaciones, suscribirse a eventos. |
| `agents:write` | Mandar mensajes, cancelar, contestar permisos, programar, compartir, borrar cuenta. |
| `files` | Subir y leer archivos de la cuenta, transcribir audio. |

Pides los que necesitas separados por espacio. El servidor los **acota** al techo de tu cliente sin fallar: si pides `files` y tu cliente no lo tiene, el token sale sin él. Los scopes quedan **congelados** en el token; un permiso nuevo llega hasta el siguiente login.

## Flujo

1. Genera `code_verifier` (43–128 caracteres) y `code_challenge = BASE64URL(SHA256(code_verifier))`. Sólo se acepta `S256`.
2. Abre en el navegador:

```
https://www.ghosty.studio/oauth2/authorize
  ?response_type=code
  &client_id=TU_CLIENT_ID
  &redirect_uri=https://tu-app.com/callback
  &scope=profile%20agents:read%20agents:write%20files
  &state=ALEATORIO
  &code_challenge=…
  &code_challenge_method=S256
```

3. La persona entra (Google, Apple o correo) y acepta. Vuelve a tu `redirect_uri` con `?code=…&state=…`. El código vale **60 segundos**.
4. Canjéalo:

```bash
curl -X POST https://www.ghosty.studio/oauth2/token \
  -d grant_type=authorization_code \
  -d code=EL_CODE \
  -d redirect_uri=https://tu-app.com/callback \
  -d client_id=TU_CLIENT_ID \
  -d code_verifier=EL_VERIFIER
```

```json
{
  "access_token": "…",
  "refresh_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "profile agents:read agents:write files"
}
```

5. Usa el token:

:::tabs
```bash tab=curl
curl https://www.ghosty.studio/api/v2/me/agents \
  -H "Authorization: Bearer ACCESS_TOKEN"
```
```typescript tab=TypeScript
const res = await fetch("https://www.ghosty.studio/api/v2/me/agents", {
  headers: { Authorization: `Bearer ${accessToken}` },
});
const { agentes } = await res.json();
```
```python tab=Python
import requests
r = requests.get("https://www.ghosty.studio/api/v2/me/agents",
                 headers={"Authorization": f"Bearer {access_token}"})
agentes = r.json()["agentes"]
```
:::
## Refrescar

El access token dura **1 hora**; el refresh token, **90 días**, y **rota** en cada uso: guarda el nuevo y descarta el viejo. Reusar un refresh token ya rotado revoca la familia entera (es la señal de un robo).

```bash
curl -X POST https://www.ghosty.studio/oauth2/token \
  -d grant_type=refresh_token \
  -d refresh_token=EL_REFRESH \
  -d client_id=TU_CLIENT_ID
```

Regla práctica: `401` → refresca y reintenta una vez; `403` → falta un scope, no reintentes.

## Clientes confidenciales y públicos

- **Público** (móvil, SPA): sólo `client_id`; PKCE obligatorio.
- **Confidencial** (backend): además `client_secret`, por `client_secret_post` o `Authorization: Basic`.

`redirect_uri` se compara **exacto**: sin comodines ni subrutas.

## Errores

| Código | Cuándo |
|---|---|
| `400 invalid_grant` | Código caducado, verifier incorrecto, refresh rotado, `redirect_uri` distinto. No distingue la causa a propósito. |
| `401 invalid_client` | `client_id` desconocido o secreto incorrecto. |
| `400 unsupported_grant_type` | Sólo `authorization_code` y `refresh_token`. |

## Obtener un `client_id`

El alta de clientes es manual mientras la API está en beta: escribe a [hola@ghosty.studio](mailto:hola@ghosty.studio) con el nombre de tu app, los `redirect_uri` exactos y los scopes que necesitas. Los de la casa (Teams, la app móvil) saltan la pantalla de consentimiento; un tercero siempre la ve.
