@mtvproject/platform-crypto-middleware
Version:
Platform's Authentication Middleware
131 lines • 5.37 kB
JavaScript
;
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