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
- El cliente (empresa/desarrollador) solicita o genera una API Key desde tu panel.
- La almacena de forma segura (variables de entorno, secrets manager).
- La envía en cada petición:
GET /api/v1/data
X-API-Key: sk_live_a3f2b1c9d4e5f6... - 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.