@ltonetwork/http-message-signatures
Version:
Implementation of the IETF HTTP Message Signatures draft standard
89 lines • 3.8 kB
JavaScript
;
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