UNPKG

@ltonetwork/http-message-signatures

Version:

Implementation of the IETF HTTP Message Signatures draft standard

89 lines 3.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractHeader = extractHeader; exports.getUrl = getUrl; exports.extractComponent = extractComponent; exports.buildSignatureInputString = buildSignatureInputString; exports.buildSignedData = buildSignedData; function extractHeader({ headers }, header) { var _a, _b; if (typeof headers.get === 'function') return (_a = headers.get(header)) !== null && _a !== void 0 ? _a : ''; const lcHeader = header.toLowerCase(); const key = Object.keys(headers).find((name) => name.toLowerCase() === lcHeader); let val = key ? (_b = headers[key]) !== null && _b !== void 0 ? _b : '' : ''; if (Array.isArray(val)) { val = val.join(', '); } return val.toString().replace(/\s+/g, ' '); } function getUrl(message, component) { if ('url' in message && 'protocol' in message) { const host = extractHeader(message, 'host'); const protocol = message.protocol || 'http'; const baseUrl = `${protocol}://${host}`; return new URL(message.url, baseUrl); } if (!message.url) throw new Error(`${component} is only valid for requests`); return new URL(message.url); } // see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures-06#section-2.3 function extractComponent(message, component) { switch (component) { case '@method': if (!message.method) throw new Error(`${component} is only valid for requests`); return message.method.toUpperCase(); case '@target-uri': if (!message.url) throw new Error(`${component} is only valid for requests`); return message.url; case '@authority': { const url = getUrl(message, component); const port = url.port ? parseInt(url.port, 10) : null; return `${url.host}${port && ![80, 443].includes(port) ? `:${port}` : ''}`; } case '@scheme': return getUrl(message, component).protocol.slice(0, -1); case '@request-target': { const { pathname, search } = getUrl(message, component); return `${pathname}${search}`; } case '@path': return getUrl(message, component).pathname; case '@query': return getUrl(message, component).search; case '@status': if (!message.status) throw new Error(`${component} is only valid for responses`); return message.status.toString(); case '@query-params': case '@request-response': throw new Error(`${component} is not implemented yet`); default: throw new Error(`Unknown specialty component ${component}`); } } function buildSignatureInputString(componentNames, parameters) { const components = componentNames.map((name) => `"${name.toLowerCase()}"`).join(' '); const values = Object.entries(parameters) .map(([parameter, value]) => { if (typeof value === 'number') return `;${parameter}=${value}`; if (value instanceof Date) return `;${parameter}=${Math.floor(value.getTime() / 1000)}`; return `;${parameter}="${value.toString()}"`; }) .join(''); return `(${components})${values}`; } function buildSignedData(request, components, signatureInputString) { const parts = components.map((component) => { const value = component.startsWith('@') ? extractComponent(request, component) : extractHeader(request, component); return `"${component.toLowerCase()}": ${value}`; }); parts.push(`"@signature-params": ${signatureInputString}`); return parts.join('\n'); } //# sourceMappingURL=build.js.map