UNPKG

@mtvproject/platform-crypto-middleware

Version:
131 lines 5.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createPayload = exports.verifyExpiration = exports.verifyMetadata = exports.verifyTimestamp = exports.verifySign = exports.verifyEIP1654Sign = exports.verifyPersonalSign = exports.extractAuthChain = exports.isEIP1664AuthChain = void 0; const crypto_1 = require("@mtvproject/crypto"); const types_1 = require("./types"); const errors_1 = __importDefault(require("./errors")); function isEIP1664AuthChain(authChain) { switch (authChain.length) { case 2: case 3: return authChain[0].type === crypto_1.AuthLinkType.SIGNER && authChain[1].type === crypto_1.AuthLinkType.ECDSA_EIP_1654_EPHEMERAL; default: return false; } } exports.isEIP1664AuthChain = isEIP1664AuthChain; function extractAuthChain(headers) { let index = 0; const chain = []; while (headers[types_1.AUTH_CHAIN_HEADER_PREFIX + index]) { try { const item = Array.isArray(headers[types_1.AUTH_CHAIN_HEADER_PREFIX + index]) ? headers[types_1.AUTH_CHAIN_HEADER_PREFIX + index][0] : headers[types_1.AUTH_CHAIN_HEADER_PREFIX + index]; chain.push(JSON.parse(item)); } catch (err) { throw new errors_1.default(`Invalid chain format: ${err.message}`, 400); } index++; } if (chain.length <= 1) { throw new errors_1.default(`Invalid Auth Chain`, 400); } return chain; } exports.extractAuthChain = extractAuthChain; async function verifyPersonalSign(authChain, payload) { const verification = await crypto_1.Authenticator.validateSignature(payload, authChain, null); if (!verification.ok) { throw new errors_1.default(`Invalid signature: ${verification.message}`, 401); } return crypto_1.Authenticator.ownerAddress(authChain).toLowerCase(); } exports.verifyPersonalSign = verifyPersonalSign; async function verifyEIP1654Sign(authChain, payload, options) { const catalyst = new URL(options.catalyst ?? types_1.DEFAULT_CATALYST); const ownerAddress = crypto_1.Authenticator.ownerAddress(authChain).toLowerCase(); let verification; let response; try { response = await options.fetcher.fetch(`https://${catalyst.host}/lambdas/crypto/validate-signature`, { method: 'POST', headers: { 'content-type': 'application/json', 'accept-type': 'application/json' }, body: JSON.stringify({ authChain, timestamp: payload }) }); } catch (err) { throw new errors_1.default(`Error connecting to catalyst "https://${catalyst.host}"`, 503); } let body = ''; try { body = await response.text(); verification = JSON.parse(body); } catch (err) { throw new errors_1.default(`Invalid response from catalyst "https://${catalyst.host}": ${body}`, 503); } if (!verification.valid || verification.ownerAddress.toLowerCase() !== ownerAddress) { throw new errors_1.default(`Invalid signature`, 401); } return ownerAddress; } exports.verifyEIP1654Sign = verifyEIP1654Sign; function verifySign(authChain, payload, options) { if (isEIP1664AuthChain(authChain)) { return verifyEIP1654Sign(authChain, payload, options); } return verifyPersonalSign(authChain, payload); } exports.verifySign = verifySign; function verifyTimestamp(value) { const timestamp = Number(value || '0'); if (value && !Number.isFinite(timestamp)) { throw new errors_1.default(`Invalid chain timestamp: ${value}`, 400); } return timestamp; } exports.verifyTimestamp = verifyTimestamp; function verifyMetadata(value) { try { return JSON.parse(value ? String(value) : '{}'); } catch (err) { throw new errors_1.default(`Invalid chain metadata: "${value}"`, 400); } } exports.verifyMetadata = verifyMetadata; function verifyExpiration(timestamp, options) { const expiration = options.expiration ?? types_1.DEFAULT_EXPIRATION; const now = Date.now(); if (timestamp + expiration < now) { throw new errors_1.default(`Expired signature: signature timestamp: ${timestamp}, timestamp expiration: ${timestamp + expiration}, local timestamp: ${now}`, 401); } return true; } exports.verifyExpiration = verifyExpiration; function createPayload(method, path, rawTimestamp, rawMetadata) { return [method, path, rawTimestamp, rawMetadata].join(':').toLowerCase(); } exports.createPayload = createPayload; async function verify(method, path, headers, options) { const authChain = extractAuthChain(headers); const timestamp = verifyTimestamp(headers[types_1.AUTH_TIMESTAMP_HEADER]); const metadata = verifyMetadata(headers[types_1.AUTH_METADATA_HEADER]); const payload = createPayload(method, path, headers[types_1.AUTH_TIMESTAMP_HEADER], headers[types_1.AUTH_METADATA_HEADER]); const ownerAddress = await verifySign(authChain, payload, options); verifyExpiration(timestamp, options); return { auth: ownerAddress, authMetadata: metadata }; } exports.default = verify; //# sourceMappingURL=verify.js.map