feature/integracion-cloudrestore-targets (#9)

integraciones para cloud recovery

Reviewed-on: #9
Co-authored-by: hreyes <hreyes@aduanasoft.com.mx>
Co-committed-by: hreyes <hreyes@aduanasoft.com.mx>
This commit is contained in:
2026-06-09 14:58:48 +00:00
committed by acazares
parent 3e5557c034
commit b8be307892
30 changed files with 2426 additions and 67 deletions

View File

@@ -0,0 +1,40 @@
/**
* Autenticación servicio-a-servicio por token Bearer para los endpoints que
* consume CloudRestoreAS. Separado del JWT de usuarios (cookie) porque el cliente
* es una máquina, no un navegador.
*
* NOTA DE SEGURIDAD: estos endpoints entregan credenciales SQL. En producción
* deben servirse solo sobre transporte cifrado (TLS/SSH/VPN) — ver plan, G5.
*/
import { timingSafeEqual } from 'node:crypto';
import { env } from '$env/dynamic/private';
/**
* Valida el header Authorization: Bearer <token> contra CLOUDRESTORE_API_TOKEN.
* Devuelve null si es válido, o el código HTTP a responder (401/500) si no.
* Comparación en tiempo constante para no filtrar el token por timing.
*/
export function checkServiceToken(request: Request): { ok: true } | { ok: false; status: 401 | 500 } {
const expected = env.CLOUDRESTORE_API_TOKEN;
if (!expected || !expected.trim()) {
// Sin token configurado no se puede autenticar de forma segura: se rechaza.
return { ok: false, status: 500 };
}
const header = request.headers.get('authorization') ?? '';
const match = header.match(/^Bearer\s+(.+)$/i);
if (!match) {
return { ok: false, status: 401 };
}
const provided = match[1].trim();
const expectedBuf = Buffer.from(expected.trim(), 'utf8');
const providedBuf = Buffer.from(provided, 'utf8');
if (expectedBuf.length !== providedBuf.length) {
return { ok: false, status: 401 };
}
if (!timingSafeEqual(expectedBuf, providedBuf)) {
return { ok: false, status: 401 };
}
return { ok: true };
}