@metamask-previews/controller-utils 11.2.0-preview-9edcf0e0 → 11.2.0-preview-cf09c0a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.cjs +138 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/{types/constants.d.ts → constants.d.cts} +2 -2
- package/dist/constants.d.cts.map +1 -0
- package/dist/constants.d.mts +117 -0
- package/dist/constants.d.mts.map +1 -0
- package/dist/constants.mjs +133 -54
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs +48 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{types/index.d.ts → index.d.cts} +6 -6
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +4 -137
- package/dist/index.mjs.map +1 -1
- package/dist/logger.cjs +7 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/{types/logger.d.ts → logger.d.cts} +2 -2
- package/dist/logger.d.cts.map +1 -0
- package/dist/logger.d.mts +5 -0
- package/dist/logger.d.mts.map +1 -0
- package/dist/logger.mjs +3 -8
- package/dist/logger.mjs.map +1 -1
- package/dist/siwe.cjs +128 -0
- package/dist/siwe.cjs.map +1 -0
- package/dist/{types/siwe.d.ts → siwe.d.cts} +2 -2
- package/dist/siwe.d.cts.map +1 -0
- package/dist/siwe.d.mts +68 -0
- package/dist/siwe.d.mts.map +1 -0
- package/dist/siwe.mjs +120 -10
- package/dist/siwe.mjs.map +1 -1
- package/dist/types.cjs +107 -0
- package/dist/types.cjs.map +1 -0
- package/dist/{types/types.d.ts → types.d.cts} +1 -1
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +132 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +100 -21
- package/dist/types.mjs.map +1 -1
- package/dist/util.cjs +512 -0
- package/dist/util.cjs.map +1 -0
- package/dist/{types/util.d.ts → util.d.cts} +4 -4
- package/dist/util.d.cts.map +1 -0
- package/dist/util.d.mts +243 -0
- package/dist/util.d.mts.map +1 -0
- package/dist/util.mjs +488 -59
- package/dist/util.mjs.map +1 -1
- package/package.json +12 -7
- package/dist/chunk-2DKE2P5J.mjs +0 -306
- package/dist/chunk-2DKE2P5J.mjs.map +0 -1
- package/dist/chunk-6LGAYF2M.mjs +0 -147
- package/dist/chunk-6LGAYF2M.mjs.map +0 -1
- package/dist/chunk-CQMU5S2P.mjs +0 -85
- package/dist/chunk-CQMU5S2P.mjs.map +0 -1
- package/dist/chunk-DBJSLKNR.js +0 -9
- package/dist/chunk-DBJSLKNR.js.map +0 -1
- package/dist/chunk-M6G3ISXG.js +0 -86
- package/dist/chunk-M6G3ISXG.js.map +0 -1
- package/dist/chunk-MG6F6B4L.js +0 -147
- package/dist/chunk-MG6F6B4L.js.map +0 -1
- package/dist/chunk-RZFCNKW4.mjs +0 -9
- package/dist/chunk-RZFCNKW4.mjs.map +0 -1
- package/dist/chunk-SAQIILOG.js +0 -306
- package/dist/chunk-SAQIILOG.js.map +0 -1
- package/dist/chunk-VDEIYYE4.mjs +0 -86
- package/dist/chunk-VDEIYYE4.mjs.map +0 -1
- package/dist/chunk-XH4YXW3J.js +0 -85
- package/dist/chunk-XH4YXW3J.js.map +0 -1
- package/dist/constants.js +0 -56
- package/dist/constants.js.map +0 -1
- package/dist/index.js +0 -138
- package/dist/index.js.map +0 -1
- package/dist/logger.js +0 -9
- package/dist/logger.js.map +0 -1
- package/dist/siwe.js +0 -12
- package/dist/siwe.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/dist/types/constants.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/logger.d.ts.map +0 -1
- package/dist/types/siwe.d.ts.map +0 -1
- package/dist/types/types.d.ts.map +0 -1
- package/dist/types/util.d.ts.map +0 -1
- package/dist/types.js +0 -23
- package/dist/types.js.map +0 -1
- package/dist/util.js +0 -61
- package/dist/util.js.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,138 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "./chunk-VDEIYYE4.mjs";
|
|
6
|
-
import "./chunk-RZFCNKW4.mjs";
|
|
7
|
-
import {
|
|
8
|
-
BNToHex,
|
|
9
|
-
convertHexToDecimal,
|
|
10
|
-
fetchWithErrorHandling,
|
|
11
|
-
fractionBN,
|
|
12
|
-
fromHex,
|
|
13
|
-
getBuyURL,
|
|
14
|
-
gweiDecToWEIBN,
|
|
15
|
-
handleFetch,
|
|
16
|
-
hexToBN,
|
|
17
|
-
hexToText,
|
|
18
|
-
isNonEmptyArray,
|
|
19
|
-
isPlainObject,
|
|
20
|
-
isSafeChainId,
|
|
21
|
-
isSafeDynamicKey,
|
|
22
|
-
isSmartContractCode,
|
|
23
|
-
isValidHexAddress,
|
|
24
|
-
isValidJson,
|
|
25
|
-
normalizeEnsName,
|
|
26
|
-
query,
|
|
27
|
-
safelyExecute,
|
|
28
|
-
safelyExecuteWithTimeout,
|
|
29
|
-
successfulFetch,
|
|
30
|
-
timeoutFetch,
|
|
31
|
-
toChecksumHexAddress,
|
|
32
|
-
toHex,
|
|
33
|
-
weiHexToGweiDec
|
|
34
|
-
} from "./chunk-2DKE2P5J.mjs";
|
|
35
|
-
import {
|
|
36
|
-
ASSET_TYPES,
|
|
37
|
-
ApprovalType,
|
|
38
|
-
BUILT_IN_NETWORKS,
|
|
39
|
-
CHAIN_ID_TO_ETHERS_NETWORK_NAME_MAP,
|
|
40
|
-
ERC1155,
|
|
41
|
-
ERC1155_INTERFACE_ID,
|
|
42
|
-
ERC1155_METADATA_URI_INTERFACE_ID,
|
|
43
|
-
ERC1155_TOKEN_RECEIVER_INTERFACE_ID,
|
|
44
|
-
ERC20,
|
|
45
|
-
ERC721,
|
|
46
|
-
ERC721_ENUMERABLE_INTERFACE_ID,
|
|
47
|
-
ERC721_INTERFACE_ID,
|
|
48
|
-
ERC721_METADATA_INTERFACE_ID,
|
|
49
|
-
FALL_BACK_VS_CURRENCY,
|
|
50
|
-
GANACHE_CHAIN_ID,
|
|
51
|
-
GWEI,
|
|
52
|
-
IPFS_DEFAULT_GATEWAY_URL,
|
|
53
|
-
MAX_SAFE_CHAIN_ID,
|
|
54
|
-
NFT_API_BASE_URL,
|
|
55
|
-
NFT_API_TIMEOUT,
|
|
56
|
-
NFT_API_VERSION,
|
|
57
|
-
OPENSEA_PROXY_URL,
|
|
58
|
-
ORIGIN_METAMASK,
|
|
59
|
-
RPC,
|
|
60
|
-
TESTNET_TICKER_SYMBOLS
|
|
61
|
-
} from "./chunk-6LGAYF2M.mjs";
|
|
62
|
-
import {
|
|
63
|
-
BlockExplorerUrl,
|
|
64
|
-
BuiltInNetworkName,
|
|
65
|
-
ChainId,
|
|
66
|
-
InfuraNetworkType,
|
|
67
|
-
NetworkNickname,
|
|
68
|
-
NetworkType,
|
|
69
|
-
NetworksTicker,
|
|
70
|
-
isInfuraNetworkType,
|
|
71
|
-
isNetworkType
|
|
72
|
-
} from "./chunk-CQMU5S2P.mjs";
|
|
73
|
-
export {
|
|
74
|
-
ASSET_TYPES,
|
|
75
|
-
ApprovalType,
|
|
76
|
-
BNToHex,
|
|
77
|
-
BUILT_IN_NETWORKS,
|
|
78
|
-
BlockExplorerUrl,
|
|
79
|
-
BuiltInNetworkName,
|
|
80
|
-
CHAIN_ID_TO_ETHERS_NETWORK_NAME_MAP,
|
|
81
|
-
ChainId,
|
|
82
|
-
ERC1155,
|
|
83
|
-
ERC1155_INTERFACE_ID,
|
|
84
|
-
ERC1155_METADATA_URI_INTERFACE_ID,
|
|
85
|
-
ERC1155_TOKEN_RECEIVER_INTERFACE_ID,
|
|
86
|
-
ERC20,
|
|
87
|
-
ERC721,
|
|
88
|
-
ERC721_ENUMERABLE_INTERFACE_ID,
|
|
89
|
-
ERC721_INTERFACE_ID,
|
|
90
|
-
ERC721_METADATA_INTERFACE_ID,
|
|
91
|
-
FALL_BACK_VS_CURRENCY,
|
|
92
|
-
GANACHE_CHAIN_ID,
|
|
93
|
-
GWEI,
|
|
94
|
-
IPFS_DEFAULT_GATEWAY_URL,
|
|
95
|
-
InfuraNetworkType,
|
|
96
|
-
MAX_SAFE_CHAIN_ID,
|
|
97
|
-
NFT_API_BASE_URL,
|
|
98
|
-
NFT_API_TIMEOUT,
|
|
99
|
-
NFT_API_VERSION,
|
|
100
|
-
NetworkNickname,
|
|
101
|
-
NetworkType,
|
|
102
|
-
NetworksTicker,
|
|
103
|
-
OPENSEA_PROXY_URL,
|
|
104
|
-
ORIGIN_METAMASK,
|
|
105
|
-
RPC,
|
|
106
|
-
TESTNET_TICKER_SYMBOLS,
|
|
107
|
-
convertHexToDecimal,
|
|
108
|
-
detectSIWE,
|
|
109
|
-
fetchWithErrorHandling,
|
|
110
|
-
fractionBN,
|
|
111
|
-
fromHex,
|
|
112
|
-
getBuyURL,
|
|
113
|
-
gweiDecToWEIBN,
|
|
114
|
-
handleFetch,
|
|
115
|
-
hexToBN,
|
|
116
|
-
hexToText,
|
|
117
|
-
isInfuraNetworkType,
|
|
118
|
-
isNetworkType,
|
|
119
|
-
isNonEmptyArray,
|
|
120
|
-
isPlainObject,
|
|
121
|
-
isSafeChainId,
|
|
122
|
-
isSafeDynamicKey,
|
|
123
|
-
isSmartContractCode,
|
|
124
|
-
isValidHexAddress,
|
|
125
|
-
isValidJson,
|
|
126
|
-
isValidSIWEOrigin,
|
|
127
|
-
normalizeEnsName,
|
|
128
|
-
parseDomainParts,
|
|
129
|
-
query,
|
|
130
|
-
safelyExecute,
|
|
131
|
-
safelyExecuteWithTimeout,
|
|
132
|
-
successfulFetch,
|
|
133
|
-
timeoutFetch,
|
|
134
|
-
toChecksumHexAddress,
|
|
135
|
-
toHex,
|
|
136
|
-
weiHexToGweiDec
|
|
137
|
-
};
|
|
1
|
+
export * from "./constants.mjs";
|
|
2
|
+
export { BNToHex, convertHexToDecimal, fetchWithErrorHandling, fractionBN, fromHex, getBuyURL, gweiDecToWEIBN, handleFetch, hexToBN, hexToText, isNonEmptyArray, isPlainObject, isSafeChainId, isSafeDynamicKey, isSmartContractCode, isValidJson, isValidHexAddress, normalizeEnsName, query, safelyExecute, safelyExecuteWithTimeout, successfulFetch, timeoutFetch, toChecksumHexAddress, toHex, weiHexToGweiDec } from "./util.mjs";
|
|
3
|
+
export * from "./types.mjs";
|
|
4
|
+
export * from "./siwe.mjs";
|
|
138
5
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gCAA4B;AAE5B,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,sBAAsB,EACtB,UAAU,EACV,OAAO,EACP,SAAS,EACT,cAAc,EACd,WAAW,EACX,OAAO,EACP,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,EACL,aAAa,EACb,wBAAwB,EACxB,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,KAAK,EACL,eAAe,EAChB,mBAAe;AAChB,4BAAwB;AACxB,2BAAuB","sourcesContent":["export * from './constants';\nexport type { NonEmptyArray } from './util';\nexport {\n BNToHex,\n convertHexToDecimal,\n fetchWithErrorHandling,\n fractionBN,\n fromHex,\n getBuyURL,\n gweiDecToWEIBN,\n handleFetch,\n hexToBN,\n hexToText,\n isNonEmptyArray,\n isPlainObject,\n isSafeChainId,\n isSafeDynamicKey,\n isSmartContractCode,\n isValidJson,\n isValidHexAddress,\n normalizeEnsName,\n query,\n safelyExecute,\n safelyExecuteWithTimeout,\n successfulFetch,\n timeoutFetch,\n toChecksumHexAddress,\n toHex,\n weiHexToGweiDec,\n} from './util';\nexport * from './types';\nexport * from './siwe';\n"]}
|
package/dist/logger.cjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createModuleLogger = exports.projectLogger = void 0;
|
|
4
|
+
const utils_1 = require("@metamask/utils");
|
|
5
|
+
Object.defineProperty(exports, "createModuleLogger", { enumerable: true, get: function () { return utils_1.createModuleLogger; } });
|
|
6
|
+
exports.projectLogger = (0, utils_1.createProjectLogger)('controller-utils');
|
|
7
|
+
//# sourceMappingURL=logger.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.cjs","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";;;AAAA,2CAA0E;AAIjE,mGAJqB,0BAAkB,OAIrB;AAFd,QAAA,aAAa,GAAG,IAAA,2BAAmB,EAAC,kBAAkB,CAAC,CAAC","sourcesContent":["import { createProjectLogger, createModuleLogger } from '@metamask/utils';\n\nexport const projectLogger = createProjectLogger('controller-utils');\n\nexport { createModuleLogger };\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="debug" />
|
|
2
|
-
import { createModuleLogger } from
|
|
2
|
+
import { createModuleLogger } from "@metamask/utils";
|
|
3
3
|
export declare const projectLogger: import("debug").Debugger;
|
|
4
4
|
export { createModuleLogger };
|
|
5
|
-
//# sourceMappingURL=logger.d.
|
|
5
|
+
//# sourceMappingURL=logger.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.cts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";AAAA,OAAO,EAAuB,kBAAkB,EAAE,wBAAwB;AAE1E,eAAO,MAAM,aAAa,0BAA0C,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.mts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";AAAA,OAAO,EAAuB,kBAAkB,EAAE,wBAAwB;AAE1E,eAAO,MAAM,aAAa,0BAA0C,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
package/dist/logger.mjs
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-RZFCNKW4.mjs";
|
|
5
|
-
export {
|
|
6
|
-
createModuleLogger,
|
|
7
|
-
projectLogger
|
|
8
|
-
};
|
|
1
|
+
import { createProjectLogger, createModuleLogger } from "@metamask/utils";
|
|
2
|
+
export const projectLogger = createProjectLogger('controller-utils');
|
|
3
|
+
export { createModuleLogger };
|
|
9
4
|
//# sourceMappingURL=logger.mjs.map
|
package/dist/logger.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"
|
|
1
|
+
{"version":3,"file":"logger.mjs","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,wBAAwB;AAE1E,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAAC,kBAAkB,CAAC,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,CAAC","sourcesContent":["import { createProjectLogger, createModuleLogger } from '@metamask/utils';\n\nexport const projectLogger = createProjectLogger('controller-utils');\n\nexport { createModuleLogger };\n"]}
|
package/dist/siwe.cjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectSIWE = exports.isValidSIWEOrigin = exports.parseDomainParts = void 0;
|
|
4
|
+
const utils_1 = require("@metamask/utils");
|
|
5
|
+
const siwe_parser_1 = require("@spruceid/siwe-parser");
|
|
6
|
+
const logger_1 = require("./logger.cjs");
|
|
7
|
+
const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, 'detect-siwe');
|
|
8
|
+
/**
|
|
9
|
+
* This function strips the hex prefix from a string if it has one.
|
|
10
|
+
* If the input is not a string, return it unmodified.
|
|
11
|
+
*
|
|
12
|
+
* @param str - The string to check
|
|
13
|
+
* @returns The string without the hex prefix
|
|
14
|
+
*/
|
|
15
|
+
function safeStripHexPrefix(str) {
|
|
16
|
+
if (typeof str !== 'string') {
|
|
17
|
+
return str;
|
|
18
|
+
}
|
|
19
|
+
return (0, utils_1.remove0x)(str);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* This function converts a hex string to text if it's not a 32 byte hex string.
|
|
23
|
+
*
|
|
24
|
+
* @param hex - The hex string to convert to text
|
|
25
|
+
* @returns The text representation of the hex string
|
|
26
|
+
*/
|
|
27
|
+
function msgHexToText(hex) {
|
|
28
|
+
try {
|
|
29
|
+
const stripped = safeStripHexPrefix(hex);
|
|
30
|
+
const buff = Buffer.from(stripped, 'hex');
|
|
31
|
+
return buff.length === 32 ? hex : buff.toString('utf8');
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
log(e);
|
|
35
|
+
return hex;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const DEFAULT_PORTS_BY_PROTOCOL = {
|
|
39
|
+
'http:': '80',
|
|
40
|
+
'https:': '443',
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Parses parts from RFC 3986 authority from EIP-4361 `domain` field.
|
|
44
|
+
*
|
|
45
|
+
* @param domain - input string
|
|
46
|
+
* @param originProtocol - implied protocol from origin
|
|
47
|
+
* @returns parsed parts
|
|
48
|
+
*/
|
|
49
|
+
const parseDomainParts = (domain, originProtocol) => {
|
|
50
|
+
if (domain.match(/^[^/:]*:\/\//u)) {
|
|
51
|
+
return new URL(domain);
|
|
52
|
+
}
|
|
53
|
+
return new URL(`${originProtocol}//${domain}`);
|
|
54
|
+
};
|
|
55
|
+
exports.parseDomainParts = parseDomainParts;
|
|
56
|
+
/**
|
|
57
|
+
* Validates origin of a Sign-In With Ethereum (SIWE)(EIP-4361) request.
|
|
58
|
+
* As per spec:
|
|
59
|
+
* hostname must match.
|
|
60
|
+
* port and username must match iff specified.
|
|
61
|
+
* Protocol binding and full same-origin are currently not performed.
|
|
62
|
+
*
|
|
63
|
+
* @param req - Signature request
|
|
64
|
+
* @returns true if origin matches domain; false otherwise
|
|
65
|
+
*/
|
|
66
|
+
const isValidSIWEOrigin = (req) => {
|
|
67
|
+
try {
|
|
68
|
+
const { origin, siwe } = req;
|
|
69
|
+
// origin = scheme://[user[:password]@]domain[:port]
|
|
70
|
+
// origin is supplied by environment and must match domain claim in message
|
|
71
|
+
if (!origin || !siwe?.parsedMessage?.domain) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const originParts = new URL(origin);
|
|
75
|
+
const domainParts = (0, exports.parseDomainParts)(siwe.parsedMessage.domain, originParts.protocol);
|
|
76
|
+
if (domainParts.hostname.localeCompare(originParts.hostname, undefined, {
|
|
77
|
+
sensitivity: 'accent',
|
|
78
|
+
}) !== 0) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (domainParts.port !== '' && domainParts.port !== originParts.port) {
|
|
82
|
+
// If origin port is not specified, protocol default is implied
|
|
83
|
+
return (originParts.port === '' &&
|
|
84
|
+
domainParts.port === DEFAULT_PORTS_BY_PROTOCOL[originParts.protocol]);
|
|
85
|
+
}
|
|
86
|
+
if (domainParts.username !== '' &&
|
|
87
|
+
domainParts.username !== originParts.username) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
log(e);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
exports.isValidSIWEOrigin = isValidSIWEOrigin;
|
|
98
|
+
/**
|
|
99
|
+
* This function intercepts a sign message, detects if it's a
|
|
100
|
+
* Sign-In With Ethereum (SIWE)(EIP-4361) message, and returns an object with
|
|
101
|
+
* relevant SIWE data.
|
|
102
|
+
*
|
|
103
|
+
* {@see {@link https://eips.ethereum.org/EIPS/eip-4361}}
|
|
104
|
+
*
|
|
105
|
+
* @param msgParams - The params of the message to sign
|
|
106
|
+
* @param msgParams.data - The data of the message to sign
|
|
107
|
+
* @returns An object with the relevant SIWE data
|
|
108
|
+
*/
|
|
109
|
+
const detectSIWE = (msgParams) => {
|
|
110
|
+
try {
|
|
111
|
+
const { data } = msgParams;
|
|
112
|
+
const message = msgHexToText(data);
|
|
113
|
+
const parsedMessage = new siwe_parser_1.ParsedMessage(message);
|
|
114
|
+
return {
|
|
115
|
+
isSIWEMessage: true,
|
|
116
|
+
parsedMessage,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// ignore error, it's not a valid SIWE message
|
|
121
|
+
return {
|
|
122
|
+
isSIWEMessage: false,
|
|
123
|
+
parsedMessage: null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
exports.detectSIWE = detectSIWE;
|
|
128
|
+
//# sourceMappingURL=siwe.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siwe.cjs","sourceRoot":"","sources":["../src/siwe.ts"],"names":[],"mappings":"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","sourcesContent":["import { remove0x } from '@metamask/utils';\nimport { ParsedMessage } from '@spruceid/siwe-parser';\n\nimport { projectLogger, createModuleLogger } from './logger';\n\nconst log = createModuleLogger(projectLogger, 'detect-siwe');\n\n/**\n * This function strips the hex prefix from a string if it has one.\n * If the input is not a string, return it unmodified.\n *\n * @param str - The string to check\n * @returns The string without the hex prefix\n */\nfunction safeStripHexPrefix(str: string) {\n if (typeof str !== 'string') {\n return str;\n }\n return remove0x(str);\n}\n\n/**\n * This function converts a hex string to text if it's not a 32 byte hex string.\n *\n * @param hex - The hex string to convert to text\n * @returns The text representation of the hex string\n */\nfunction msgHexToText(hex: string): string {\n try {\n const stripped = safeStripHexPrefix(hex);\n const buff = Buffer.from(stripped, 'hex');\n return buff.length === 32 ? hex : buff.toString('utf8');\n } catch (e) {\n log(e);\n return hex;\n }\n}\n\n/**\n * @type WrappedSIWERequest\n *\n * Sign-In With Ethereum (SIWE)(EIP-4361) message with request metadata\n * @property {string} from - Subject account address\n * @property {string} origin - The RFC 3986 originating authority of the signing request, including scheme\n * @property {ParsedMessage} siwe - The data parsed from the message\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface WrappedSIWERequest {\n from: string;\n origin: string;\n siwe: SIWEMessage;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface DomainParts {\n username?: string;\n hostname: string;\n port?: string;\n}\n\nconst DEFAULT_PORTS_BY_PROTOCOL = {\n 'http:': '80',\n 'https:': '443',\n} as Record<string, string>;\n\n/**\n * Parses parts from RFC 3986 authority from EIP-4361 `domain` field.\n *\n * @param domain - input string\n * @param originProtocol - implied protocol from origin\n * @returns parsed parts\n */\nexport const parseDomainParts = (\n domain: string,\n originProtocol: string,\n): DomainParts => {\n if (domain.match(/^[^/:]*:\\/\\//u)) {\n return new URL(domain);\n }\n return new URL(`${originProtocol}//${domain}`);\n};\n\n/**\n * Validates origin of a Sign-In With Ethereum (SIWE)(EIP-4361) request.\n * As per spec:\n * hostname must match.\n * port and username must match iff specified.\n * Protocol binding and full same-origin are currently not performed.\n *\n * @param req - Signature request\n * @returns true if origin matches domain; false otherwise\n */\nexport const isValidSIWEOrigin = (req: WrappedSIWERequest): boolean => {\n try {\n const { origin, siwe } = req;\n\n // origin = scheme://[user[:password]@]domain[:port]\n // origin is supplied by environment and must match domain claim in message\n if (!origin || !siwe?.parsedMessage?.domain) {\n return false;\n }\n\n const originParts = new URL(origin);\n const domainParts = parseDomainParts(\n siwe.parsedMessage.domain,\n originParts.protocol,\n );\n\n if (\n domainParts.hostname.localeCompare(originParts.hostname, undefined, {\n sensitivity: 'accent',\n }) !== 0\n ) {\n return false;\n }\n\n if (domainParts.port !== '' && domainParts.port !== originParts.port) {\n // If origin port is not specified, protocol default is implied\n return (\n originParts.port === '' &&\n domainParts.port === DEFAULT_PORTS_BY_PROTOCOL[originParts.protocol]\n );\n }\n\n if (\n domainParts.username !== '' &&\n domainParts.username !== originParts.username\n ) {\n return false;\n }\n\n return true;\n } catch (e) {\n log(e);\n return false;\n }\n};\n\n/**\n * A locally defined object used to provide data to identify a Sign-In With Ethereum (SIWE)(EIP-4361) message and provide the parsed message\n *\n * @typedef SIWEMessage\n * @param {boolean} isSIWEMessage - Does the intercepted message conform to the SIWE specification?\n * @param {ParsedMessage} parsedMessage - The data parsed out of the message\n */\nexport type SIWEMessage =\n | { isSIWEMessage: true; parsedMessage: ParsedMessage }\n | { isSIWEMessage: false; parsedMessage: null };\n\n/**\n * This function intercepts a sign message, detects if it's a\n * Sign-In With Ethereum (SIWE)(EIP-4361) message, and returns an object with\n * relevant SIWE data.\n *\n * {@see {@link https://eips.ethereum.org/EIPS/eip-4361}}\n *\n * @param msgParams - The params of the message to sign\n * @param msgParams.data - The data of the message to sign\n * @returns An object with the relevant SIWE data\n */\nexport const detectSIWE = (msgParams: { data: string }): SIWEMessage => {\n try {\n const { data } = msgParams;\n const message = msgHexToText(data);\n const parsedMessage = new ParsedMessage(message);\n\n return {\n isSIWEMessage: true,\n parsedMessage,\n };\n } catch (error) {\n // ignore error, it's not a valid SIWE message\n return {\n isSIWEMessage: false,\n parsedMessage: null,\n };\n }\n};\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ParsedMessage } from
|
|
1
|
+
import { ParsedMessage } from "@spruceid/siwe-parser";
|
|
2
2
|
/**
|
|
3
3
|
* @type WrappedSIWERequest
|
|
4
4
|
*
|
|
@@ -65,4 +65,4 @@ export declare const detectSIWE: (msgParams: {
|
|
|
65
65
|
data: string;
|
|
66
66
|
}) => SIWEMessage;
|
|
67
67
|
export {};
|
|
68
|
-
//# sourceMappingURL=siwe.d.
|
|
68
|
+
//# sourceMappingURL=siwe.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siwe.d.cts","sourceRoot":"","sources":["../src/siwe.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,8BAA8B;AAqCtD;;;;;;;GAOG;AAIH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;CACnB;AAKD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,WACnB,MAAM,kBACE,MAAM,KACrB,WAKF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,QAAS,kBAAkB,KAAG,OA4C3D,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,aAAa,EAAE,IAAI,CAAC;IAAC,aAAa,EAAE,aAAa,CAAA;CAAE,GACrD;IAAE,aAAa,EAAE,KAAK,CAAC;IAAC,aAAa,EAAE,IAAI,CAAA;CAAE,CAAC;AAElD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,cAAe;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,KAAG,WAiBxD,CAAC"}
|
package/dist/siwe.d.mts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ParsedMessage } from "@spruceid/siwe-parser";
|
|
2
|
+
/**
|
|
3
|
+
* @type WrappedSIWERequest
|
|
4
|
+
*
|
|
5
|
+
* Sign-In With Ethereum (SIWE)(EIP-4361) message with request metadata
|
|
6
|
+
* @property {string} from - Subject account address
|
|
7
|
+
* @property {string} origin - The RFC 3986 originating authority of the signing request, including scheme
|
|
8
|
+
* @property {ParsedMessage} siwe - The data parsed from the message
|
|
9
|
+
*/
|
|
10
|
+
export interface WrappedSIWERequest {
|
|
11
|
+
from: string;
|
|
12
|
+
origin: string;
|
|
13
|
+
siwe: SIWEMessage;
|
|
14
|
+
}
|
|
15
|
+
interface DomainParts {
|
|
16
|
+
username?: string;
|
|
17
|
+
hostname: string;
|
|
18
|
+
port?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parses parts from RFC 3986 authority from EIP-4361 `domain` field.
|
|
22
|
+
*
|
|
23
|
+
* @param domain - input string
|
|
24
|
+
* @param originProtocol - implied protocol from origin
|
|
25
|
+
* @returns parsed parts
|
|
26
|
+
*/
|
|
27
|
+
export declare const parseDomainParts: (domain: string, originProtocol: string) => DomainParts;
|
|
28
|
+
/**
|
|
29
|
+
* Validates origin of a Sign-In With Ethereum (SIWE)(EIP-4361) request.
|
|
30
|
+
* As per spec:
|
|
31
|
+
* hostname must match.
|
|
32
|
+
* port and username must match iff specified.
|
|
33
|
+
* Protocol binding and full same-origin are currently not performed.
|
|
34
|
+
*
|
|
35
|
+
* @param req - Signature request
|
|
36
|
+
* @returns true if origin matches domain; false otherwise
|
|
37
|
+
*/
|
|
38
|
+
export declare const isValidSIWEOrigin: (req: WrappedSIWERequest) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* A locally defined object used to provide data to identify a Sign-In With Ethereum (SIWE)(EIP-4361) message and provide the parsed message
|
|
41
|
+
*
|
|
42
|
+
* @typedef SIWEMessage
|
|
43
|
+
* @param {boolean} isSIWEMessage - Does the intercepted message conform to the SIWE specification?
|
|
44
|
+
* @param {ParsedMessage} parsedMessage - The data parsed out of the message
|
|
45
|
+
*/
|
|
46
|
+
export type SIWEMessage = {
|
|
47
|
+
isSIWEMessage: true;
|
|
48
|
+
parsedMessage: ParsedMessage;
|
|
49
|
+
} | {
|
|
50
|
+
isSIWEMessage: false;
|
|
51
|
+
parsedMessage: null;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* This function intercepts a sign message, detects if it's a
|
|
55
|
+
* Sign-In With Ethereum (SIWE)(EIP-4361) message, and returns an object with
|
|
56
|
+
* relevant SIWE data.
|
|
57
|
+
*
|
|
58
|
+
* {@see {@link https://eips.ethereum.org/EIPS/eip-4361}}
|
|
59
|
+
*
|
|
60
|
+
* @param msgParams - The params of the message to sign
|
|
61
|
+
* @param msgParams.data - The data of the message to sign
|
|
62
|
+
* @returns An object with the relevant SIWE data
|
|
63
|
+
*/
|
|
64
|
+
export declare const detectSIWE: (msgParams: {
|
|
65
|
+
data: string;
|
|
66
|
+
}) => SIWEMessage;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=siwe.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siwe.d.mts","sourceRoot":"","sources":["../src/siwe.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,8BAA8B;AAqCtD;;;;;;;GAOG;AAIH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;CACnB;AAKD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,WACnB,MAAM,kBACE,MAAM,KACrB,WAKF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,QAAS,kBAAkB,KAAG,OA4C3D,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,aAAa,EAAE,IAAI,CAAC;IAAC,aAAa,EAAE,aAAa,CAAA;CAAE,GACrD;IAAE,aAAa,EAAE,KAAK,CAAC;IAAC,aAAa,EAAE,IAAI,CAAA;CAAE,CAAC;AAElD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,cAAe;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,KAAG,WAiBxD,CAAC"}
|
package/dist/siwe.mjs
CHANGED
|
@@ -1,12 +1,122 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { remove0x } from "@metamask/utils";
|
|
2
|
+
import { ParsedMessage } from "@spruceid/siwe-parser";
|
|
3
|
+
import { projectLogger, createModuleLogger } from "./logger.mjs";
|
|
4
|
+
const log = createModuleLogger(projectLogger, 'detect-siwe');
|
|
5
|
+
/**
|
|
6
|
+
* This function strips the hex prefix from a string if it has one.
|
|
7
|
+
* If the input is not a string, return it unmodified.
|
|
8
|
+
*
|
|
9
|
+
* @param str - The string to check
|
|
10
|
+
* @returns The string without the hex prefix
|
|
11
|
+
*/
|
|
12
|
+
function safeStripHexPrefix(str) {
|
|
13
|
+
if (typeof str !== 'string') {
|
|
14
|
+
return str;
|
|
15
|
+
}
|
|
16
|
+
return remove0x(str);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* This function converts a hex string to text if it's not a 32 byte hex string.
|
|
20
|
+
*
|
|
21
|
+
* @param hex - The hex string to convert to text
|
|
22
|
+
* @returns The text representation of the hex string
|
|
23
|
+
*/
|
|
24
|
+
function msgHexToText(hex) {
|
|
25
|
+
try {
|
|
26
|
+
const stripped = safeStripHexPrefix(hex);
|
|
27
|
+
const buff = Buffer.from(stripped, 'hex');
|
|
28
|
+
return buff.length === 32 ? hex : buff.toString('utf8');
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
log(e);
|
|
32
|
+
return hex;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const DEFAULT_PORTS_BY_PROTOCOL = {
|
|
36
|
+
'http:': '80',
|
|
37
|
+
'https:': '443',
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Parses parts from RFC 3986 authority from EIP-4361 `domain` field.
|
|
41
|
+
*
|
|
42
|
+
* @param domain - input string
|
|
43
|
+
* @param originProtocol - implied protocol from origin
|
|
44
|
+
* @returns parsed parts
|
|
45
|
+
*/
|
|
46
|
+
export const parseDomainParts = (domain, originProtocol) => {
|
|
47
|
+
if (domain.match(/^[^/:]*:\/\//u)) {
|
|
48
|
+
return new URL(domain);
|
|
49
|
+
}
|
|
50
|
+
return new URL(`${originProtocol}//${domain}`);
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Validates origin of a Sign-In With Ethereum (SIWE)(EIP-4361) request.
|
|
54
|
+
* As per spec:
|
|
55
|
+
* hostname must match.
|
|
56
|
+
* port and username must match iff specified.
|
|
57
|
+
* Protocol binding and full same-origin are currently not performed.
|
|
58
|
+
*
|
|
59
|
+
* @param req - Signature request
|
|
60
|
+
* @returns true if origin matches domain; false otherwise
|
|
61
|
+
*/
|
|
62
|
+
export const isValidSIWEOrigin = (req) => {
|
|
63
|
+
try {
|
|
64
|
+
const { origin, siwe } = req;
|
|
65
|
+
// origin = scheme://[user[:password]@]domain[:port]
|
|
66
|
+
// origin is supplied by environment and must match domain claim in message
|
|
67
|
+
if (!origin || !siwe?.parsedMessage?.domain) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const originParts = new URL(origin);
|
|
71
|
+
const domainParts = parseDomainParts(siwe.parsedMessage.domain, originParts.protocol);
|
|
72
|
+
if (domainParts.hostname.localeCompare(originParts.hostname, undefined, {
|
|
73
|
+
sensitivity: 'accent',
|
|
74
|
+
}) !== 0) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (domainParts.port !== '' && domainParts.port !== originParts.port) {
|
|
78
|
+
// If origin port is not specified, protocol default is implied
|
|
79
|
+
return (originParts.port === '' &&
|
|
80
|
+
domainParts.port === DEFAULT_PORTS_BY_PROTOCOL[originParts.protocol]);
|
|
81
|
+
}
|
|
82
|
+
if (domainParts.username !== '' &&
|
|
83
|
+
domainParts.username !== originParts.username) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
log(e);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* This function intercepts a sign message, detects if it's a
|
|
95
|
+
* Sign-In With Ethereum (SIWE)(EIP-4361) message, and returns an object with
|
|
96
|
+
* relevant SIWE data.
|
|
97
|
+
*
|
|
98
|
+
* {@see {@link https://eips.ethereum.org/EIPS/eip-4361}}
|
|
99
|
+
*
|
|
100
|
+
* @param msgParams - The params of the message to sign
|
|
101
|
+
* @param msgParams.data - The data of the message to sign
|
|
102
|
+
* @returns An object with the relevant SIWE data
|
|
103
|
+
*/
|
|
104
|
+
export const detectSIWE = (msgParams) => {
|
|
105
|
+
try {
|
|
106
|
+
const { data } = msgParams;
|
|
107
|
+
const message = msgHexToText(data);
|
|
108
|
+
const parsedMessage = new ParsedMessage(message);
|
|
109
|
+
return {
|
|
110
|
+
isSIWEMessage: true,
|
|
111
|
+
parsedMessage,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// ignore error, it's not a valid SIWE message
|
|
116
|
+
return {
|
|
117
|
+
isSIWEMessage: false,
|
|
118
|
+
parsedMessage: null,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
11
121
|
};
|
|
12
122
|
//# sourceMappingURL=siwe.mjs.map
|
package/dist/siwe.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"
|
|
1
|
+
{"version":3,"file":"siwe.mjs","sourceRoot":"","sources":["../src/siwe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,wBAAwB;AAC3C,OAAO,EAAE,aAAa,EAAE,8BAA8B;AAEtD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,qBAAiB;AAE7D,MAAM,GAAG,GAAG,kBAAkB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAE7D;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,GAAW;IACrC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;QAC3B,OAAO,GAAG,CAAC;KACZ;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI;QACF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;KACzD;IAAC,OAAO,CAAC,EAAE;QACV,GAAG,CAAC,CAAC,CAAC,CAAC;QACP,OAAO,GAAG,CAAC;KACZ;AACH,CAAC;AA4BD,MAAM,yBAAyB,GAAG;IAChC,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,KAAK;CACU,CAAC;AAE5B;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,MAAc,EACd,cAAsB,EACT,EAAE;IACf,IAAI,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE;QACjC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;KACxB;IACD,OAAO,IAAI,GAAG,CAAC,GAAG,cAAc,KAAK,MAAM,EAAE,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAuB,EAAW,EAAE;IACpE,IAAI;QACF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;QAE7B,oDAAoD;QACpD,2EAA2E;QAC3E,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE;YAC3C,OAAO,KAAK,CAAC;SACd;QAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,WAAW,GAAG,gBAAgB,CAClC,IAAI,CAAC,aAAa,CAAC,MAAM,EACzB,WAAW,CAAC,QAAQ,CACrB,CAAC;QAEF,IACE,WAAW,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,CAAC,QAAQ,EAAE,SAAS,EAAE;YAClE,WAAW,EAAE,QAAQ;SACtB,CAAC,KAAK,CAAC,EACR;YACA,OAAO,KAAK,CAAC;SACd;QAED,IAAI,WAAW,CAAC,IAAI,KAAK,EAAE,IAAI,WAAW,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,EAAE;YACpE,+DAA+D;YAC/D,OAAO,CACL,WAAW,CAAC,IAAI,KAAK,EAAE;gBACvB,WAAW,CAAC,IAAI,KAAK,yBAAyB,CAAC,WAAW,CAAC,QAAQ,CAAC,CACrE,CAAC;SACH;QAED,IACE,WAAW,CAAC,QAAQ,KAAK,EAAE;YAC3B,WAAW,CAAC,QAAQ,KAAK,WAAW,CAAC,QAAQ,EAC7C;YACA,OAAO,KAAK,CAAC;SACd;QAED,OAAO,IAAI,CAAC;KACb;IAAC,OAAO,CAAC,EAAE;QACV,GAAG,CAAC,CAAC,CAAC,CAAC;QACP,OAAO,KAAK,CAAC;KACd;AACH,CAAC,CAAC;AAaF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,SAA2B,EAAe,EAAE;IACrE,IAAI;QACF,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;QAC3B,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO;YACL,aAAa,EAAE,IAAI;YACnB,aAAa;SACd,CAAC;KACH;IAAC,OAAO,KAAK,EAAE;QACd,8CAA8C;QAC9C,OAAO;YACL,aAAa,EAAE,KAAK;YACpB,aAAa,EAAE,IAAI;SACpB,CAAC;KACH;AACH,CAAC,CAAC","sourcesContent":["import { remove0x } from '@metamask/utils';\nimport { ParsedMessage } from '@spruceid/siwe-parser';\n\nimport { projectLogger, createModuleLogger } from './logger';\n\nconst log = createModuleLogger(projectLogger, 'detect-siwe');\n\n/**\n * This function strips the hex prefix from a string if it has one.\n * If the input is not a string, return it unmodified.\n *\n * @param str - The string to check\n * @returns The string without the hex prefix\n */\nfunction safeStripHexPrefix(str: string) {\n if (typeof str !== 'string') {\n return str;\n }\n return remove0x(str);\n}\n\n/**\n * This function converts a hex string to text if it's not a 32 byte hex string.\n *\n * @param hex - The hex string to convert to text\n * @returns The text representation of the hex string\n */\nfunction msgHexToText(hex: string): string {\n try {\n const stripped = safeStripHexPrefix(hex);\n const buff = Buffer.from(stripped, 'hex');\n return buff.length === 32 ? hex : buff.toString('utf8');\n } catch (e) {\n log(e);\n return hex;\n }\n}\n\n/**\n * @type WrappedSIWERequest\n *\n * Sign-In With Ethereum (SIWE)(EIP-4361) message with request metadata\n * @property {string} from - Subject account address\n * @property {string} origin - The RFC 3986 originating authority of the signing request, including scheme\n * @property {ParsedMessage} siwe - The data parsed from the message\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface WrappedSIWERequest {\n from: string;\n origin: string;\n siwe: SIWEMessage;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface DomainParts {\n username?: string;\n hostname: string;\n port?: string;\n}\n\nconst DEFAULT_PORTS_BY_PROTOCOL = {\n 'http:': '80',\n 'https:': '443',\n} as Record<string, string>;\n\n/**\n * Parses parts from RFC 3986 authority from EIP-4361 `domain` field.\n *\n * @param domain - input string\n * @param originProtocol - implied protocol from origin\n * @returns parsed parts\n */\nexport const parseDomainParts = (\n domain: string,\n originProtocol: string,\n): DomainParts => {\n if (domain.match(/^[^/:]*:\\/\\//u)) {\n return new URL(domain);\n }\n return new URL(`${originProtocol}//${domain}`);\n};\n\n/**\n * Validates origin of a Sign-In With Ethereum (SIWE)(EIP-4361) request.\n * As per spec:\n * hostname must match.\n * port and username must match iff specified.\n * Protocol binding and full same-origin are currently not performed.\n *\n * @param req - Signature request\n * @returns true if origin matches domain; false otherwise\n */\nexport const isValidSIWEOrigin = (req: WrappedSIWERequest): boolean => {\n try {\n const { origin, siwe } = req;\n\n // origin = scheme://[user[:password]@]domain[:port]\n // origin is supplied by environment and must match domain claim in message\n if (!origin || !siwe?.parsedMessage?.domain) {\n return false;\n }\n\n const originParts = new URL(origin);\n const domainParts = parseDomainParts(\n siwe.parsedMessage.domain,\n originParts.protocol,\n );\n\n if (\n domainParts.hostname.localeCompare(originParts.hostname, undefined, {\n sensitivity: 'accent',\n }) !== 0\n ) {\n return false;\n }\n\n if (domainParts.port !== '' && domainParts.port !== originParts.port) {\n // If origin port is not specified, protocol default is implied\n return (\n originParts.port === '' &&\n domainParts.port === DEFAULT_PORTS_BY_PROTOCOL[originParts.protocol]\n );\n }\n\n if (\n domainParts.username !== '' &&\n domainParts.username !== originParts.username\n ) {\n return false;\n }\n\n return true;\n } catch (e) {\n log(e);\n return false;\n }\n};\n\n/**\n * A locally defined object used to provide data to identify a Sign-In With Ethereum (SIWE)(EIP-4361) message and provide the parsed message\n *\n * @typedef SIWEMessage\n * @param {boolean} isSIWEMessage - Does the intercepted message conform to the SIWE specification?\n * @param {ParsedMessage} parsedMessage - The data parsed out of the message\n */\nexport type SIWEMessage =\n | { isSIWEMessage: true; parsedMessage: ParsedMessage }\n | { isSIWEMessage: false; parsedMessage: null };\n\n/**\n * This function intercepts a sign message, detects if it's a\n * Sign-In With Ethereum (SIWE)(EIP-4361) message, and returns an object with\n * relevant SIWE data.\n *\n * {@see {@link https://eips.ethereum.org/EIPS/eip-4361}}\n *\n * @param msgParams - The params of the message to sign\n * @param msgParams.data - The data of the message to sign\n * @returns An object with the relevant SIWE data\n */\nexport const detectSIWE = (msgParams: { data: string }): SIWEMessage => {\n try {\n const { data } = msgParams;\n const message = msgHexToText(data);\n const parsedMessage = new ParsedMessage(message);\n\n return {\n isSIWEMessage: true,\n parsedMessage,\n };\n } catch (error) {\n // ignore error, it's not a valid SIWE message\n return {\n isSIWEMessage: false,\n parsedMessage: null,\n };\n }\n};\n"]}
|