sign-in-with-ethereum-parser
Version:
Parse Messages that conform to EIP-4361: Sign In with Ethereum (SIWE)
438 lines (437 loc) • 16.8 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.cb = void 0;
const node_exports_1 = __importDefault(require("apg-js/src/apg-lib/node-exports"));
const utils = node_exports_1.default.utils;
const id = node_exports_1.default.ids;
const utils_1 = require("./utils");
/* copied from siwe/lib/utils.ts */
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]))$/;
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.cb = {
signInWithEtherium: function (result, chars, phraseIndex, data) {
switch (result.state) {
case id.ACTIVE:
if (typeof data !== "object" || data === null) {
throw new Error("data must be an object");
}
break;
case id.NOMATCH:
data.errors.push(`invalid message: max line number was ${data.lineno}`);
}
},
lineno: function lineno(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
data.lineno += 1;
}
},
exTitle: function exTitle(result, chars, phraseIndex, data) {
if (result.state === id.NOMATCH) {
data.lineno -= 1;
}
},
nbTitle: function nbTitle(result, chars, phraseIndex, data) {
if (result.state === id.NOMATCH) {
data.lineno -= 1;
}
},
riTitle: function riTitle(result, chars, phraseIndex, data) {
if (result.state === id.NOMATCH) {
data.lineno -= 1;
}
},
reTitle: function reTitle(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
data.resources = [];
}
else if (result.state === id.NOMATCH) {
data.lineno -= 1;
}
},
oscheme: function oscheme(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
// if matched, remove :// from oscheme
data.scheme = utils.charsToString(chars, phraseIndex, result.phraseLength - 3);
}
},
domain: function domain(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.domain = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.EMPTY:
data.errors.push(`line ${data.lineno}: domain cannot be empty`);
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid domain`);
}
},
address: function address(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.address = utils.charsToString(chars, phraseIndex, result.phraseLength);
if (!(0, utils_1.isEIP55Address)(data.address)) {
data.errors.push(`line ${data.lineno}: invalid EIP-55 address - ${data.address}`);
}
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid address`);
break;
}
},
statement: function statement(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
data.statement = utils.charsToString(chars, phraseIndex, result.phraseLength);
}
},
emptyStatement: function emptyStatement(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
data.statement = "";
}
},
version: function version(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.version = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid version`);
break;
}
},
nonce: function nonce(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.nonce = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid nonce`);
break;
}
},
issuedAt: function issuedAt(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.issuedAt = utils.charsToString(chars, phraseIndex, result.phraseLength);
if (!isValidISO8601Date(data.issuedAt)) {
data.errors.push(`line ${data.lineno}: invalid issued-at date time semantics`);
}
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid issued-at date time syntax`);
break;
}
},
expirationTime: function expirationTime(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.expirationTime = utils.charsToString(chars, phraseIndex, result.phraseLength);
if (!isValidISO8601Date(data.expirationTime)) {
data.errors.push(`line ${data.lineno}: invalid expiration-time date time semantics`);
}
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid expiration-time date time syntax`);
break;
}
},
notBefore: function notBefore(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.notBefore = utils.charsToString(chars, phraseIndex, result.phraseLength);
if (!isValidISO8601Date(data.notBefore)) {
data.errors.push(`line ${data.lineno}: invalid not-before date time semantics`);
}
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid not-before date time syntax`);
break;
}
},
requestId: function requestId(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.requestId = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.EMPTY:
data.requestId = "";
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid requestID`);
break;
}
},
chainId: function chainId(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.chainId = (0, utils_1.parseIntegerNumber)(utils.charsToString(chars, phraseIndex, result.phraseLength));
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid chain-id`);
break;
}
},
uriR: function uriR(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriR = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid resource URI`);
break;
}
},
resource: function resource(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.resources.push(data.uriR);
delete data.uriR;
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid resource`);
break;
}
},
// handle the URI
scheme: function scheme(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.scheme = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI scheme`);
break;
}
},
userinfo: function userinfo(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.userinfo = utils.charsToString(chars, phraseIndex, result.phraseLength - 1);
break;
}
},
host: function host(result, chars, phraseIndex, data) {
switch (result.state) {
case id.ACTIVE:
data.iplit = false;
break;
case id.MATCH:
if (data.iplit) {
// strip leading "[" and trailing "]" brackets
data.uriElements.host = utils.charsToString(chars, phraseIndex + 1, result.phraseLength - 2);
}
else {
data.uriElements.host = utils.charsToString(chars, phraseIndex, result.phraseLength);
}
break;
case id.EMPTY:
data.uriElements.host = "";
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI host`);
break;
}
},
ipLiteral: function ipLiteral(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
data.iplit = true;
}
},
port: function port(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.port = (0, utils_1.parseIntegerNumber)(utils.charsToString(chars, phraseIndex, result.phraseLength));
break;
case id.EMPTY:
data.uriElements.port = "";
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI port`);
break;
}
},
pathAbempty: function pathAbempty(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.path = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.EMPTY:
data.uriElements.path = "";
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI path-abempty`);
break;
}
},
pathAbsolute: function pathAbsolute(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.path = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
}
},
pathRootless: function pathRootless(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.path = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
}
},
pathEmpty: function pathEmpty(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI - path-empty must be empty`);
break;
case id.EMPTY:
data.uriElements.path = "";
break;
}
},
query: function query(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.query = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.EMPTY:
data.uriElements.query = "";
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI query`);
break;
}
},
fragment: function fragment(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.uriElements.fragment = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.EMPTY:
data.uriElements.fragment = "";
break;
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI fragment`);
break;
}
},
uri: function URI(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
//NOTE: all "valid-url" tests are satisfied if URI ABNF parses without error.
data.uri = utils.charsToString(chars, phraseIndex, result.phraseLength);
break;
case id.EMPTY:
case id.NOMATCH:
data.errors.push(`line ${data.lineno}: invalid URI`);
break;
}
},
ipv4: function ipv4(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
data.ipv4 = true;
}
},
h16: function h16(result, chars, phraseIndex, data) {
if (result.state === id.MATCH) {
data.h16count += 1;
}
},
nodcolon: function nodcolon(result, chars, phraseIndex, data) {
switch (result.state) {
case id.ACTIVE:
data.h16count = 0;
data.ipv4 = false;
break;
case id.MATCH:
// semantically validate the number of 16-bit digits
if (data.ipv4) {
if (data.h16count === 6) {
result.state = id.MATCH;
}
else {
result.state = id.NOMATCH;
result.phraseLength = 0;
}
}
else {
if (data.h16count === 8) {
result.state = id.MATCH;
}
else {
result.state = id.NOMATCH;
result.phraseLength = 0;
}
}
break;
}
},
dcolon: function dcolon(result, chars, phraseIndex, data) {
switch (result.state) {
case id.ACTIVE:
data.h16count = 0;
data.ipv4 = false;
break;
case id.MATCH:
// semantically validate the number of 16-bit digits
if (data.ipv4) {
if (data.h16count < 6) {
result.state = id.MATCH;
}
else {
result.state = id.NOMATCH;
result.phraseLength = 0;
}
}
else {
if (data.h16count < 8) {
result.state = id.MATCH;
}
else {
result.state = id.NOMATCH;
result.phraseLength = 0;
}
}
break;
}
},
decOctet: function decOctet(result, chars, phraseIndex, data) {
switch (result.state) {
case id.ACTIVE:
data.octet = 0;
break;
case id.MATCH:
// semantically validate the octet
if (data.octet > 255) {
result.state = id.NOMATCH;
result.phraseLength = 0;
}
else {
result.state = id.MATCH;
}
break;
}
},
decDigit: function decDigit(result, chars, phraseIndex, data) {
switch (result.state) {
case id.MATCH:
data.octet = 10 * data.octet + chars[phraseIndex] - 48;
break;
}
},
};