Autenticación
La API tiene un sistema de autenticación multi-nivel.
Diagrama de autenticación
flowchart TD
REQ["Request entrante"] --> ROUTE{"¿Qué tipo<br/>de ruta?"}
ROUTE -->|"Admin only<br/>(api-keys, admin/*)"| ADMIN_CHECK
ROUTE -->|"Cliente<br/>(stores, orders, etc)"| CLIENT_CHECK
ROUTE -->|"Webhook"| SIG_CHECK
ROUTE -->|"Pública<br/>(login, callback, validate)"| PUBLIC["✅ Sin auth"]
subgraph ADMIN["adminToken()"]
ADMIN_CHECK{"¿x-api-token<br/>== API_TOKEN?"} -->|No| ADMIN_REJECT["❌ 403"]
ADMIN_CHECK -->|Sí| ADMIN_OK["✅ Acceso admin<br/>Inyecta db"]
end
subgraph CLIENT["clientToken()"]
CLIENT_CHECK{"¿Tiene<br/>x-api-token?"} -->|No| CLIENT_401["❌ 401"]
CLIENT_CHECK -->|Sí| IS_ADMIN{"¿Es el<br/>Admin Token?"}
IS_ADMIN -->|Sí| HAS_CLIENT{"¿Tiene ?client=<br/>en query?"}
HAS_CLIENT -->|No| CLIENT_400["❌ 400 Admin<br/>requires client"]
HAS_CLIENT -->|Sí| CLIENT_ADMIN["✅ clientName = query.client"]
IS_ADMIN -->|No| DB_CHECK["Buscar en api_keys"]
DB_CHECK --> EXISTS{"¿Existe y<br/>está activa?"}
EXISTS -->|No| CLIENT_403["❌ 403"]
EXISTS -->|Sí| CLIENT_OK["✅ clientName = apiKey.clientName"]
end
subgraph WEBHOOK["webhookSignature()"]
SIG_CHECK["Validar HMAC-SHA256<br/>x-signature header"] -->|Inválida| SIG_REJECT["❌ 401"]
SIG_CHECK -->|Válida| SIG_OK["✅ Procesar webhook"]
end
style ADMIN_OK fill:#00d4aa,color:#000
style CLIENT_ADMIN fill:#00d4aa,color:#000
style CLIENT_OK fill:#00d4aa,color:#000
style SIG_OK fill:#00d4aa,color:#000
style PUBLIC fill:#00d4aa,color:#000
style ADMIN_REJECT fill:#ff4d6a,color:#000
style CLIENT_401 fill:#ff4d6a,color:#000
style CLIENT_400 fill:#ff4d6a,color:#000
style CLIENT_403 fill:#ff4d6a,color:#000
style SIG_REJECT fill:#ff4d6a,color:#000
Niveles de acceso
Admin Token
Token global definido en la variable de entorno API_TOKEN. Se usa en dos contextos:
En rutas admin-only (/api-keys, /admin/*): acceso directo, no requiere parámetros extra.
# Listar API keys — solo necesita el admin token
curl https://api.example.com/api-keys \
-H "x-api-token: MI_ADMIN_TOKEN"
En rutas de cliente (/stores, /orders, etc.): requiere ?client= para indicar en nombre de qué cliente opera.
# Operar como un cliente desde el admin token
curl https://api.example.com/stores?client=mi-cliente \
-H "x-api-token: MI_ADMIN_TOKEN"
| Permiso | |
|---|---|
| Crear/revocar API keys | ✅ |
| Ver info de cualquier cliente | ✅ |
| Simular eventos de órdenes | ✅ |
| Ver activity logs y webhook logs | ✅ |
Operar como cualquier cliente (con ?client=) |
✅ |
Client API Key
Key generada por el admin para cada cliente. Prefijo mk_, 64 caracteres hexadecimales.
El middleware identifica al cliente automáticamente por su key, no necesita enviar ?client= en ninguna request.
| Permiso | |
|---|---|
| Gestionar sus stores/POS | ✅ |
| Crear/consultar órdenes | ✅ |
| Ver estado OAuth propio | ✅ |
| Ver datos de otros clientes | ❌ |
| Gestionar API keys | ❌ |
| Endpoints de admin | ❌ |
Webhook Signature
Los webhooks de MercadoPago se validan con HMAC-SHA256. No usan API key ni admin token.
La validación construye un manifest con el formato:
Y verifica:
- Hash HMAC-SHA256 del manifest contra el
WEBHOOK_SECRET - Tolerancia de timestamp (5 minutos máximo)
- Comparación timing-safe para evitar timing attacks
Middlewares
adminToken()
Usado en rutas admin-only: /api-keys, /admin/*.
Solo acepta el admin token. Rechaza client keys.
clientToken()
Usado en rutas de cliente: /stores, /orders, /terminals, /oauth/status, etc.
Acepta tanto admin token (con ?client=) como client API key.
// Inyecta en el context:
c.get('clientName') // string — nombre del cliente
c.get('db') // Database — instancia de Drizzle
webhookSignature()
Usado solo en POST /webhooks. Valida la firma HMAC-SHA256 de MercadoPago.
Validación de API Key
Endpoint público GET /oauth/validate para verificar si una key es válida sin pasar por ningún middleware de auth: