Saltar al contenido principal

API Keys

Mecanismo de autenticación sencillo y muy extendido en APIs B2B (Business-to-Business), webhooks e integraciones servidor a servidor. Una API Key es una cadena aleatoria única que identifica y autentica a un cliente o aplicación (no a un usuario humano).

Características y casos de uso

  • No identifica usuarios, sino aplicaciones o clientes (empresas, integraciones, servicios).
  • Adecuada para acceso programático donde no hay un usuario interactivo.
  • Común en: SDKs de terceros, webhooks, acceso a datos por parte de otras empresas, CLIs, scripts de automatización.
  • Más sencilla que OAuth 2.0 para integraciones M2M (Machine-to-Machine) donde no se necesita delegación de permisos de usuario.

Flujo

  1. El cliente (empresa/desarrollador) solicita o genera una API Key desde tu panel.
  2. La almacena de forma segura (variables de entorno, secrets manager).
  3. La envía en cada petición:
    GET /api/v1/data
    X-API-Key: sk_live_a3f2b1c9d4e5f6...
  4. Tu servidor busca la key en la base de datos, valida permisos y procesa la petición.

Implementación segura

import crypto from 'node:crypto';

// Generar una API Key con prefijo identificativo
export const generateApiKey = (environment = 'live') => {
const prefix = `sk_${environment}_`;
const secret = crypto.randomBytes(32).toString('hex');
return prefix + secret;
// Ejemplo: sk_live_a3f2b1c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0
};

// Crear una API Key para un cliente
export const createApiKey = async (clientId, { name, scopes = [], expiresAt = null } = {}) => {
const rawKey = generateApiKey();

// Guardar SOLO el hash en la BD. El valor original se muestra una sola vez.
const keyHash = crypto.createHash('sha256').update(rawKey).digest('hex');

await db.saveApiKey({
clientId,
keyHash,
name, // Nombre descriptivo: "Producción", "CI/CD", etc.
prefix: rawKey.substring(0, 14), // Para mostrar en el panel: "sk_live_a3f2b1"
scopes, // Permisos: ['read:data', 'write:data']
expiresAt,
createdAt: new Date(),
lastUsedAt: null,
});

return rawKey; // Mostrar al usuario UNA SOLA VEZ. No se puede recuperar después.
};

// Middleware de validación
export const validateApiKey = async (req, res, next) => {
const rawKey = req.headers['x-api-key'];
if (!rawKey) return res.status(401).json({ error: 'API Key requerida' });

const keyHash = crypto.createHash('sha256').update(rawKey).digest('hex');
const storedKey = await db.findApiKeyByHash(keyHash);

if (!storedKey) return res.status(403).json({ error: 'API Key inválida' });
if (storedKey.expiresAt && storedKey.expiresAt < new Date()) {
return res.status(403).json({ error: 'API Key expirada' });
}

// Registrar el uso (útil para auditoría y detección de abusos)
await db.updateApiKeyLastUsed(storedKey.id);

req.apiClient = storedKey;
next();
};

// Autorización por scope (si implementas permisos granulares)
export const requireScope = (scope) => {
return (req, res, next) => {
if (!req.apiClient.scopes.includes(scope)) {
return res.status(403).json({ error: `Permiso requerido: ${scope}` });
}
next();
};
};

// Uso
app.get('/api/data', validateApiKey, requireScope('read:data'), dataHandler);
app.post('/api/data', validateApiKey, requireScope('write:data'), createDataHandler);

Buenas prácticas

  • Prefijos identificativos: Usa prefijos que identifiquen el entorno y el tipo de clave (sk_live_, sk_test_, pk_live_). Esto permite:
    • Al usuario, identificar qué key es cuál en su panel.
    • A los detectores de secrets (GitHub Secret Scanning, etc.), reconocerlas y alertar si se filtran en un repositorio.
  • Guardar solo el hash en base de datos: Si la base de datos se compromete, el atacante obtiene hashes SHA-256 que no puede usar directamente. El valor original solo existe en el momento de la creación.
  • Mostrar el valor una sola vez: Al igual que GitHub o Stripe, muestra la key completa únicamente al crearla. Después, solo muestra el prefijo (sk_live_a3f2b1...).
  • Scopes y permisos granulares: Define qué puede hacer cada key. Una key de solo lectura comprometida no puede escribir datos.
  • Expiración y rotación: Permite a los clientes crear keys con fecha de expiración y revocar las antiguas sin interrumpir el servicio.
  • Rate limiting por key: Controlar el volumen de peticiones individualmente es más justo y seguro que limitar solo por IP.