@bananalink-sdk/protocol 1.2.7
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/README.md +604 -0
- package/dist/chunk-32OWUOZ3.js +308 -0
- package/dist/chunk-32OWUOZ3.js.map +1 -0
- package/dist/chunk-65HNHRJK.cjs +123 -0
- package/dist/chunk-65HNHRJK.cjs.map +1 -0
- package/dist/chunk-7KYDLL3B.js +480 -0
- package/dist/chunk-7KYDLL3B.js.map +1 -0
- package/dist/chunk-A6FLEJ7R.cjs +62 -0
- package/dist/chunk-A6FLEJ7R.cjs.map +1 -0
- package/dist/chunk-CUJK7ZTS.js +217 -0
- package/dist/chunk-CUJK7ZTS.js.map +1 -0
- package/dist/chunk-GI3BUPIH.cjs +236 -0
- package/dist/chunk-GI3BUPIH.cjs.map +1 -0
- package/dist/chunk-JXHV66Q4.js +106 -0
- package/dist/chunk-JXHV66Q4.js.map +1 -0
- package/dist/chunk-KNGZKGRS.cjs +552 -0
- package/dist/chunk-KNGZKGRS.cjs.map +1 -0
- package/dist/chunk-LELPCIE7.js +840 -0
- package/dist/chunk-LELPCIE7.js.map +1 -0
- package/dist/chunk-MCZG7QEM.cjs +310 -0
- package/dist/chunk-MCZG7QEM.cjs.map +1 -0
- package/dist/chunk-TCVKC227.js +56 -0
- package/dist/chunk-TCVKC227.js.map +1 -0
- package/dist/chunk-VXLUSU5B.cjs +856 -0
- package/dist/chunk-VXLUSU5B.cjs.map +1 -0
- package/dist/chunk-WCQVDF3K.js +12 -0
- package/dist/chunk-WCQVDF3K.js.map +1 -0
- package/dist/chunk-WGEGR3DF.cjs +15 -0
- package/dist/chunk-WGEGR3DF.cjs.map +1 -0
- package/dist/client-session-claim-3QF3noOr.d.ts +197 -0
- package/dist/client-session-claim-C4lUik3b.d.cts +197 -0
- package/dist/core-DMhuNfoz.d.cts +62 -0
- package/dist/core-DMhuNfoz.d.ts +62 -0
- package/dist/crypto/providers/noble-provider.cjs +14 -0
- package/dist/crypto/providers/noble-provider.cjs.map +1 -0
- package/dist/crypto/providers/noble-provider.d.cts +30 -0
- package/dist/crypto/providers/noble-provider.d.ts +30 -0
- package/dist/crypto/providers/noble-provider.js +5 -0
- package/dist/crypto/providers/noble-provider.js.map +1 -0
- package/dist/crypto/providers/node-provider.cjs +308 -0
- package/dist/crypto/providers/node-provider.cjs.map +1 -0
- package/dist/crypto/providers/node-provider.d.cts +32 -0
- package/dist/crypto/providers/node-provider.d.ts +32 -0
- package/dist/crypto/providers/node-provider.js +306 -0
- package/dist/crypto/providers/node-provider.js.map +1 -0
- package/dist/crypto/providers/quickcrypto-provider.cjs +339 -0
- package/dist/crypto/providers/quickcrypto-provider.cjs.map +1 -0
- package/dist/crypto/providers/quickcrypto-provider.d.cts +34 -0
- package/dist/crypto/providers/quickcrypto-provider.d.ts +34 -0
- package/dist/crypto/providers/quickcrypto-provider.js +337 -0
- package/dist/crypto/providers/quickcrypto-provider.js.map +1 -0
- package/dist/crypto/providers/webcrypto-provider.cjs +310 -0
- package/dist/crypto/providers/webcrypto-provider.cjs.map +1 -0
- package/dist/crypto/providers/webcrypto-provider.d.cts +30 -0
- package/dist/crypto/providers/webcrypto-provider.d.ts +30 -0
- package/dist/crypto/providers/webcrypto-provider.js +308 -0
- package/dist/crypto/providers/webcrypto-provider.js.map +1 -0
- package/dist/crypto-BUS06Qz-.d.cts +40 -0
- package/dist/crypto-BUS06Qz-.d.ts +40 -0
- package/dist/crypto-export.cjs +790 -0
- package/dist/crypto-export.cjs.map +1 -0
- package/dist/crypto-export.d.cts +257 -0
- package/dist/crypto-export.d.ts +257 -0
- package/dist/crypto-export.js +709 -0
- package/dist/crypto-export.js.map +1 -0
- package/dist/crypto-provider-deYoVIxi.d.cts +36 -0
- package/dist/crypto-provider-deYoVIxi.d.ts +36 -0
- package/dist/index.cjs +615 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +379 -0
- package/dist/index.d.ts +379 -0
- package/dist/index.js +504 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas-export.cjs +294 -0
- package/dist/schemas-export.cjs.map +1 -0
- package/dist/schemas-export.d.cts +1598 -0
- package/dist/schemas-export.d.ts +1598 -0
- package/dist/schemas-export.js +5 -0
- package/dist/schemas-export.js.map +1 -0
- package/dist/siwe-export.cjs +237 -0
- package/dist/siwe-export.cjs.map +1 -0
- package/dist/siwe-export.d.cts +27 -0
- package/dist/siwe-export.d.ts +27 -0
- package/dist/siwe-export.js +228 -0
- package/dist/siwe-export.js.map +1 -0
- package/dist/testing.cjs +54 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +20 -0
- package/dist/testing.d.ts +20 -0
- package/dist/testing.js +51 -0
- package/dist/testing.js.map +1 -0
- package/dist/validation-export.cjs +359 -0
- package/dist/validation-export.cjs.map +1 -0
- package/dist/validation-export.d.cts +3 -0
- package/dist/validation-export.d.ts +3 -0
- package/dist/validation-export.js +6 -0
- package/dist/validation-export.js.map +1 -0
- package/dist/validators-export.cjs +73 -0
- package/dist/validators-export.cjs.map +1 -0
- package/dist/validators-export.d.cts +37 -0
- package/dist/validators-export.d.ts +37 -0
- package/dist/validators-export.js +4 -0
- package/dist/validators-export.js.map +1 -0
- package/package.json +140 -0
- package/src/constants/index.ts +205 -0
- package/src/crypto/context.ts +228 -0
- package/src/crypto/diagnostics.ts +772 -0
- package/src/crypto/errors.ts +114 -0
- package/src/crypto/index.ts +89 -0
- package/src/crypto/payload-handler.ts +102 -0
- package/src/crypto/providers/compliance-provider.ts +579 -0
- package/src/crypto/providers/factory.ts +204 -0
- package/src/crypto/providers/index.ts +44 -0
- package/src/crypto/providers/noble-provider.ts +392 -0
- package/src/crypto/providers/node-provider.ts +433 -0
- package/src/crypto/providers/quickcrypto-provider.ts +483 -0
- package/src/crypto/providers/registry.ts +129 -0
- package/src/crypto/providers/webcrypto-provider.ts +364 -0
- package/src/crypto/session-security.ts +185 -0
- package/src/crypto/types.ts +93 -0
- package/src/crypto/utils.ts +190 -0
- package/src/crypto-export.ts +21 -0
- package/src/index.ts +38 -0
- package/src/schemas/auth.ts +60 -0
- package/src/schemas/client-messages.ts +57 -0
- package/src/schemas/core.ts +144 -0
- package/src/schemas/crypto.ts +65 -0
- package/src/schemas/discovery.ts +79 -0
- package/src/schemas/index.ts +239 -0
- package/src/schemas/relay-messages.ts +45 -0
- package/src/schemas/wallet-messages.ts +177 -0
- package/src/schemas-export.ts +23 -0
- package/src/siwe-export.ts +27 -0
- package/src/testing.ts +71 -0
- package/src/types/auth.ts +60 -0
- package/src/types/client-messages.ts +84 -0
- package/src/types/core.ts +131 -0
- package/src/types/crypto-provider.ts +264 -0
- package/src/types/crypto.ts +90 -0
- package/src/types/discovery.ts +50 -0
- package/src/types/errors.ts +87 -0
- package/src/types/index.ts +197 -0
- package/src/types/post-auth-operations.ts +363 -0
- package/src/types/providers.ts +72 -0
- package/src/types/relay-messages.ts +60 -0
- package/src/types/request-lifecycle.ts +161 -0
- package/src/types/signing-operations.ts +99 -0
- package/src/types/wallet-messages.ts +251 -0
- package/src/utils/client-session-claim.ts +188 -0
- package/src/utils/index.ts +54 -0
- package/src/utils/public-keys.ts +49 -0
- package/src/utils/siwe.ts +362 -0
- package/src/utils/url-decoding.ts +126 -0
- package/src/utils/url-encoding.ts +144 -0
- package/src/utils/wallet-session-claim.ts +188 -0
- package/src/validation-export.ts +32 -0
- package/src/validators/index.ts +222 -0
- package/src/validators-export.ts +8 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { authenticateConnectionPayloadSchema, authenticationDataSchema, bananaLinkErrorSchema, base64Schema, claimSessionPayloadSchema, clientMessageEnvelopeSchema, clientMessagePayloadSchema, clientReconnectPayloadSchema, clientSessionClaimSchema, closeSessionPayloadSchema, connectionAuthenticatedMessageSchema, connectionRejectedMessageSchema, connectionRejectedPayloadSchema, dAppMetadataSchema as coreDAppMetadataSchema, securityPolicySchema as coreSecurityPolicySchema, createSessionRequestSchema, createSessionResponseSchema, dAppMetadataSchema2 as dAppMetadataSchema, displayInfoSchema, encryptedPayloadSchema, errorCodeSchema, frutiLinkSchema, originProofSchema, prefetchMetadataPayloadSchema, prefetchMetadataResponseSchema, publicKeySchema, qrPayloadSchema, reconnectedMessageSchema, rejectionDataSchema, relayMessageSchema, relayMessageTypeSchema, relayNotificationMessageSchema, relayToDAppMessageSchema, safeValidateClientMessage, safeValidateClientSessionClaim, safeValidateFrutiLink, safeValidateQRPayload, safeValidateRelayMessage, safeValidateRelayNotificationMessage, safeValidateRelayToDAppMessage, safeValidateSessionInfo, safeValidateWalletMessage, safeValidateWalletSessionClaim, securityPolicySchema2 as securityPolicySchema, sessionClosedAckSchema, sessionClosedNotificationSchema, sessionConfigSchema, sessionIdSchema, sessionInfoSchema, sessionMetadataSchema, sessionOptionsSchema, sessionStateSchema, siweFieldsSchema, supportedChainIdSchema, validateClientMessage, validateClientSessionClaim, validateFrutiLink, validateQRPayload, validateRelayMessage, validateRelayNotificationMessage, validateRelayToDAppMessage, validateSessionInfo, validateTimestamp, validateWalletMessage, validateWalletSessionClaim, walletHandshakeMessageSchema, walletMessageEnvelopeSchema, walletMessagePayloadSchema, walletMetadataSchema, walletReconnectPayloadSchema, walletSessionClaimSchema } from './chunk-7KYDLL3B.js';
|
|
2
|
+
import './chunk-CUJK7ZTS.js';
|
|
3
|
+
import './chunk-WCQVDF3K.js';
|
|
4
|
+
//# sourceMappingURL=schemas-export.js.map
|
|
5
|
+
//# sourceMappingURL=schemas-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"schemas-export.js"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkWGEGR3DF_cjs = require('./chunk-WGEGR3DF.cjs');
|
|
4
|
+
|
|
5
|
+
// src/utils/siwe.ts
|
|
6
|
+
function constructSIWEMessage(options) {
|
|
7
|
+
const {
|
|
8
|
+
domain,
|
|
9
|
+
address,
|
|
10
|
+
statement,
|
|
11
|
+
uri,
|
|
12
|
+
version = "1",
|
|
13
|
+
chainId,
|
|
14
|
+
nonce,
|
|
15
|
+
issuedAt = (/* @__PURE__ */ new Date()).toISOString(),
|
|
16
|
+
expirationTime,
|
|
17
|
+
notBefore,
|
|
18
|
+
requestId,
|
|
19
|
+
resources,
|
|
20
|
+
scheme
|
|
21
|
+
} = options;
|
|
22
|
+
if (!domain || !address || !uri || !chainId || !nonce) {
|
|
23
|
+
throw new Error("Missing required SIWE fields: domain, address, uri, chainId, nonce");
|
|
24
|
+
}
|
|
25
|
+
if (!isValidEthereumAddress(address)) {
|
|
26
|
+
throw new Error("Invalid Ethereum address format");
|
|
27
|
+
}
|
|
28
|
+
const parts = [];
|
|
29
|
+
const header = scheme ? `${scheme}://${domain}` : domain;
|
|
30
|
+
parts.push(`${header} wants you to sign in with your Ethereum account:`);
|
|
31
|
+
parts.push(address);
|
|
32
|
+
parts.push("");
|
|
33
|
+
if (statement) {
|
|
34
|
+
parts.push(statement);
|
|
35
|
+
parts.push("");
|
|
36
|
+
}
|
|
37
|
+
parts.push(`URI: ${uri}`);
|
|
38
|
+
parts.push(`Version: ${version}`);
|
|
39
|
+
parts.push(`Chain ID: ${chainId}`);
|
|
40
|
+
parts.push(`Nonce: ${nonce}`);
|
|
41
|
+
parts.push(`Issued At: ${issuedAt}`);
|
|
42
|
+
if (expirationTime) {
|
|
43
|
+
parts.push(`Expiration Time: ${expirationTime}`);
|
|
44
|
+
}
|
|
45
|
+
if (notBefore) {
|
|
46
|
+
parts.push(`Not Before: ${notBefore}`);
|
|
47
|
+
}
|
|
48
|
+
if (requestId) {
|
|
49
|
+
parts.push(`Request ID: ${requestId}`);
|
|
50
|
+
}
|
|
51
|
+
if (resources && resources.length > 0) {
|
|
52
|
+
parts.push("Resources:");
|
|
53
|
+
resources.forEach((resource) => {
|
|
54
|
+
parts.push(`- ${resource}`);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return parts.join("\n");
|
|
58
|
+
}
|
|
59
|
+
chunkWGEGR3DF_cjs.__name(constructSIWEMessage, "constructSIWEMessage");
|
|
60
|
+
function parseSIWEMessage(message) {
|
|
61
|
+
const lines = message.split("\n");
|
|
62
|
+
if (lines.length < 6) {
|
|
63
|
+
throw new Error("Invalid SIWE message format: insufficient lines");
|
|
64
|
+
}
|
|
65
|
+
const headerMatch = lines[0].match(/^((?<scheme>[^:]+):\/\/)?(?<domain>.+) wants you to sign in with your Ethereum account:$/);
|
|
66
|
+
if (!headerMatch) {
|
|
67
|
+
throw new Error("Invalid SIWE message header");
|
|
68
|
+
}
|
|
69
|
+
const fields = {
|
|
70
|
+
scheme: headerMatch.groups?.scheme,
|
|
71
|
+
domain: headerMatch.groups?.domain || "",
|
|
72
|
+
address: lines[1],
|
|
73
|
+
statement: void 0,
|
|
74
|
+
uri: "",
|
|
75
|
+
version: "1",
|
|
76
|
+
chainId: 0,
|
|
77
|
+
nonce: "",
|
|
78
|
+
issuedAt: ""
|
|
79
|
+
};
|
|
80
|
+
let fieldIndex = 2;
|
|
81
|
+
const statementLines = [];
|
|
82
|
+
while (fieldIndex < lines.length && !lines[fieldIndex].startsWith("URI: ")) {
|
|
83
|
+
if (lines[fieldIndex]) {
|
|
84
|
+
statementLines.push(lines[fieldIndex]);
|
|
85
|
+
}
|
|
86
|
+
fieldIndex++;
|
|
87
|
+
}
|
|
88
|
+
if (statementLines.length > 0) {
|
|
89
|
+
if (statementLines[statementLines.length - 1] === "") {
|
|
90
|
+
statementLines.pop();
|
|
91
|
+
}
|
|
92
|
+
fields.statement = statementLines.join("\n");
|
|
93
|
+
}
|
|
94
|
+
for (let i = fieldIndex; i < lines.length; i++) {
|
|
95
|
+
const line = lines[i];
|
|
96
|
+
if (line.startsWith("URI: ")) {
|
|
97
|
+
fields.uri = line.substring(5);
|
|
98
|
+
} else if (line.startsWith("Version: ")) {
|
|
99
|
+
fields.version = line.substring(9);
|
|
100
|
+
} else if (line.startsWith("Chain ID: ")) {
|
|
101
|
+
fields.chainId = parseInt(line.substring(10), 10);
|
|
102
|
+
} else if (line.startsWith("Nonce: ")) {
|
|
103
|
+
fields.nonce = line.substring(7);
|
|
104
|
+
} else if (line.startsWith("Issued At: ")) {
|
|
105
|
+
fields.issuedAt = line.substring(11);
|
|
106
|
+
} else if (line.startsWith("Expiration Time: ")) {
|
|
107
|
+
fields.expirationTime = line.substring(17);
|
|
108
|
+
} else if (line.startsWith("Not Before: ")) {
|
|
109
|
+
fields.notBefore = line.substring(12);
|
|
110
|
+
} else if (line.startsWith("Request ID: ")) {
|
|
111
|
+
fields.requestId = line.substring(12);
|
|
112
|
+
} else if (line === "Resources:") {
|
|
113
|
+
fields.resources = [];
|
|
114
|
+
i++;
|
|
115
|
+
while (i < lines.length && lines[i].startsWith("- ")) {
|
|
116
|
+
fields.resources.push(lines[i].substring(2));
|
|
117
|
+
i++;
|
|
118
|
+
}
|
|
119
|
+
i--;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!fields.uri || !fields.nonce || !fields.chainId || !fields.issuedAt) {
|
|
123
|
+
throw new Error("Missing required SIWE fields in message");
|
|
124
|
+
}
|
|
125
|
+
return fields;
|
|
126
|
+
}
|
|
127
|
+
chunkWGEGR3DF_cjs.__name(parseSIWEMessage, "parseSIWEMessage");
|
|
128
|
+
function createBananaLinkSIWEMessage(sessionId, address, domain, chainId, nonce, statement) {
|
|
129
|
+
return constructSIWEMessage({
|
|
130
|
+
domain,
|
|
131
|
+
address,
|
|
132
|
+
statement: statement || `Sign in to ${domain}`,
|
|
133
|
+
uri: `bananalink://session/${sessionId}`,
|
|
134
|
+
chainId,
|
|
135
|
+
nonce,
|
|
136
|
+
version: "1",
|
|
137
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
chunkWGEGR3DF_cjs.__name(createBananaLinkSIWEMessage, "createBananaLinkSIWEMessage");
|
|
141
|
+
function isValidEthereumAddress(address) {
|
|
142
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
143
|
+
}
|
|
144
|
+
chunkWGEGR3DF_cjs.__name(isValidEthereumAddress, "isValidEthereumAddress");
|
|
145
|
+
function validateSIWETimestamp(timestamp, maxAge = 5 * 60 * 1e3) {
|
|
146
|
+
try {
|
|
147
|
+
const messageTime = new Date(timestamp).getTime();
|
|
148
|
+
const now = Date.now();
|
|
149
|
+
const age = Math.abs(now - messageTime);
|
|
150
|
+
return age <= maxAge;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
chunkWGEGR3DF_cjs.__name(validateSIWETimestamp, "validateSIWETimestamp");
|
|
156
|
+
function validateSIWEExpiration(expirationTime) {
|
|
157
|
+
if (expirationTime === "") {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (!expirationTime) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const expiry = new Date(expirationTime).getTime();
|
|
165
|
+
if (isNaN(expiry)) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return Date.now() < expiry;
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
chunkWGEGR3DF_cjs.__name(validateSIWEExpiration, "validateSIWEExpiration");
|
|
174
|
+
function validateSIWENotBefore(notBefore) {
|
|
175
|
+
if (notBefore === "") {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (!notBefore) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const notBeforeTime = new Date(notBefore).getTime();
|
|
183
|
+
if (isNaN(notBeforeTime)) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
return Date.now() >= notBeforeTime;
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
chunkWGEGR3DF_cjs.__name(validateSIWENotBefore, "validateSIWENotBefore");
|
|
192
|
+
function validateSIWEMessage(fields, options = {}) {
|
|
193
|
+
const errors = [];
|
|
194
|
+
if (!fields.domain) errors.push("Missing domain");
|
|
195
|
+
if (!fields.address) errors.push("Missing address");
|
|
196
|
+
if (!fields.uri) errors.push("Missing URI");
|
|
197
|
+
if (!fields.chainId) errors.push("Missing chain ID");
|
|
198
|
+
if (!fields.nonce) errors.push("Missing nonce");
|
|
199
|
+
if (!fields.issuedAt) errors.push("Missing issued at timestamp");
|
|
200
|
+
if (fields.address && !isValidEthereumAddress(fields.address)) {
|
|
201
|
+
errors.push("Invalid Ethereum address format");
|
|
202
|
+
}
|
|
203
|
+
if (fields.version !== "1") {
|
|
204
|
+
errors.push(`Invalid SIWE version: ${fields.version} (must be 1)`);
|
|
205
|
+
}
|
|
206
|
+
if (options.expectedDomain && fields.domain !== options.expectedDomain) {
|
|
207
|
+
errors.push(`Domain mismatch: expected ${options.expectedDomain}, got ${fields.domain}`);
|
|
208
|
+
}
|
|
209
|
+
if (options.expectedChainId && fields.chainId !== options.expectedChainId) {
|
|
210
|
+
errors.push(`Chain ID mismatch: expected ${options.expectedChainId}, got ${fields.chainId}`);
|
|
211
|
+
}
|
|
212
|
+
if (!validateSIWETimestamp(fields.issuedAt, options.maxAge)) {
|
|
213
|
+
errors.push("Message timestamp is too old or invalid");
|
|
214
|
+
}
|
|
215
|
+
if (!validateSIWEExpiration(fields.expirationTime)) {
|
|
216
|
+
errors.push("Message has expired");
|
|
217
|
+
}
|
|
218
|
+
if (!validateSIWENotBefore(fields.notBefore)) {
|
|
219
|
+
errors.push("Message is not yet valid (not-before time not reached)");
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
valid: errors.length === 0,
|
|
223
|
+
errors
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
chunkWGEGR3DF_cjs.__name(validateSIWEMessage, "validateSIWEMessage");
|
|
227
|
+
|
|
228
|
+
exports.constructSIWEMessage = constructSIWEMessage;
|
|
229
|
+
exports.createBananaLinkSIWEMessage = createBananaLinkSIWEMessage;
|
|
230
|
+
exports.isValidEthereumAddress = isValidEthereumAddress;
|
|
231
|
+
exports.parseSIWEMessage = parseSIWEMessage;
|
|
232
|
+
exports.validateSIWEExpiration = validateSIWEExpiration;
|
|
233
|
+
exports.validateSIWEMessage = validateSIWEMessage;
|
|
234
|
+
exports.validateSIWENotBefore = validateSIWENotBefore;
|
|
235
|
+
exports.validateSIWETimestamp = validateSIWETimestamp;
|
|
236
|
+
//# sourceMappingURL=siwe-export.cjs.map
|
|
237
|
+
//# sourceMappingURL=siwe-export.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/siwe.ts"],"names":["__name"],"mappings":"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","file":"siwe-export.cjs","sourcesContent":["/**\n * SIWE (Sign-In with Ethereum) message construction utilities\n * Implements ERC-4361 compliant message formatting\n */\n\nimport type { SIWEFields } from '../types/core';\n\n/**\n * Options for SIWE message construction\n */\nexport interface SIWEMessageOptions extends Partial<SIWEFields> {\n domain: string;\n address: string;\n uri: string;\n chainId: number;\n nonce: string;\n statement?: string;\n}\n\n/**\n * Construct a SIWE message according to ERC-4361 specification\n * @param options - SIWE message options\n * @returns Formatted SIWE message string\n */\nexport function constructSIWEMessage(options: SIWEMessageOptions): string {\n const {\n domain,\n address,\n statement,\n uri,\n version = '1',\n chainId,\n nonce,\n issuedAt = new Date().toISOString(),\n expirationTime,\n notBefore,\n requestId,\n resources,\n scheme,\n } = options;\n\n // Validate required fields\n if (!domain || !address || !uri || !chainId || !nonce) {\n throw new Error('Missing required SIWE fields: domain, address, uri, chainId, nonce');\n }\n\n // Validate address format\n if (!isValidEthereumAddress(address)) {\n throw new Error('Invalid Ethereum address format');\n }\n\n // Construct message parts according to ERC-4361\n const parts: string[] = [];\n\n // Header: domain wants you to sign in\n const header = scheme ? `${scheme}://${domain}` : domain;\n parts.push(`${header} wants you to sign in with your Ethereum account:`);\n parts.push(address);\n parts.push(''); // Empty line\n\n // Optional statement\n if (statement) {\n parts.push(statement);\n parts.push(''); // Empty line\n }\n\n // Required fields\n parts.push(`URI: ${uri}`);\n parts.push(`Version: ${version}`);\n parts.push(`Chain ID: ${chainId}`);\n parts.push(`Nonce: ${nonce}`);\n parts.push(`Issued At: ${issuedAt}`);\n\n // Optional fields\n if (expirationTime) {\n parts.push(`Expiration Time: ${expirationTime}`);\n }\n if (notBefore) {\n parts.push(`Not Before: ${notBefore}`);\n }\n if (requestId) {\n parts.push(`Request ID: ${requestId}`);\n }\n\n // Resources\n if (resources && resources.length > 0) {\n parts.push('Resources:');\n resources.forEach(resource => {\n parts.push(`- ${resource}`);\n });\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Parse a SIWE message string into its component fields\n * @param message - SIWE message string\n * @returns Parsed SIWE fields\n */\nexport function parseSIWEMessage(message: string): SIWEFields {\n const lines = message.split('\\n');\n\n if (lines.length < 6) {\n throw new Error('Invalid SIWE message format: insufficient lines');\n }\n\n // Parse header\n const headerMatch = lines[0].match(/^((?<scheme>[^:]+):\\/\\/)?(?<domain>.+) wants you to sign in with your Ethereum account:$/);\n if (!headerMatch) {\n throw new Error('Invalid SIWE message header');\n }\n\n const fields: SIWEFields = {\n scheme: headerMatch.groups?.scheme,\n domain: headerMatch.groups?.domain || '',\n address: lines[1],\n statement: undefined,\n uri: '',\n version: '1',\n chainId: 0,\n nonce: '',\n issuedAt: '',\n };\n\n // Find where the required fields start\n let fieldIndex = 2;\n\n // Check for optional statement (non-empty lines before URI)\n const statementLines: string[] = [];\n while (fieldIndex < lines.length && !lines[fieldIndex].startsWith('URI: ')) {\n if (lines[fieldIndex]) {\n statementLines.push(lines[fieldIndex]);\n }\n fieldIndex++;\n }\n\n if (statementLines.length > 0) {\n // Remove trailing empty line if present\n if (statementLines[statementLines.length - 1] === '') {\n statementLines.pop();\n }\n fields.statement = statementLines.join('\\n');\n }\n\n // Parse required and optional fields\n for (let i = fieldIndex; i < lines.length; i++) {\n const line = lines[i];\n\n if (line.startsWith('URI: ')) {\n fields.uri = line.substring(5);\n } else if (line.startsWith('Version: ')) {\n fields.version = line.substring(9);\n } else if (line.startsWith('Chain ID: ')) {\n fields.chainId = parseInt(line.substring(10), 10);\n } else if (line.startsWith('Nonce: ')) {\n fields.nonce = line.substring(7);\n } else if (line.startsWith('Issued At: ')) {\n fields.issuedAt = line.substring(11);\n } else if (line.startsWith('Expiration Time: ')) {\n fields.expirationTime = line.substring(17);\n } else if (line.startsWith('Not Before: ')) {\n fields.notBefore = line.substring(12);\n } else if (line.startsWith('Request ID: ')) {\n fields.requestId = line.substring(12);\n } else if (line === 'Resources:') {\n // Parse resources list\n fields.resources = [];\n i++;\n while (i < lines.length && lines[i].startsWith('- ')) {\n fields.resources.push(lines[i].substring(2));\n i++;\n }\n i--; // Adjust for the outer loop increment\n }\n }\n\n // Validate required fields\n if (!fields.uri || !fields.nonce || !fields.chainId || !fields.issuedAt) {\n throw new Error('Missing required SIWE fields in message');\n }\n\n return fields;\n}\n\n/**\n * Create a BananaLink-specific SIWE message for session authentication\n * @param sessionId - Session identifier\n * @param address - Ethereum address\n * @param domain - dApp domain\n * @param chainId - Chain ID\n * @param nonce - Session nonce\n * @param statement - Optional custom statement\n * @returns Formatted SIWE message\n */\nexport function createBananaLinkSIWEMessage(\n sessionId: string,\n address: string,\n domain: string,\n chainId: number,\n nonce: string,\n statement?: string\n): string {\n return constructSIWEMessage({\n domain,\n address,\n statement: statement || `Sign in to ${domain}`,\n uri: `bananalink://session/${sessionId}`,\n chainId,\n nonce,\n version: '1',\n issuedAt: new Date().toISOString(),\n });\n}\n\n/**\n * Validate an Ethereum address format\n * @param address - Address to validate\n * @returns True if valid Ethereum address\n */\nexport function isValidEthereumAddress(address: string): boolean {\n return /^0x[a-fA-F0-9]{40}$/.test(address);\n}\n\n// NOTE: Nonce generation moved to @bananalink-sdk/crypto package to avoid circular dependencies\n// SDKs should use crypto package to generate nonces before constructing SIWE messages\n\n/**\n * Validate SIWE message timestamp\n * @param timestamp - ISO 8601 timestamp\n * @param maxAge - Maximum age in milliseconds (default: 5 minutes)\n * @returns True if timestamp is within valid range\n */\nexport function validateSIWETimestamp(timestamp: string, maxAge: number = 5 * 60 * 1000): boolean {\n try {\n const messageTime = new Date(timestamp).getTime();\n const now = Date.now();\n const age = Math.abs(now - messageTime);\n return age <= maxAge;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate SIWE message expiration\n * @param expirationTime - Optional expiration timestamp\n * @returns True if message has not expired\n */\nexport function validateSIWEExpiration(expirationTime?: string): boolean {\n // Empty string should be treated as invalid (check before falsy check)\n if (expirationTime === '') {\n return false;\n }\n\n if (!expirationTime) {\n return true; // No expiration set (undefined or null)\n }\n\n try {\n const expiry = new Date(expirationTime).getTime();\n // Check for Invalid Date\n if (isNaN(expiry)) {\n return false;\n }\n return Date.now() < expiry;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate SIWE message not-before time\n * @param notBefore - Optional not-before timestamp\n * @returns True if current time is after not-before time\n */\nexport function validateSIWENotBefore(notBefore?: string): boolean {\n // Empty string should be treated as invalid (check before falsy check)\n if (notBefore === '') {\n return false;\n }\n\n if (!notBefore) {\n return true; // No not-before restriction (undefined or null)\n }\n\n try {\n const notBeforeTime = new Date(notBefore).getTime();\n // Check for Invalid Date\n if (isNaN(notBeforeTime)) {\n return false;\n }\n return Date.now() >= notBeforeTime;\n } catch {\n return false;\n }\n}\n\n/**\n * Comprehensive SIWE message validation\n * @param fields - SIWE fields to validate\n * @param options - Validation options\n * @returns Validation result with any errors\n */\nexport function validateSIWEMessage(\n fields: SIWEFields,\n options: {\n expectedDomain?: string;\n expectedChainId?: number;\n maxAge?: number;\n } = {}\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check required fields\n if (!fields.domain) errors.push('Missing domain');\n if (!fields.address) errors.push('Missing address');\n if (!fields.uri) errors.push('Missing URI');\n if (!fields.chainId) errors.push('Missing chain ID');\n if (!fields.nonce) errors.push('Missing nonce');\n if (!fields.issuedAt) errors.push('Missing issued at timestamp');\n\n // Validate address format\n if (fields.address && !isValidEthereumAddress(fields.address)) {\n errors.push('Invalid Ethereum address format');\n }\n\n // Validate version\n if (fields.version !== '1') {\n errors.push(`Invalid SIWE version: ${fields.version} (must be 1)`);\n }\n\n // Domain validation\n if (options.expectedDomain && fields.domain !== options.expectedDomain) {\n errors.push(`Domain mismatch: expected ${options.expectedDomain}, got ${fields.domain}`);\n }\n\n // Chain ID validation\n if (options.expectedChainId && fields.chainId !== options.expectedChainId) {\n errors.push(`Chain ID mismatch: expected ${options.expectedChainId}, got ${fields.chainId}`);\n }\n\n // Timestamp validation\n if (!validateSIWETimestamp(fields.issuedAt, options.maxAge)) {\n errors.push('Message timestamp is too old or invalid');\n }\n\n // Expiration validation\n if (!validateSIWEExpiration(fields.expirationTime)) {\n errors.push('Message has expired');\n }\n\n // Not-before validation\n if (!validateSIWENotBefore(fields.notBefore)) {\n errors.push('Message is not yet valid (not-before time not reached)');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { a as SIWEFields } from './core-DMhuNfoz.cjs';
|
|
2
|
+
|
|
3
|
+
interface SIWEMessageOptions extends Partial<SIWEFields> {
|
|
4
|
+
domain: string;
|
|
5
|
+
address: string;
|
|
6
|
+
uri: string;
|
|
7
|
+
chainId: number;
|
|
8
|
+
nonce: string;
|
|
9
|
+
statement?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function constructSIWEMessage(options: SIWEMessageOptions): string;
|
|
12
|
+
declare function parseSIWEMessage(message: string): SIWEFields;
|
|
13
|
+
declare function createBananaLinkSIWEMessage(sessionId: string, address: string, domain: string, chainId: number, nonce: string, statement?: string): string;
|
|
14
|
+
declare function isValidEthereumAddress(address: string): boolean;
|
|
15
|
+
declare function validateSIWETimestamp(timestamp: string, maxAge?: number): boolean;
|
|
16
|
+
declare function validateSIWEExpiration(expirationTime?: string): boolean;
|
|
17
|
+
declare function validateSIWENotBefore(notBefore?: string): boolean;
|
|
18
|
+
declare function validateSIWEMessage(fields: SIWEFields, options?: {
|
|
19
|
+
expectedDomain?: string;
|
|
20
|
+
expectedChainId?: number;
|
|
21
|
+
maxAge?: number;
|
|
22
|
+
}): {
|
|
23
|
+
valid: boolean;
|
|
24
|
+
errors: string[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { type SIWEMessageOptions, constructSIWEMessage, createBananaLinkSIWEMessage, isValidEthereumAddress, parseSIWEMessage, validateSIWEExpiration, validateSIWEMessage, validateSIWENotBefore, validateSIWETimestamp };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { a as SIWEFields } from './core-DMhuNfoz.js';
|
|
2
|
+
|
|
3
|
+
interface SIWEMessageOptions extends Partial<SIWEFields> {
|
|
4
|
+
domain: string;
|
|
5
|
+
address: string;
|
|
6
|
+
uri: string;
|
|
7
|
+
chainId: number;
|
|
8
|
+
nonce: string;
|
|
9
|
+
statement?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function constructSIWEMessage(options: SIWEMessageOptions): string;
|
|
12
|
+
declare function parseSIWEMessage(message: string): SIWEFields;
|
|
13
|
+
declare function createBananaLinkSIWEMessage(sessionId: string, address: string, domain: string, chainId: number, nonce: string, statement?: string): string;
|
|
14
|
+
declare function isValidEthereumAddress(address: string): boolean;
|
|
15
|
+
declare function validateSIWETimestamp(timestamp: string, maxAge?: number): boolean;
|
|
16
|
+
declare function validateSIWEExpiration(expirationTime?: string): boolean;
|
|
17
|
+
declare function validateSIWENotBefore(notBefore?: string): boolean;
|
|
18
|
+
declare function validateSIWEMessage(fields: SIWEFields, options?: {
|
|
19
|
+
expectedDomain?: string;
|
|
20
|
+
expectedChainId?: number;
|
|
21
|
+
maxAge?: number;
|
|
22
|
+
}): {
|
|
23
|
+
valid: boolean;
|
|
24
|
+
errors: string[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { type SIWEMessageOptions, constructSIWEMessage, createBananaLinkSIWEMessage, isValidEthereumAddress, parseSIWEMessage, validateSIWEExpiration, validateSIWEMessage, validateSIWENotBefore, validateSIWETimestamp };
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { __name } from './chunk-WCQVDF3K.js';
|
|
2
|
+
|
|
3
|
+
// src/utils/siwe.ts
|
|
4
|
+
function constructSIWEMessage(options) {
|
|
5
|
+
const {
|
|
6
|
+
domain,
|
|
7
|
+
address,
|
|
8
|
+
statement,
|
|
9
|
+
uri,
|
|
10
|
+
version = "1",
|
|
11
|
+
chainId,
|
|
12
|
+
nonce,
|
|
13
|
+
issuedAt = (/* @__PURE__ */ new Date()).toISOString(),
|
|
14
|
+
expirationTime,
|
|
15
|
+
notBefore,
|
|
16
|
+
requestId,
|
|
17
|
+
resources,
|
|
18
|
+
scheme
|
|
19
|
+
} = options;
|
|
20
|
+
if (!domain || !address || !uri || !chainId || !nonce) {
|
|
21
|
+
throw new Error("Missing required SIWE fields: domain, address, uri, chainId, nonce");
|
|
22
|
+
}
|
|
23
|
+
if (!isValidEthereumAddress(address)) {
|
|
24
|
+
throw new Error("Invalid Ethereum address format");
|
|
25
|
+
}
|
|
26
|
+
const parts = [];
|
|
27
|
+
const header = scheme ? `${scheme}://${domain}` : domain;
|
|
28
|
+
parts.push(`${header} wants you to sign in with your Ethereum account:`);
|
|
29
|
+
parts.push(address);
|
|
30
|
+
parts.push("");
|
|
31
|
+
if (statement) {
|
|
32
|
+
parts.push(statement);
|
|
33
|
+
parts.push("");
|
|
34
|
+
}
|
|
35
|
+
parts.push(`URI: ${uri}`);
|
|
36
|
+
parts.push(`Version: ${version}`);
|
|
37
|
+
parts.push(`Chain ID: ${chainId}`);
|
|
38
|
+
parts.push(`Nonce: ${nonce}`);
|
|
39
|
+
parts.push(`Issued At: ${issuedAt}`);
|
|
40
|
+
if (expirationTime) {
|
|
41
|
+
parts.push(`Expiration Time: ${expirationTime}`);
|
|
42
|
+
}
|
|
43
|
+
if (notBefore) {
|
|
44
|
+
parts.push(`Not Before: ${notBefore}`);
|
|
45
|
+
}
|
|
46
|
+
if (requestId) {
|
|
47
|
+
parts.push(`Request ID: ${requestId}`);
|
|
48
|
+
}
|
|
49
|
+
if (resources && resources.length > 0) {
|
|
50
|
+
parts.push("Resources:");
|
|
51
|
+
resources.forEach((resource) => {
|
|
52
|
+
parts.push(`- ${resource}`);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return parts.join("\n");
|
|
56
|
+
}
|
|
57
|
+
__name(constructSIWEMessage, "constructSIWEMessage");
|
|
58
|
+
function parseSIWEMessage(message) {
|
|
59
|
+
const lines = message.split("\n");
|
|
60
|
+
if (lines.length < 6) {
|
|
61
|
+
throw new Error("Invalid SIWE message format: insufficient lines");
|
|
62
|
+
}
|
|
63
|
+
const headerMatch = lines[0].match(/^((?<scheme>[^:]+):\/\/)?(?<domain>.+) wants you to sign in with your Ethereum account:$/);
|
|
64
|
+
if (!headerMatch) {
|
|
65
|
+
throw new Error("Invalid SIWE message header");
|
|
66
|
+
}
|
|
67
|
+
const fields = {
|
|
68
|
+
scheme: headerMatch.groups?.scheme,
|
|
69
|
+
domain: headerMatch.groups?.domain || "",
|
|
70
|
+
address: lines[1],
|
|
71
|
+
statement: void 0,
|
|
72
|
+
uri: "",
|
|
73
|
+
version: "1",
|
|
74
|
+
chainId: 0,
|
|
75
|
+
nonce: "",
|
|
76
|
+
issuedAt: ""
|
|
77
|
+
};
|
|
78
|
+
let fieldIndex = 2;
|
|
79
|
+
const statementLines = [];
|
|
80
|
+
while (fieldIndex < lines.length && !lines[fieldIndex].startsWith("URI: ")) {
|
|
81
|
+
if (lines[fieldIndex]) {
|
|
82
|
+
statementLines.push(lines[fieldIndex]);
|
|
83
|
+
}
|
|
84
|
+
fieldIndex++;
|
|
85
|
+
}
|
|
86
|
+
if (statementLines.length > 0) {
|
|
87
|
+
if (statementLines[statementLines.length - 1] === "") {
|
|
88
|
+
statementLines.pop();
|
|
89
|
+
}
|
|
90
|
+
fields.statement = statementLines.join("\n");
|
|
91
|
+
}
|
|
92
|
+
for (let i = fieldIndex; i < lines.length; i++) {
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
if (line.startsWith("URI: ")) {
|
|
95
|
+
fields.uri = line.substring(5);
|
|
96
|
+
} else if (line.startsWith("Version: ")) {
|
|
97
|
+
fields.version = line.substring(9);
|
|
98
|
+
} else if (line.startsWith("Chain ID: ")) {
|
|
99
|
+
fields.chainId = parseInt(line.substring(10), 10);
|
|
100
|
+
} else if (line.startsWith("Nonce: ")) {
|
|
101
|
+
fields.nonce = line.substring(7);
|
|
102
|
+
} else if (line.startsWith("Issued At: ")) {
|
|
103
|
+
fields.issuedAt = line.substring(11);
|
|
104
|
+
} else if (line.startsWith("Expiration Time: ")) {
|
|
105
|
+
fields.expirationTime = line.substring(17);
|
|
106
|
+
} else if (line.startsWith("Not Before: ")) {
|
|
107
|
+
fields.notBefore = line.substring(12);
|
|
108
|
+
} else if (line.startsWith("Request ID: ")) {
|
|
109
|
+
fields.requestId = line.substring(12);
|
|
110
|
+
} else if (line === "Resources:") {
|
|
111
|
+
fields.resources = [];
|
|
112
|
+
i++;
|
|
113
|
+
while (i < lines.length && lines[i].startsWith("- ")) {
|
|
114
|
+
fields.resources.push(lines[i].substring(2));
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
i--;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!fields.uri || !fields.nonce || !fields.chainId || !fields.issuedAt) {
|
|
121
|
+
throw new Error("Missing required SIWE fields in message");
|
|
122
|
+
}
|
|
123
|
+
return fields;
|
|
124
|
+
}
|
|
125
|
+
__name(parseSIWEMessage, "parseSIWEMessage");
|
|
126
|
+
function createBananaLinkSIWEMessage(sessionId, address, domain, chainId, nonce, statement) {
|
|
127
|
+
return constructSIWEMessage({
|
|
128
|
+
domain,
|
|
129
|
+
address,
|
|
130
|
+
statement: statement || `Sign in to ${domain}`,
|
|
131
|
+
uri: `bananalink://session/${sessionId}`,
|
|
132
|
+
chainId,
|
|
133
|
+
nonce,
|
|
134
|
+
version: "1",
|
|
135
|
+
issuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
__name(createBananaLinkSIWEMessage, "createBananaLinkSIWEMessage");
|
|
139
|
+
function isValidEthereumAddress(address) {
|
|
140
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
141
|
+
}
|
|
142
|
+
__name(isValidEthereumAddress, "isValidEthereumAddress");
|
|
143
|
+
function validateSIWETimestamp(timestamp, maxAge = 5 * 60 * 1e3) {
|
|
144
|
+
try {
|
|
145
|
+
const messageTime = new Date(timestamp).getTime();
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
const age = Math.abs(now - messageTime);
|
|
148
|
+
return age <= maxAge;
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
__name(validateSIWETimestamp, "validateSIWETimestamp");
|
|
154
|
+
function validateSIWEExpiration(expirationTime) {
|
|
155
|
+
if (expirationTime === "") {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
if (!expirationTime) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const expiry = new Date(expirationTime).getTime();
|
|
163
|
+
if (isNaN(expiry)) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return Date.now() < expiry;
|
|
167
|
+
} catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
__name(validateSIWEExpiration, "validateSIWEExpiration");
|
|
172
|
+
function validateSIWENotBefore(notBefore) {
|
|
173
|
+
if (notBefore === "") {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (!notBefore) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
const notBeforeTime = new Date(notBefore).getTime();
|
|
181
|
+
if (isNaN(notBeforeTime)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return Date.now() >= notBeforeTime;
|
|
185
|
+
} catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
__name(validateSIWENotBefore, "validateSIWENotBefore");
|
|
190
|
+
function validateSIWEMessage(fields, options = {}) {
|
|
191
|
+
const errors = [];
|
|
192
|
+
if (!fields.domain) errors.push("Missing domain");
|
|
193
|
+
if (!fields.address) errors.push("Missing address");
|
|
194
|
+
if (!fields.uri) errors.push("Missing URI");
|
|
195
|
+
if (!fields.chainId) errors.push("Missing chain ID");
|
|
196
|
+
if (!fields.nonce) errors.push("Missing nonce");
|
|
197
|
+
if (!fields.issuedAt) errors.push("Missing issued at timestamp");
|
|
198
|
+
if (fields.address && !isValidEthereumAddress(fields.address)) {
|
|
199
|
+
errors.push("Invalid Ethereum address format");
|
|
200
|
+
}
|
|
201
|
+
if (fields.version !== "1") {
|
|
202
|
+
errors.push(`Invalid SIWE version: ${fields.version} (must be 1)`);
|
|
203
|
+
}
|
|
204
|
+
if (options.expectedDomain && fields.domain !== options.expectedDomain) {
|
|
205
|
+
errors.push(`Domain mismatch: expected ${options.expectedDomain}, got ${fields.domain}`);
|
|
206
|
+
}
|
|
207
|
+
if (options.expectedChainId && fields.chainId !== options.expectedChainId) {
|
|
208
|
+
errors.push(`Chain ID mismatch: expected ${options.expectedChainId}, got ${fields.chainId}`);
|
|
209
|
+
}
|
|
210
|
+
if (!validateSIWETimestamp(fields.issuedAt, options.maxAge)) {
|
|
211
|
+
errors.push("Message timestamp is too old or invalid");
|
|
212
|
+
}
|
|
213
|
+
if (!validateSIWEExpiration(fields.expirationTime)) {
|
|
214
|
+
errors.push("Message has expired");
|
|
215
|
+
}
|
|
216
|
+
if (!validateSIWENotBefore(fields.notBefore)) {
|
|
217
|
+
errors.push("Message is not yet valid (not-before time not reached)");
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
valid: errors.length === 0,
|
|
221
|
+
errors
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
__name(validateSIWEMessage, "validateSIWEMessage");
|
|
225
|
+
|
|
226
|
+
export { constructSIWEMessage, createBananaLinkSIWEMessage, isValidEthereumAddress, parseSIWEMessage, validateSIWEExpiration, validateSIWEMessage, validateSIWENotBefore, validateSIWETimestamp };
|
|
227
|
+
//# sourceMappingURL=siwe-export.js.map
|
|
228
|
+
//# sourceMappingURL=siwe-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/siwe.ts"],"names":[],"mappings":"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","file":"siwe-export.js","sourcesContent":["/**\n * SIWE (Sign-In with Ethereum) message construction utilities\n * Implements ERC-4361 compliant message formatting\n */\n\nimport type { SIWEFields } from '../types/core';\n\n/**\n * Options for SIWE message construction\n */\nexport interface SIWEMessageOptions extends Partial<SIWEFields> {\n domain: string;\n address: string;\n uri: string;\n chainId: number;\n nonce: string;\n statement?: string;\n}\n\n/**\n * Construct a SIWE message according to ERC-4361 specification\n * @param options - SIWE message options\n * @returns Formatted SIWE message string\n */\nexport function constructSIWEMessage(options: SIWEMessageOptions): string {\n const {\n domain,\n address,\n statement,\n uri,\n version = '1',\n chainId,\n nonce,\n issuedAt = new Date().toISOString(),\n expirationTime,\n notBefore,\n requestId,\n resources,\n scheme,\n } = options;\n\n // Validate required fields\n if (!domain || !address || !uri || !chainId || !nonce) {\n throw new Error('Missing required SIWE fields: domain, address, uri, chainId, nonce');\n }\n\n // Validate address format\n if (!isValidEthereumAddress(address)) {\n throw new Error('Invalid Ethereum address format');\n }\n\n // Construct message parts according to ERC-4361\n const parts: string[] = [];\n\n // Header: domain wants you to sign in\n const header = scheme ? `${scheme}://${domain}` : domain;\n parts.push(`${header} wants you to sign in with your Ethereum account:`);\n parts.push(address);\n parts.push(''); // Empty line\n\n // Optional statement\n if (statement) {\n parts.push(statement);\n parts.push(''); // Empty line\n }\n\n // Required fields\n parts.push(`URI: ${uri}`);\n parts.push(`Version: ${version}`);\n parts.push(`Chain ID: ${chainId}`);\n parts.push(`Nonce: ${nonce}`);\n parts.push(`Issued At: ${issuedAt}`);\n\n // Optional fields\n if (expirationTime) {\n parts.push(`Expiration Time: ${expirationTime}`);\n }\n if (notBefore) {\n parts.push(`Not Before: ${notBefore}`);\n }\n if (requestId) {\n parts.push(`Request ID: ${requestId}`);\n }\n\n // Resources\n if (resources && resources.length > 0) {\n parts.push('Resources:');\n resources.forEach(resource => {\n parts.push(`- ${resource}`);\n });\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Parse a SIWE message string into its component fields\n * @param message - SIWE message string\n * @returns Parsed SIWE fields\n */\nexport function parseSIWEMessage(message: string): SIWEFields {\n const lines = message.split('\\n');\n\n if (lines.length < 6) {\n throw new Error('Invalid SIWE message format: insufficient lines');\n }\n\n // Parse header\n const headerMatch = lines[0].match(/^((?<scheme>[^:]+):\\/\\/)?(?<domain>.+) wants you to sign in with your Ethereum account:$/);\n if (!headerMatch) {\n throw new Error('Invalid SIWE message header');\n }\n\n const fields: SIWEFields = {\n scheme: headerMatch.groups?.scheme,\n domain: headerMatch.groups?.domain || '',\n address: lines[1],\n statement: undefined,\n uri: '',\n version: '1',\n chainId: 0,\n nonce: '',\n issuedAt: '',\n };\n\n // Find where the required fields start\n let fieldIndex = 2;\n\n // Check for optional statement (non-empty lines before URI)\n const statementLines: string[] = [];\n while (fieldIndex < lines.length && !lines[fieldIndex].startsWith('URI: ')) {\n if (lines[fieldIndex]) {\n statementLines.push(lines[fieldIndex]);\n }\n fieldIndex++;\n }\n\n if (statementLines.length > 0) {\n // Remove trailing empty line if present\n if (statementLines[statementLines.length - 1] === '') {\n statementLines.pop();\n }\n fields.statement = statementLines.join('\\n');\n }\n\n // Parse required and optional fields\n for (let i = fieldIndex; i < lines.length; i++) {\n const line = lines[i];\n\n if (line.startsWith('URI: ')) {\n fields.uri = line.substring(5);\n } else if (line.startsWith('Version: ')) {\n fields.version = line.substring(9);\n } else if (line.startsWith('Chain ID: ')) {\n fields.chainId = parseInt(line.substring(10), 10);\n } else if (line.startsWith('Nonce: ')) {\n fields.nonce = line.substring(7);\n } else if (line.startsWith('Issued At: ')) {\n fields.issuedAt = line.substring(11);\n } else if (line.startsWith('Expiration Time: ')) {\n fields.expirationTime = line.substring(17);\n } else if (line.startsWith('Not Before: ')) {\n fields.notBefore = line.substring(12);\n } else if (line.startsWith('Request ID: ')) {\n fields.requestId = line.substring(12);\n } else if (line === 'Resources:') {\n // Parse resources list\n fields.resources = [];\n i++;\n while (i < lines.length && lines[i].startsWith('- ')) {\n fields.resources.push(lines[i].substring(2));\n i++;\n }\n i--; // Adjust for the outer loop increment\n }\n }\n\n // Validate required fields\n if (!fields.uri || !fields.nonce || !fields.chainId || !fields.issuedAt) {\n throw new Error('Missing required SIWE fields in message');\n }\n\n return fields;\n}\n\n/**\n * Create a BananaLink-specific SIWE message for session authentication\n * @param sessionId - Session identifier\n * @param address - Ethereum address\n * @param domain - dApp domain\n * @param chainId - Chain ID\n * @param nonce - Session nonce\n * @param statement - Optional custom statement\n * @returns Formatted SIWE message\n */\nexport function createBananaLinkSIWEMessage(\n sessionId: string,\n address: string,\n domain: string,\n chainId: number,\n nonce: string,\n statement?: string\n): string {\n return constructSIWEMessage({\n domain,\n address,\n statement: statement || `Sign in to ${domain}`,\n uri: `bananalink://session/${sessionId}`,\n chainId,\n nonce,\n version: '1',\n issuedAt: new Date().toISOString(),\n });\n}\n\n/**\n * Validate an Ethereum address format\n * @param address - Address to validate\n * @returns True if valid Ethereum address\n */\nexport function isValidEthereumAddress(address: string): boolean {\n return /^0x[a-fA-F0-9]{40}$/.test(address);\n}\n\n// NOTE: Nonce generation moved to @bananalink-sdk/crypto package to avoid circular dependencies\n// SDKs should use crypto package to generate nonces before constructing SIWE messages\n\n/**\n * Validate SIWE message timestamp\n * @param timestamp - ISO 8601 timestamp\n * @param maxAge - Maximum age in milliseconds (default: 5 minutes)\n * @returns True if timestamp is within valid range\n */\nexport function validateSIWETimestamp(timestamp: string, maxAge: number = 5 * 60 * 1000): boolean {\n try {\n const messageTime = new Date(timestamp).getTime();\n const now = Date.now();\n const age = Math.abs(now - messageTime);\n return age <= maxAge;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate SIWE message expiration\n * @param expirationTime - Optional expiration timestamp\n * @returns True if message has not expired\n */\nexport function validateSIWEExpiration(expirationTime?: string): boolean {\n // Empty string should be treated as invalid (check before falsy check)\n if (expirationTime === '') {\n return false;\n }\n\n if (!expirationTime) {\n return true; // No expiration set (undefined or null)\n }\n\n try {\n const expiry = new Date(expirationTime).getTime();\n // Check for Invalid Date\n if (isNaN(expiry)) {\n return false;\n }\n return Date.now() < expiry;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate SIWE message not-before time\n * @param notBefore - Optional not-before timestamp\n * @returns True if current time is after not-before time\n */\nexport function validateSIWENotBefore(notBefore?: string): boolean {\n // Empty string should be treated as invalid (check before falsy check)\n if (notBefore === '') {\n return false;\n }\n\n if (!notBefore) {\n return true; // No not-before restriction (undefined or null)\n }\n\n try {\n const notBeforeTime = new Date(notBefore).getTime();\n // Check for Invalid Date\n if (isNaN(notBeforeTime)) {\n return false;\n }\n return Date.now() >= notBeforeTime;\n } catch {\n return false;\n }\n}\n\n/**\n * Comprehensive SIWE message validation\n * @param fields - SIWE fields to validate\n * @param options - Validation options\n * @returns Validation result with any errors\n */\nexport function validateSIWEMessage(\n fields: SIWEFields,\n options: {\n expectedDomain?: string;\n expectedChainId?: number;\n maxAge?: number;\n } = {}\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check required fields\n if (!fields.domain) errors.push('Missing domain');\n if (!fields.address) errors.push('Missing address');\n if (!fields.uri) errors.push('Missing URI');\n if (!fields.chainId) errors.push('Missing chain ID');\n if (!fields.nonce) errors.push('Missing nonce');\n if (!fields.issuedAt) errors.push('Missing issued at timestamp');\n\n // Validate address format\n if (fields.address && !isValidEthereumAddress(fields.address)) {\n errors.push('Invalid Ethereum address format');\n }\n\n // Validate version\n if (fields.version !== '1') {\n errors.push(`Invalid SIWE version: ${fields.version} (must be 1)`);\n }\n\n // Domain validation\n if (options.expectedDomain && fields.domain !== options.expectedDomain) {\n errors.push(`Domain mismatch: expected ${options.expectedDomain}, got ${fields.domain}`);\n }\n\n // Chain ID validation\n if (options.expectedChainId && fields.chainId !== options.expectedChainId) {\n errors.push(`Chain ID mismatch: expected ${options.expectedChainId}, got ${fields.chainId}`);\n }\n\n // Timestamp validation\n if (!validateSIWETimestamp(fields.issuedAt, options.maxAge)) {\n errors.push('Message timestamp is too old or invalid');\n }\n\n // Expiration validation\n if (!validateSIWEExpiration(fields.expirationTime)) {\n errors.push('Message has expired');\n }\n\n // Not-before validation\n if (!validateSIWENotBefore(fields.notBefore)) {\n errors.push('Message is not yet valid (not-before time not reached)');\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n"]}
|