siwe
Version:
Sign-In with Ethereum
80 lines (79 loc) • 3.59 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkInvalidKeys = exports.isValidISO8601Date = exports.generateNonce = exports.checkContractWalletSignature = void 0;
const random_1 = require("@stablelib/random");
// @ts-expect-error -- ethers v6 compatibility hack
const ethers_1 = require("ethers");
const ethersCompat_1 = require("./ethersCompat");
const EIP1271_ABI = [
'function isValidSignature(bytes32 _message, bytes _signature) public view returns (bytes4)',
];
const EIP1271_MAGICVALUE = '0x1626ba7e';
const ISO8601 = /^(?<date>[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(.[0-9]+)?(([Zz])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))$/;
/**
* This method calls the EIP-1271 method for Smart Contract wallets
* @param message The EIP-4361 parsed message
* @param provider Web3 provider able to perform a contract check (Web3/EthersJS).
* @returns {Promise<boolean>} Checks for the smart contract (if it exists) if
* the signature is valid for given address.
*/
const checkContractWalletSignature = async (message, signature, provider) => {
if (!provider) {
return false;
}
const walletContract = new ethers_1.Contract(message.address, EIP1271_ABI, provider);
const hashedMessage = (0, ethersCompat_1.hashMessage)(message.prepareMessage());
const res = await walletContract.isValidSignature(hashedMessage, signature);
return res === EIP1271_MAGICVALUE;
};
exports.checkContractWalletSignature = checkContractWalletSignature;
/**
* This method leverages a native CSPRNG with support for both browser and Node.js
* environments in order generate a cryptographically secure nonce for use in the
* SiweMessage in order to prevent replay attacks.
*
* 96 bits has been chosen as a number to sufficiently balance size and security considerations
* relative to the lifespan of it's usage.
*
* @returns cryptographically generated random nonce with 96 bits of entropy encoded with
* an alphanumeric character set.
*/
const generateNonce = () => {
const nonce = (0, random_1.randomStringForEntropy)(96);
if (!nonce || nonce.length < 8) {
throw new Error('Error during nonce creation.');
}
return nonce;
};
exports.generateNonce = generateNonce;
/**
* This method matches the given date string against the ISO-8601 regex and also
* performs checks if it's a valid date.
* @param inputDate any string to be validated against ISO-8601
* @returns boolean indicating if the provided date is valid and conformant to ISO-8601
*/
const isValidISO8601Date = (inputDate) => {
/* Split groups and make sure inputDate is in ISO8601 format */
const inputMatch = ISO8601.exec(inputDate);
/* if inputMatch is null the date is not ISO-8601 */
if (!inputMatch) {
return false;
}
/* Creates a date object with input date to parse for invalid days e.g. Feb, 30 -> Mar, 01 */
const inputDateParsed = new Date(inputMatch.groups.date).toISOString();
/* Get groups from new parsed date to compare with the original input */
const parsedInputMatch = ISO8601.exec(inputDateParsed);
/* Compare remaining fields */
return inputMatch.groups.date === parsedInputMatch.groups.date;
};
exports.isValidISO8601Date = isValidISO8601Date;
const checkInvalidKeys = (obj, keys) => {
const invalidKeys = [];
Object.keys(obj).forEach(key => {
if (!keys.includes(key)) {
invalidKeys.push(key);
}
});
return invalidKeys;
};
exports.checkInvalidKeys = checkInvalidKeys;