---
title: "auth.md — autenticación de agentes en Polarweb"
description: "Manual paso a paso para que un agente de IA registre una credencial OAuth y actúe en nombre de un usuario en Polarweb: registro dinámico público, PKCE y canje de token, con endpoints y JSON reales."
---

# auth.md

Eres un agente. Este documento te dice cómo registrar una credencial para actuar **en nombre de un usuario** en Polarweb. Sigue los pasos en orden; no te los saltes.

Este es el flujo de **registro dinámico anónimo**: te das de alta como cliente OAuth público sin cuenta previa del agente ni dato personal — cualquier agente puede hacerlo. No hay hoy un flujo verificado por email ni por un proveedor de identidad de confianza (ID-JAG).

La mayor parte de Polarweb es de lectura pública y NO necesita nada de esto — ver [Cuándo no hace falta nada de esto](#cuándo-no-hace-falta-nada-de-esto) más abajo. Este flujo solo hace falta para actuar en nombre de una cuenta real del panel.

Los endpoints viven en https://app.polarweb.io.

## Paso 1 — Regístrate como cliente OAuth

Registro dinámico (RFC 7591 vía el plugin `mcp` de Better Auth). Sin autenticación previa.

```http
POST /api/auth/mcp/register HTTP/1.1
Host: app.polarweb.io
Content-Type: application/json

{
  "redirect_uris": ["https://tu-agente.example/callback"],
  "token_endpoint_auth_method": "none",
  "client_name": "Mi agente"
}
```

Respuesta (`201 Created`):

```json
{
  "client_id": "aB3dEfGh...",
  "client_id_issued_at": 1798675200,
  "redirect_uris": ["https://tu-agente.example/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "client_name": "Mi agente"
}
```

Qué es cada campo:

- `client_id` — identifica a tu agente ante Polarweb. Guárdalo: lo necesitas en el Paso 2.
- `token_endpoint_auth_method: "none"` — te registras como cliente **público**: no recibes `client_secret` (no hay nada que proteger ni rotar). A cambio, el Paso 2 exige PKCE.
- `redirect_uris` — a dónde te devuelve el navegador tras el login. Debe coincidir EXACTO con el que uses en el Paso 2, carácter a carácter.
- `client_id_issued_at` — segundos Unix de cuándo se creó el registro.

## Paso 2 — Autoriza con PKCE y canjea el código

Genera un par PKCE (S256, RFC 7636) tú mismo, antes de nada: un `code_verifier` aleatorio (43-128 caracteres, base64url) y su `code_challenge = BASE64URL(SHA256(code_verifier))`.

Lleva al usuario a esta URL (en su navegador, no por HTTP directo desde tu proceso):

```http
GET /api/auth/mcp/authorize?client_id=<tu_client_id>&response_type=code&redirect_uri=<tu_redirect_uri_url-encoded>&scope=openid+profile+email+offline_access&code_challenge=<tu_code_challenge>&code_challenge_method=S256&state=<un_valor_aleatorio_tuyo> HTTP/1.1
Host: app.polarweb.io
```

- Si el usuario no tiene sesión iniciada, Polarweb le redirige primero a https://app.polarweb.io/acceso (el login real del panel) y, tras iniciar sesión, vuelve aquí solo.
- Con sesión, responde `302` a tu `redirect_uri` con `?code=<código>&state=<el mismo state que enviaste>`. Verifica que el `state` coincide antes de seguir — si no coincide, para y no canjees el código.

Canjea el código por un token:

```http
POST /api/auth/mcp/token HTTP/1.1
Host: app.polarweb.io
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "<código del paso anterior>",
  "redirect_uri": "https://tu-agente.example/callback",
  "client_id": "<tu_client_id>",
  "code_verifier": "<el code_verifier SIN hashear que generaste al principio>"
}
```

Respuesta (`200 OK`):

```json
{
  "access_token": "...",
  "token_type": "Bearer",
  "refresh_token": "...",
  "id_token": "...",
  "scope": "openid profile email offline_access"
}
```

`refresh_token` solo aparece si pediste el scope `offline_access`; `id_token` solo si pediste `openid`. Si el `code_verifier` no coincide con el `code_challenge` original, el canje falla (4xx) — no se regala un token.

## Paso 3 — Usa el token

```http
GET /api/auth/mcp/get-session HTTP/1.1
Host: app.polarweb.io
Authorization: Bearer <access_token>
```

Esto es, hoy, el alcance real de un agente autenticado por este camino: resolver la sesión (identidad del usuario) contra `/api/auth/mcp/get-session`. Polarweb todavía no expone, por esta vía, una API de acciones (crear o editar una web, cambiar precios) para agentes autenticados — cuando exista, este documento se actualiza con las rutas reales. Sin `Authorization`, el endpoint responde `null`: ninguna sesión se cuela gratis.

Si el token caducó y pediste `offline_access`, pide uno nuevo:

```http
POST /api/auth/mcp/token HTTP/1.1
Host: app.polarweb.io
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "<tu refresh_token>",
  "client_id": "<tu_client_id>"
}
```

Sin `refresh_token` válido, repite desde el Paso 2.

## Cuándo no hace falta nada de esto

- Leer cualquier página pública (HTML o su gemelo Markdown vía `Accept: text/markdown`).
- Consultar el servidor MCP de solo lectura
  ([https://polarweb.io/.well-known/mcp/server-card.json](https://polarweb.io/.well-known/mcp/server-card.json)):
  precios, qué es Polarweb, galería de ejemplos.
- Navegar con las tools WebMCP de la landing (`ir_a_seccion`, `ir_a_registro`, `ver_precios`): son de navegación, no de escritura.

## Honestidad de este documento

Cada endpoint, cada campo y cada respuesta de arriba son reales: el flujo completo (registro → authorize con PKCE → canje de token → `/mcp/get-session` resolviendo la sesión de verdad, sin mocks) está probado de punta a punta en `apps/api/src/__tests__/auth-mcp-oauth.test.ts`. Si algo de esto responde 404 al leerlo hoy, es que ese despliegue concreto aún no ha salido a producción — no que el flujo sea falso.

## Preguntas o alta manual

Si este flujo no encaja con tu caso, escribe a [hola@polarweb.io](mailto:hola@polarweb.io); lo lee un humano.
