Initial import of verae-middleware from zapier monorepo
This commit is contained in:
commit
87cd4896c2
67 changed files with 6630 additions and 0 deletions
64
src/lib/receiptPdf.js
Normal file
64
src/lib/receiptPdf.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* Minimal PDF builder for retrieval receipts (no native deps).
|
||||
* @module lib/receiptPdf
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @returns {string}
|
||||
*/
|
||||
function pdfEscape(s) {
|
||||
return String(s).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} receipt
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
export function buildReceiptPdf(receipt) {
|
||||
const lines = [
|
||||
'Verae Time — certified retrieval receipt',
|
||||
`Type: ${receipt.type}`,
|
||||
`Job ID: ${receipt.jobId}`,
|
||||
`SHA256: ${receipt.sha256 ?? ''}`,
|
||||
`Original timestamp: ${receipt.timestamp ?? ''}`,
|
||||
`Retrieved at: ${receipt.extraSeal?.retrievedAt ?? ''}`,
|
||||
`Tenant: ${receipt.extraSeal?.tenantId ?? ''}`,
|
||||
`Seal event: ${receipt.extraSeal?.event ?? ''}`,
|
||||
`Certificate: ${String(receipt.certificate ?? '').slice(0, 80)}`,
|
||||
];
|
||||
|
||||
const commands = lines
|
||||
.map((line, i) => {
|
||||
const y = 720 - i * 18;
|
||||
return `BT /F1 11 Tf 50 ${y} Td (${pdfEscape(line)}) Tj ET`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
const stream = `${commands}\n`;
|
||||
const objects = [
|
||||
'1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj',
|
||||
'2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj',
|
||||
'3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >> endobj',
|
||||
`4 0 obj << /Length ${Buffer.byteLength(stream)} >> stream\n${stream}endstream endobj`,
|
||||
'5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj',
|
||||
];
|
||||
|
||||
let offset = '%PDF-1.4\n'.length;
|
||||
const xref = [0];
|
||||
let body = '%PDF-1.4\n';
|
||||
for (const obj of objects) {
|
||||
xref.push(offset);
|
||||
const chunk = `${obj}\n`;
|
||||
body += chunk;
|
||||
offset += Buffer.byteLength(chunk);
|
||||
}
|
||||
const xrefStart = offset;
|
||||
let xrefTable = `xref\n0 ${objects.length + 1}\n0000000000 65535 f \n`;
|
||||
for (let i = 1; i < xref.length; i += 1) {
|
||||
xrefTable += `${String(xref[i]).padStart(10, '0')} 00000 n \n`;
|
||||
}
|
||||
body += xrefTable;
|
||||
body += `trailer << /Size ${objects.length + 1} /Root 1 0 R >>\nstartxref\n${xrefStart}\n%%EOF\n`;
|
||||
return Buffer.from(body, 'utf8');
|
||||
}
|
||||
97
src/lib/tokens.js
Normal file
97
src/lib/tokens.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* @fileoverview API keys and HMAC session tokens for the middleware auth bridge.
|
||||
* @module lib/tokens
|
||||
*/
|
||||
|
||||
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
||||
import { config } from '../config.js';
|
||||
|
||||
const TOKEN_PREFIX = 'zmt_';
|
||||
const API_KEY_PREFIX = 'zmw_';
|
||||
|
||||
/**
|
||||
* @param {string} payload
|
||||
* @returns {string}
|
||||
*/
|
||||
function sign(payload) {
|
||||
return createHmac('sha256', config.tokenSecret).update(payload).digest('base64url');
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a signed middleware session token embedding tenant + Verae JWT.
|
||||
*
|
||||
* @param {object} params
|
||||
* @param {string} params.tenantId
|
||||
* @param {string} params.veraeToken
|
||||
* @param {string} [params.expiresAt]
|
||||
* @returns {string} Token string starting with `zmt_`
|
||||
*/
|
||||
export function issueSessionToken({ tenantId, veraeToken, expiresAt }) {
|
||||
const payload = Buffer.from(
|
||||
JSON.stringify({
|
||||
tenantId,
|
||||
veraeToken,
|
||||
expiresAt,
|
||||
nonce: randomBytes(8).toString('hex'),
|
||||
}),
|
||||
).toString('base64url');
|
||||
const signature = sign(payload);
|
||||
return `${TOKEN_PREFIX}${payload}.${signature}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and verify a session token.
|
||||
*
|
||||
* @param {string} token
|
||||
* @returns {{ tenantId: string, veraeToken: string, expiresAt?: string, nonce?: string }|null}
|
||||
*/
|
||||
export function parseSessionToken(token) {
|
||||
if (!token?.startsWith(TOKEN_PREFIX)) return null;
|
||||
|
||||
const raw = token.slice(TOKEN_PREFIX.length);
|
||||
const dot = raw.lastIndexOf('.');
|
||||
if (dot === -1) return null;
|
||||
|
||||
const payload = raw.slice(0, dot);
|
||||
const signature = raw.slice(dot + 1);
|
||||
const expected = sign(payload);
|
||||
|
||||
const sigBuf = Buffer.from(signature);
|
||||
const expBuf = Buffer.from(expected);
|
||||
if (sigBuf.length !== expBuf.length || !timingSafeEqual(sigBuf, expBuf)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new public API key (`zmw_…`).
|
||||
* @returns {string}
|
||||
*/
|
||||
export function generateApiKey() {
|
||||
return `${API_KEY_PREFIX}${randomBytes(24).toString('base64url')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} value
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isApiKey(value) {
|
||||
return typeof value === 'string' && value.startsWith(API_KEY_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract bearer credential from an Authorization header.
|
||||
* @param {string|undefined} header
|
||||
* @returns {string|null}
|
||||
*/
|
||||
export function extractBearerToken(header) {
|
||||
if (!header) return null;
|
||||
const match = String(header).match(/^Bearer\s+(.+)$/i);
|
||||
return match?.[1] ?? null;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue