Files
documenso/packages/signing/transports/google-cloud.ts
T
Lucas Smith 9035240b4d refactor: replace pdf-sign with libpdf/core for PDF operations (#2403)
Migrate from @documenso/pdf-sign and @cantoo/pdf-lib to @libpdf/core
for all PDF manipulation and signing operations. This includes:

- New signing transports for Google Cloud KMS and local certificates
- Consolidated PDF operations using libpdf API
- Added TSA (timestamp authority) helper for digital signatures
- Removed deprecated flatten and insert utilities
- Updated tests to use new PDF library
2026-01-21 15:16:23 +11:00

86 lines
2.7 KiB
TypeScript

import { GoogleKmsSigner, parsePem } from '@libpdf/core';
import fs from 'node:fs';
import { env } from '@documenso/lib/utils/env';
const loadCertificates = async (): Promise<Uint8Array[]> => {
// Try chain file first (takes precedence)
const chainContents = env('NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_CONTENTS');
const chainFilePath = env('NEXT_PRIVATE_SIGNING_GCLOUD_HSM_CERT_CHAIN_FILE_PATH');
if (chainContents) {
return parsePem(Buffer.from(chainContents, 'base64').toString('utf-8')).map(
(block) => block.der,
);
}
if (chainFilePath) {
return parsePem(fs.readFileSync(chainFilePath).toString('utf-8')).map((block) => block.der);
}
// Fall back to single certificate (existing behavior)
const certContents = env('NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_CONTENTS');
const certFilePath = env('NEXT_PRIVATE_SIGNING_GCLOUD_HSM_PUBLIC_CRT_FILE_PATH');
if (certContents) {
return parsePem(Buffer.from(certContents, 'base64').toString('utf-8')).map(
(block) => block.der,
);
}
if (certFilePath) {
return parsePem(fs.readFileSync(certFilePath).toString('utf-8')).map((block) => block.der);
}
// Would use: NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH
const certPath = env('NEXT_PRIVATE_SIGNING_GCLOUD_HSM_SECRET_MANAGER_CERT_PATH');
if (certPath) {
const { cert, chain } = await GoogleKmsSigner.getCertificateFromSecretManager(certPath);
if (chain) {
return [cert, ...chain];
}
return [cert];
}
throw new Error('No certificate found for Google Cloud HSM signing');
};
export const createGoogleCloudSigner = async () => {
const keyPath = env('NEXT_PRIVATE_SIGNING_GCLOUD_HSM_KEY_PATH');
if (!keyPath) {
throw new Error('No key path provided for Google Cloud HSM signing');
}
const googleAuthCredentials = env('GOOGLE_APPLICATION_CREDENTIALS');
const googleAuthCredentialContents = env(
'NEXT_PRIVATE_SIGNING_GCLOUD_APPLICATION_CREDENTIALS_CONTENTS',
);
// To handle hosting in serverless environments like Vercel we can supply the base64 encoded
// application credentials as an environment variable and write it to a file if it doesn't exist
if (googleAuthCredentials && googleAuthCredentialContents) {
if (!fs.existsSync(googleAuthCredentials)) {
const contents = new Uint8Array(Buffer.from(googleAuthCredentialContents, 'base64'));
fs.writeFileSync(googleAuthCredentials, contents);
}
}
const certs = await loadCertificates();
if (certs.length === 0) {
throw new Error('No valid certificates found');
}
return GoogleKmsSigner.create({
keyVersionName: keyPath,
certificate: certs[0],
certificateChain: certs.length > 1 ? certs.slice(1) : undefined,
buildChain: true,
});
};