@phantom/embedded-provider-core 0.1.1
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 +349 -0
- package/dist/index.d.mts +154 -0
- package/dist/index.d.ts +154 -0
- package/dist/index.js +542 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +512 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +63 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
EmbeddedProvider: () => EmbeddedProvider,
|
|
24
|
+
JWTAuth: () => JWTAuth,
|
|
25
|
+
generateSessionId: () => generateSessionId,
|
|
26
|
+
retryWithBackoff: () => retryWithBackoff
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(src_exports);
|
|
29
|
+
|
|
30
|
+
// src/embedded-provider.ts
|
|
31
|
+
var import_client = require("@phantom/client");
|
|
32
|
+
var import_api_key_stamper = require("@phantom/api-key-stamper");
|
|
33
|
+
var import_parsers = require("@phantom/parsers");
|
|
34
|
+
|
|
35
|
+
// src/auth/jwt-auth.ts
|
|
36
|
+
var JWTAuth = class {
|
|
37
|
+
async authenticate(options) {
|
|
38
|
+
if (!options.jwtToken || typeof options.jwtToken !== "string") {
|
|
39
|
+
throw new Error("Invalid JWT token: token must be a non-empty string");
|
|
40
|
+
}
|
|
41
|
+
const jwtParts = options.jwtToken.split(".");
|
|
42
|
+
if (jwtParts.length !== 3) {
|
|
43
|
+
throw new Error("Invalid JWT token format: token must have 3 parts separated by dots");
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const response = await fetch("/api/auth/jwt", {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: {
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
Authorization: `Bearer ${options.jwtToken}`
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
organizationId: options.organizationId,
|
|
54
|
+
parentOrganizationId: options.parentOrganizationId,
|
|
55
|
+
customAuthData: options.customAuthData
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
60
|
+
try {
|
|
61
|
+
const errorData = await response.json();
|
|
62
|
+
errorMessage = errorData.message || errorData.error || errorMessage;
|
|
63
|
+
} catch {
|
|
64
|
+
errorMessage = response.statusText || errorMessage;
|
|
65
|
+
}
|
|
66
|
+
switch (response.status) {
|
|
67
|
+
case 400:
|
|
68
|
+
throw new Error(`Invalid JWT authentication request: ${errorMessage}`);
|
|
69
|
+
case 401:
|
|
70
|
+
throw new Error(`JWT token is invalid or expired: ${errorMessage}`);
|
|
71
|
+
case 403:
|
|
72
|
+
throw new Error(`JWT authentication forbidden: ${errorMessage}`);
|
|
73
|
+
case 404:
|
|
74
|
+
throw new Error(`JWT authentication endpoint not found: ${errorMessage}`);
|
|
75
|
+
case 429:
|
|
76
|
+
throw new Error(`Too many JWT authentication requests: ${errorMessage}`);
|
|
77
|
+
case 500:
|
|
78
|
+
case 502:
|
|
79
|
+
case 503:
|
|
80
|
+
case 504:
|
|
81
|
+
throw new Error(`JWT authentication server error: ${errorMessage}`);
|
|
82
|
+
default:
|
|
83
|
+
throw new Error(`JWT authentication failed: ${errorMessage}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
let result;
|
|
87
|
+
try {
|
|
88
|
+
result = await response.json();
|
|
89
|
+
} catch (parseError) {
|
|
90
|
+
throw new Error("Invalid response from JWT authentication server: response is not valid JSON");
|
|
91
|
+
}
|
|
92
|
+
if (!result.walletId) {
|
|
93
|
+
throw new Error("Invalid JWT authentication response: missing walletId");
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
walletId: result.walletId,
|
|
97
|
+
provider: "jwt",
|
|
98
|
+
userInfo: result.userInfo || {}
|
|
99
|
+
};
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
102
|
+
throw new Error("JWT authentication failed: network error or invalid endpoint");
|
|
103
|
+
}
|
|
104
|
+
if (error instanceof Error) {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
throw new Error(`JWT authentication error: ${String(error)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/utils/session.ts
|
|
113
|
+
function generateSessionId() {
|
|
114
|
+
return "session_" + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + "_" + Date.now();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/utils/retry.ts
|
|
118
|
+
async function retryWithBackoff(operation, operationName, logger, maxRetries = 3, baseDelay = 1e3) {
|
|
119
|
+
let lastError;
|
|
120
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
121
|
+
try {
|
|
122
|
+
logger.log("EMBEDDED_PROVIDER", `Attempting ${operationName}`, {
|
|
123
|
+
attempt,
|
|
124
|
+
maxRetries
|
|
125
|
+
});
|
|
126
|
+
return await operation();
|
|
127
|
+
} catch (error) {
|
|
128
|
+
lastError = error;
|
|
129
|
+
logger.warn("EMBEDDED_PROVIDER", `${operationName} failed`, {
|
|
130
|
+
attempt,
|
|
131
|
+
maxRetries,
|
|
132
|
+
error: error instanceof Error ? error.message : String(error)
|
|
133
|
+
});
|
|
134
|
+
if (attempt === maxRetries) {
|
|
135
|
+
logger.error("EMBEDDED_PROVIDER", `${operationName} failed after ${maxRetries} attempts`, {
|
|
136
|
+
finalError: error instanceof Error ? error.message : String(error)
|
|
137
|
+
});
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
const delay = baseDelay * Math.pow(2, attempt - 1);
|
|
141
|
+
logger.log("EMBEDDED_PROVIDER", `Retrying ${operationName} in ${delay}ms`, {
|
|
142
|
+
attempt: attempt + 1,
|
|
143
|
+
delay
|
|
144
|
+
});
|
|
145
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
throw lastError;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/embedded-provider.ts
|
|
152
|
+
var EmbeddedProvider = class {
|
|
153
|
+
constructor(config, platform, logger) {
|
|
154
|
+
this.client = null;
|
|
155
|
+
this.walletId = null;
|
|
156
|
+
this.addresses = [];
|
|
157
|
+
this.logger = logger;
|
|
158
|
+
this.logger.log("EMBEDDED_PROVIDER", "Initializing EmbeddedProvider", { config });
|
|
159
|
+
this.config = config;
|
|
160
|
+
this.storage = platform.storage;
|
|
161
|
+
this.authProvider = platform.authProvider;
|
|
162
|
+
this.urlParamsAccessor = platform.urlParamsAccessor;
|
|
163
|
+
this.jwtAuth = new JWTAuth();
|
|
164
|
+
config.solanaProvider;
|
|
165
|
+
this.logger.info("EMBEDDED_PROVIDER", "EmbeddedProvider initialized");
|
|
166
|
+
}
|
|
167
|
+
async getAndFilterWalletAddresses(walletId) {
|
|
168
|
+
const addresses = await retryWithBackoff(
|
|
169
|
+
() => this.client.getWalletAddresses(walletId),
|
|
170
|
+
"getWalletAddresses",
|
|
171
|
+
this.logger
|
|
172
|
+
).catch(async (error) => {
|
|
173
|
+
this.logger.error("EMBEDDED_PROVIDER", "getWalletAddresses failed after retries, disconnecting", {
|
|
174
|
+
walletId,
|
|
175
|
+
error: error.message
|
|
176
|
+
});
|
|
177
|
+
await this.storage.clearSession();
|
|
178
|
+
this.client = null;
|
|
179
|
+
this.walletId = null;
|
|
180
|
+
this.addresses = [];
|
|
181
|
+
throw error;
|
|
182
|
+
});
|
|
183
|
+
return addresses.filter((addr) => this.config.addressTypes.some((type) => type === addr.addressType)).map((addr) => ({
|
|
184
|
+
addressType: addr.addressType,
|
|
185
|
+
address: addr.address
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
/*
|
|
189
|
+
* We use this method to make sure the session is not invalid, or there's a different session id in the url.
|
|
190
|
+
* If there's a different one, we delete the current session and start from scratch.
|
|
191
|
+
* This prevents issues where users have stale sessions or URL mismatches after redirects.
|
|
192
|
+
*/
|
|
193
|
+
async validateAndCleanSession(session) {
|
|
194
|
+
if (!session)
|
|
195
|
+
return null;
|
|
196
|
+
this.logger.log("EMBEDDED_PROVIDER", "Found existing session, validating", {
|
|
197
|
+
sessionId: session.sessionId,
|
|
198
|
+
status: session.status,
|
|
199
|
+
walletId: session.walletId
|
|
200
|
+
});
|
|
201
|
+
if (session.status !== "completed") {
|
|
202
|
+
const urlSessionId = this.urlParamsAccessor.getParam("session_id");
|
|
203
|
+
if (session.status === "pending" && !urlSessionId) {
|
|
204
|
+
this.logger.warn("EMBEDDED_PROVIDER", "Session mismatch detected - pending session without redirect context", {
|
|
205
|
+
sessionId: session.sessionId,
|
|
206
|
+
status: session.status
|
|
207
|
+
});
|
|
208
|
+
await this.storage.clearSession();
|
|
209
|
+
return null;
|
|
210
|
+
} else if (urlSessionId && urlSessionId !== session.sessionId) {
|
|
211
|
+
this.logger.warn("EMBEDDED_PROVIDER", "Session ID mismatch detected", {
|
|
212
|
+
storedSessionId: session.sessionId,
|
|
213
|
+
urlSessionId
|
|
214
|
+
});
|
|
215
|
+
await this.storage.clearSession();
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return session;
|
|
220
|
+
}
|
|
221
|
+
/*
|
|
222
|
+
* We use this method to validate authentication options before processing them.
|
|
223
|
+
* This ensures only supported auth providers are used and required tokens are present.
|
|
224
|
+
*/
|
|
225
|
+
validateAuthOptions(authOptions) {
|
|
226
|
+
if (!authOptions)
|
|
227
|
+
return;
|
|
228
|
+
if (authOptions.provider && !["google", "apple", "jwt"].includes(authOptions.provider)) {
|
|
229
|
+
throw new Error(`Invalid auth provider: ${authOptions.provider}. Must be "google", "apple", or "jwt"`);
|
|
230
|
+
}
|
|
231
|
+
if (authOptions.provider === "jwt" && !authOptions.jwtToken) {
|
|
232
|
+
throw new Error("JWT token is required when using JWT authentication");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/*
|
|
236
|
+
* We use this method to generate a new keypair and create an organization for new sessions.
|
|
237
|
+
* This is the first step when no existing session is found and we need to set up a new wallet.
|
|
238
|
+
*/
|
|
239
|
+
async createOrganizationAndKeypair() {
|
|
240
|
+
this.logger.log("EMBEDDED_PROVIDER", "Generating keypair");
|
|
241
|
+
const keypair = (0, import_client.generateKeyPair)();
|
|
242
|
+
this.logger.log("EMBEDDED_PROVIDER", "Keypair generated", { publicKey: keypair.publicKey });
|
|
243
|
+
this.logger.log("EMBEDDED_PROVIDER", "Creating temporary PhantomClient");
|
|
244
|
+
const stamper = new import_api_key_stamper.ApiKeyStamper({
|
|
245
|
+
apiSecretKey: keypair.secretKey
|
|
246
|
+
});
|
|
247
|
+
const tempClient = new import_client.PhantomClient(
|
|
248
|
+
{
|
|
249
|
+
apiBaseUrl: this.config.apiBaseUrl
|
|
250
|
+
},
|
|
251
|
+
stamper
|
|
252
|
+
);
|
|
253
|
+
const uid = Date.now();
|
|
254
|
+
const organizationName = `${this.config.organizationId}-${uid}`;
|
|
255
|
+
this.logger.log("EMBEDDED_PROVIDER", "Creating organization", { organizationName });
|
|
256
|
+
const { organizationId } = await tempClient.createOrganization(organizationName, keypair);
|
|
257
|
+
this.logger.info("EMBEDDED_PROVIDER", "Organization created", { organizationId });
|
|
258
|
+
return { organizationId, keypair };
|
|
259
|
+
}
|
|
260
|
+
async connect(authOptions) {
|
|
261
|
+
try {
|
|
262
|
+
this.logger.info("EMBEDDED_PROVIDER", "Starting embedded provider connect", {
|
|
263
|
+
authOptions: authOptions ? {
|
|
264
|
+
provider: authOptions.provider,
|
|
265
|
+
hasJwtToken: !!authOptions.jwtToken
|
|
266
|
+
} : void 0
|
|
267
|
+
});
|
|
268
|
+
this.logger.log("EMBEDDED_PROVIDER", "Getting existing session");
|
|
269
|
+
let session = await this.storage.getSession();
|
|
270
|
+
session = await this.validateAndCleanSession(session);
|
|
271
|
+
this.logger.log("EMBEDDED_PROVIDER", "Checking for redirect resume");
|
|
272
|
+
if (this.authProvider.resumeAuthFromRedirect) {
|
|
273
|
+
const authResult = this.authProvider.resumeAuthFromRedirect();
|
|
274
|
+
if (authResult) {
|
|
275
|
+
this.logger.info("EMBEDDED_PROVIDER", "Resuming from redirect", {
|
|
276
|
+
walletId: authResult.walletId,
|
|
277
|
+
provider: authResult.provider
|
|
278
|
+
});
|
|
279
|
+
return this.completeAuthConnection(authResult);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
this.validateAuthOptions(authOptions);
|
|
283
|
+
if (!session) {
|
|
284
|
+
this.logger.info("EMBEDDED_PROVIDER", "No existing session, creating new one");
|
|
285
|
+
const { organizationId, keypair } = await this.createOrganizationAndKeypair();
|
|
286
|
+
session = await this.handleAuthFlow(organizationId, keypair, authOptions);
|
|
287
|
+
}
|
|
288
|
+
if (!session) {
|
|
289
|
+
return {
|
|
290
|
+
addresses: [],
|
|
291
|
+
status: "pending"
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (!authOptions || authOptions.provider === "jwt" || this.config.embeddedWalletType === "app-wallet") {
|
|
295
|
+
session.lastUsed = Date.now();
|
|
296
|
+
await this.storage.saveSession(session);
|
|
297
|
+
}
|
|
298
|
+
await this.initializeClientFromSession(session);
|
|
299
|
+
return {
|
|
300
|
+
walletId: this.walletId,
|
|
301
|
+
addresses: this.addresses,
|
|
302
|
+
status: "completed"
|
|
303
|
+
};
|
|
304
|
+
} catch (error) {
|
|
305
|
+
this.logger.error("EMBEDDED_PROVIDER", "Connect failed with error", {
|
|
306
|
+
error: error instanceof Error ? {
|
|
307
|
+
name: error.name,
|
|
308
|
+
message: error.message,
|
|
309
|
+
stack: error.stack
|
|
310
|
+
} : error
|
|
311
|
+
});
|
|
312
|
+
if (error instanceof Error) {
|
|
313
|
+
if (error.message.includes("IndexedDB") || error.message.includes("storage")) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
"Storage error: Unable to access browser storage. Please ensure storage is available and try again."
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
if (error.message.includes("network") || error.message.includes("fetch")) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
"Network error: Unable to connect to authentication server. Please check your internet connection and try again."
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
if (error.message.includes("JWT") || error.message.includes("jwt")) {
|
|
324
|
+
throw new Error(`JWT Authentication error: ${error.message}`);
|
|
325
|
+
}
|
|
326
|
+
if (error.message.includes("Authentication") || error.message.includes("auth")) {
|
|
327
|
+
throw new Error(`Authentication error: ${error.message}`);
|
|
328
|
+
}
|
|
329
|
+
if (error.message.includes("organization") || error.message.includes("wallet")) {
|
|
330
|
+
throw new Error(`Wallet creation error: ${error.message}`);
|
|
331
|
+
}
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
throw new Error(`Embedded wallet connection failed: ${String(error)}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async disconnect() {
|
|
338
|
+
await this.storage.clearSession();
|
|
339
|
+
this.client = null;
|
|
340
|
+
this.walletId = null;
|
|
341
|
+
this.addresses = [];
|
|
342
|
+
}
|
|
343
|
+
async signMessage(params) {
|
|
344
|
+
if (!this.client || !this.walletId) {
|
|
345
|
+
throw new Error("Not connected");
|
|
346
|
+
}
|
|
347
|
+
const parsedMessage = (0, import_parsers.parseMessage)(params.message);
|
|
348
|
+
return await this.client.signMessage({
|
|
349
|
+
walletId: this.walletId,
|
|
350
|
+
message: parsedMessage.base64url,
|
|
351
|
+
networkId: params.networkId
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
async signAndSendTransaction(params) {
|
|
355
|
+
if (!this.client || !this.walletId) {
|
|
356
|
+
throw new Error("Not connected");
|
|
357
|
+
}
|
|
358
|
+
const parsedTransaction = await (0, import_parsers.parseTransaction)(params.transaction, params.networkId);
|
|
359
|
+
return await this.client.signAndSendTransaction({
|
|
360
|
+
walletId: this.walletId,
|
|
361
|
+
transaction: parsedTransaction.base64url,
|
|
362
|
+
networkId: params.networkId
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
getAddresses() {
|
|
366
|
+
return this.addresses;
|
|
367
|
+
}
|
|
368
|
+
isConnected() {
|
|
369
|
+
return this.client !== null && this.walletId !== null;
|
|
370
|
+
}
|
|
371
|
+
/*
|
|
372
|
+
* We use this method to route between different authentication flows based on wallet type and auth options.
|
|
373
|
+
* It handles app-wallet creation directly or routes to JWT/redirect authentication for user-wallets.
|
|
374
|
+
* Returns null for redirect flows since they don't complete synchronously.
|
|
375
|
+
*/
|
|
376
|
+
async handleAuthFlow(organizationId, keypair, authOptions) {
|
|
377
|
+
if (this.config.embeddedWalletType === "user-wallet") {
|
|
378
|
+
this.logger.info("EMBEDDED_PROVIDER", "Creating user-wallet, routing authentication", {
|
|
379
|
+
authProvider: authOptions?.provider || "phantom-connect"
|
|
380
|
+
});
|
|
381
|
+
if (authOptions?.provider === "jwt") {
|
|
382
|
+
return await this.handleJWTAuth(organizationId, keypair, authOptions);
|
|
383
|
+
} else {
|
|
384
|
+
await this.handleRedirectAuth(organizationId, keypair, authOptions);
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
} else {
|
|
388
|
+
const tempClient = new import_client.PhantomClient(
|
|
389
|
+
{
|
|
390
|
+
apiBaseUrl: this.config.apiBaseUrl
|
|
391
|
+
},
|
|
392
|
+
new import_api_key_stamper.ApiKeyStamper({ apiSecretKey: keypair.secretKey })
|
|
393
|
+
);
|
|
394
|
+
const wallet = await tempClient.createWallet(`Wallet ${Date.now()}`);
|
|
395
|
+
const walletId = wallet.walletId;
|
|
396
|
+
const now = Date.now();
|
|
397
|
+
const session = {
|
|
398
|
+
sessionId: generateSessionId(),
|
|
399
|
+
walletId,
|
|
400
|
+
organizationId: this.config.organizationId,
|
|
401
|
+
keypair,
|
|
402
|
+
authProvider: "app-wallet",
|
|
403
|
+
userInfo: { embeddedWalletType: this.config.embeddedWalletType },
|
|
404
|
+
status: "completed",
|
|
405
|
+
createdAt: now,
|
|
406
|
+
lastUsed: now
|
|
407
|
+
};
|
|
408
|
+
await this.storage.saveSession(session);
|
|
409
|
+
return session;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/*
|
|
413
|
+
* We use this method to handle JWT-based authentication for user-wallets.
|
|
414
|
+
* It authenticates using the provided JWT token and creates a completed session.
|
|
415
|
+
*/
|
|
416
|
+
async handleJWTAuth(organizationId, keypair, authOptions) {
|
|
417
|
+
this.logger.info("EMBEDDED_PROVIDER", "Using JWT authentication flow");
|
|
418
|
+
if (!authOptions.jwtToken) {
|
|
419
|
+
this.logger.error("EMBEDDED_PROVIDER", "JWT token missing for JWT authentication");
|
|
420
|
+
throw new Error("JWT token is required for JWT authentication");
|
|
421
|
+
}
|
|
422
|
+
this.logger.log("EMBEDDED_PROVIDER", "Starting JWT authentication");
|
|
423
|
+
const authResult = await this.jwtAuth.authenticate({
|
|
424
|
+
organizationId,
|
|
425
|
+
parentOrganizationId: this.config.organizationId,
|
|
426
|
+
jwtToken: authOptions.jwtToken,
|
|
427
|
+
customAuthData: authOptions.customAuthData
|
|
428
|
+
});
|
|
429
|
+
const walletId = authResult.walletId;
|
|
430
|
+
this.logger.info("EMBEDDED_PROVIDER", "JWT authentication completed", { walletId });
|
|
431
|
+
const now = Date.now();
|
|
432
|
+
const session = {
|
|
433
|
+
sessionId: generateSessionId(),
|
|
434
|
+
walletId,
|
|
435
|
+
organizationId: this.config.organizationId,
|
|
436
|
+
keypair,
|
|
437
|
+
authProvider: authResult.provider,
|
|
438
|
+
userInfo: authResult.userInfo,
|
|
439
|
+
status: "completed",
|
|
440
|
+
createdAt: now,
|
|
441
|
+
lastUsed: now
|
|
442
|
+
};
|
|
443
|
+
this.logger.log("EMBEDDED_PROVIDER", "Saving JWT session");
|
|
444
|
+
await this.storage.saveSession(session);
|
|
445
|
+
return session;
|
|
446
|
+
}
|
|
447
|
+
/*
|
|
448
|
+
* We use this method to handle redirect-based authentication (Google/Apple OAuth).
|
|
449
|
+
* It saves a temporary session before redirecting to prevent losing state during the redirect flow.
|
|
450
|
+
* Session timestamp is updated before redirect to prevent race conditions.
|
|
451
|
+
*/
|
|
452
|
+
async handleRedirectAuth(organizationId, keypair, authOptions) {
|
|
453
|
+
this.logger.info("EMBEDDED_PROVIDER", "Using Phantom Connect authentication flow (redirect-based)", {
|
|
454
|
+
provider: authOptions?.provider,
|
|
455
|
+
hasRedirectUrl: !!this.config.authOptions?.redirectUrl,
|
|
456
|
+
authUrl: this.config.authOptions?.authUrl
|
|
457
|
+
});
|
|
458
|
+
const now = Date.now();
|
|
459
|
+
const sessionId = generateSessionId();
|
|
460
|
+
const tempSession = {
|
|
461
|
+
sessionId,
|
|
462
|
+
walletId: `temp-${now}`,
|
|
463
|
+
// Temporary ID, will be updated after redirect
|
|
464
|
+
organizationId,
|
|
465
|
+
keypair,
|
|
466
|
+
authProvider: "phantom-connect",
|
|
467
|
+
userInfo: { provider: authOptions?.provider },
|
|
468
|
+
status: "pending",
|
|
469
|
+
createdAt: now,
|
|
470
|
+
lastUsed: now
|
|
471
|
+
};
|
|
472
|
+
this.logger.log("EMBEDDED_PROVIDER", "Saving temporary session before redirect", {
|
|
473
|
+
sessionId: tempSession.sessionId,
|
|
474
|
+
tempWalletId: tempSession.walletId
|
|
475
|
+
});
|
|
476
|
+
tempSession.lastUsed = Date.now();
|
|
477
|
+
await this.storage.saveSession(tempSession);
|
|
478
|
+
this.logger.info("EMBEDDED_PROVIDER", "Starting Phantom Connect redirect", {
|
|
479
|
+
organizationId,
|
|
480
|
+
parentOrganizationId: this.config.organizationId,
|
|
481
|
+
provider: authOptions?.provider,
|
|
482
|
+
authUrl: this.config.authOptions?.authUrl
|
|
483
|
+
});
|
|
484
|
+
await this.authProvider.authenticate({
|
|
485
|
+
organizationId,
|
|
486
|
+
parentOrganizationId: this.config.organizationId,
|
|
487
|
+
provider: authOptions?.provider,
|
|
488
|
+
redirectUrl: this.config.authOptions?.redirectUrl,
|
|
489
|
+
customAuthData: authOptions?.customAuthData,
|
|
490
|
+
authUrl: this.config.authOptions?.authUrl,
|
|
491
|
+
sessionId
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
async completeAuthConnection(authResult) {
|
|
495
|
+
const session = await this.storage.getSession();
|
|
496
|
+
if (!session) {
|
|
497
|
+
throw new Error("No session found after redirect - session may have expired");
|
|
498
|
+
}
|
|
499
|
+
session.walletId = authResult.walletId;
|
|
500
|
+
session.authProvider = authResult.provider || session.authProvider;
|
|
501
|
+
session.userInfo = { ...session.userInfo, ...authResult.userInfo };
|
|
502
|
+
session.status = "completed";
|
|
503
|
+
session.lastUsed = Date.now();
|
|
504
|
+
await this.storage.saveSession(session);
|
|
505
|
+
await this.initializeClientFromSession(session);
|
|
506
|
+
return {
|
|
507
|
+
walletId: this.walletId,
|
|
508
|
+
addresses: this.addresses,
|
|
509
|
+
status: "completed"
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
/*
|
|
513
|
+
* We use this method to initialize the PhantomClient and fetch wallet addresses from a completed session.
|
|
514
|
+
* This is the final step that sets up the provider's client state and retrieves available addresses.
|
|
515
|
+
*/
|
|
516
|
+
async initializeClientFromSession(session) {
|
|
517
|
+
this.logger.log("EMBEDDED_PROVIDER", "Initializing PhantomClient from session", {
|
|
518
|
+
organizationId: session.organizationId,
|
|
519
|
+
walletId: session.walletId
|
|
520
|
+
});
|
|
521
|
+
const stamper = new import_api_key_stamper.ApiKeyStamper({
|
|
522
|
+
apiSecretKey: session.keypair.secretKey
|
|
523
|
+
});
|
|
524
|
+
this.client = new import_client.PhantomClient(
|
|
525
|
+
{
|
|
526
|
+
apiBaseUrl: this.config.apiBaseUrl,
|
|
527
|
+
organizationId: session.organizationId
|
|
528
|
+
},
|
|
529
|
+
stamper
|
|
530
|
+
);
|
|
531
|
+
this.walletId = session.walletId;
|
|
532
|
+
this.addresses = await this.getAndFilterWalletAddresses(session.walletId);
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
536
|
+
0 && (module.exports = {
|
|
537
|
+
EmbeddedProvider,
|
|
538
|
+
JWTAuth,
|
|
539
|
+
generateSessionId,
|
|
540
|
+
retryWithBackoff
|
|
541
|
+
});
|
|
542
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/embedded-provider.ts","../src/auth/jwt-auth.ts","../src/utils/session.ts","../src/utils/retry.ts"],"sourcesContent":["export * from \"./interfaces\";\nexport * from \"./types\";\nexport * from \"./embedded-provider\";\nexport * from \"./auth/jwt-auth\";\nexport * from \"./utils/session\";\nexport * from \"./utils/retry\";\n","import { PhantomClient, generateKeyPair } from \"@phantom/client\";\nimport type { AddressType } from \"@phantom/client\";\nimport { ApiKeyStamper } from \"@phantom/api-key-stamper\";\nimport { parseMessage, parseTransaction } from \"@phantom/parsers\";\n\nimport type {\n PlatformAdapter,\n Session,\n AuthResult,\n DebugLogger,\n EmbeddedStorage,\n AuthProvider,\n URLParamsAccessor,\n} from \"./interfaces\";\nimport type {\n EmbeddedProviderConfig,\n ConnectResult,\n SignMessageParams,\n SignAndSendTransactionParams,\n SignedTransaction,\n WalletAddress,\n AuthOptions,\n} from \"./types\";\nimport { JWTAuth } from \"./auth/jwt-auth\";\nimport { generateSessionId } from \"./utils/session\";\nimport { retryWithBackoff } from \"./utils/retry\";\n\nexport class EmbeddedProvider {\n private config: EmbeddedProviderConfig;\n private storage: EmbeddedStorage;\n private authProvider: AuthProvider;\n private urlParamsAccessor: URLParamsAccessor;\n private logger: DebugLogger;\n private client: PhantomClient | null = null;\n private walletId: string | null = null;\n private addresses: WalletAddress[] = [];\n private jwtAuth: JWTAuth;\n\n constructor(config: EmbeddedProviderConfig, platform: PlatformAdapter, logger: DebugLogger) {\n this.logger = logger;\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Initializing EmbeddedProvider\", { config });\n\n this.config = config;\n this.storage = platform.storage;\n this.authProvider = platform.authProvider;\n this.urlParamsAccessor = platform.urlParamsAccessor;\n this.jwtAuth = new JWTAuth();\n\n // Store solana provider config (unused for now)\n config.solanaProvider;\n this.logger.info(\"EMBEDDED_PROVIDER\", \"EmbeddedProvider initialized\");\n }\n\n private async getAndFilterWalletAddresses(walletId: string): Promise<WalletAddress[]> {\n // Get wallet addresses with retry and auto-disconnect on failure\n const addresses = await retryWithBackoff(\n () => this.client!.getWalletAddresses(walletId),\n \"getWalletAddresses\",\n this.logger,\n ).catch(async error => {\n this.logger.error(\"EMBEDDED_PROVIDER\", \"getWalletAddresses failed after retries, disconnecting\", {\n walletId,\n error: error.message,\n });\n // Clear the session if getWalletAddresses fails after retries\n await this.storage.clearSession();\n this.client = null;\n this.walletId = null;\n this.addresses = [];\n throw error;\n });\n\n // Filter by enabled address types and return formatted addresses\n return addresses\n .filter(addr => this.config.addressTypes.some(type => type === addr.addressType))\n .map(addr => ({\n addressType: addr.addressType as AddressType,\n address: addr.address,\n }));\n }\n\n /*\n * We use this method to make sure the session is not invalid, or there's a different session id in the url.\n * If there's a different one, we delete the current session and start from scratch.\n * This prevents issues where users have stale sessions or URL mismatches after redirects.\n */\n private async validateAndCleanSession(session: Session | null): Promise<Session | null> {\n if (!session) return null;\n\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Found existing session, validating\", {\n sessionId: session.sessionId,\n status: session.status,\n walletId: session.walletId,\n });\n\n // If session is not completed, check if we're in the right context\n if (session.status !== \"completed\") {\n const urlSessionId = this.urlParamsAccessor.getParam(\"session_id\");\n\n // If we have a pending session but no sessionId in URL, this is a mismatch\n if (session.status === \"pending\" && !urlSessionId) {\n this.logger.warn(\"EMBEDDED_PROVIDER\", \"Session mismatch detected - pending session without redirect context\", {\n sessionId: session.sessionId,\n status: session.status,\n });\n // Clear the invalid session and start fresh\n await this.storage.clearSession();\n return null;\n }\n // If sessionId in URL doesn't match stored session, clear invalid session\n else if (urlSessionId && urlSessionId !== session.sessionId) {\n this.logger.warn(\"EMBEDDED_PROVIDER\", \"Session ID mismatch detected\", {\n storedSessionId: session.sessionId,\n urlSessionId: urlSessionId,\n });\n await this.storage.clearSession();\n return null;\n }\n }\n\n return session;\n }\n\n /*\n * We use this method to validate authentication options before processing them.\n * This ensures only supported auth providers are used and required tokens are present.\n */\n private validateAuthOptions(authOptions?: AuthOptions): void {\n if (!authOptions) return;\n\n if (authOptions.provider && ![\"google\", \"apple\", \"jwt\"].includes(authOptions.provider)) {\n throw new Error(`Invalid auth provider: ${authOptions.provider}. Must be \"google\", \"apple\", or \"jwt\"`);\n }\n\n if (authOptions.provider === \"jwt\" && !authOptions.jwtToken) {\n throw new Error(\"JWT token is required when using JWT authentication\");\n }\n }\n\n /*\n * We use this method to generate a new keypair and create an organization for new sessions.\n * This is the first step when no existing session is found and we need to set up a new wallet.\n */\n private async createOrganizationAndKeypair(): Promise<{ organizationId: string; keypair: any }> {\n // Generate keypair using PhantomClient\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Generating keypair\");\n const keypair = generateKeyPair();\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Keypair generated\", { publicKey: keypair.publicKey });\n\n // Create a temporary client with the keypair\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Creating temporary PhantomClient\");\n const stamper = new ApiKeyStamper({\n apiSecretKey: keypair.secretKey,\n });\n\n const tempClient = new PhantomClient(\n {\n apiBaseUrl: this.config.apiBaseUrl,\n },\n stamper,\n );\n\n // Create an organization\n // organization name is a combination of this organizationId and this userId, which will be a unique identifier\n const uid = Date.now(); // for now\n const organizationName = `${this.config.organizationId}-${uid}`;\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Creating organization\", { organizationName });\n const { organizationId } = await tempClient.createOrganization(organizationName, keypair);\n this.logger.info(\"EMBEDDED_PROVIDER\", \"Organization created\", { organizationId });\n\n return { organizationId, keypair };\n }\n\n async connect(authOptions?: AuthOptions): Promise<ConnectResult> {\n try {\n this.logger.info(\"EMBEDDED_PROVIDER\", \"Starting embedded provider connect\", {\n authOptions: authOptions\n ? {\n provider: authOptions.provider,\n hasJwtToken: !!authOptions.jwtToken,\n }\n : undefined,\n });\n\n // Get and validate existing session\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Getting existing session\");\n let session = await this.storage.getSession();\n session = await this.validateAndCleanSession(session);\n\n // First, check if we're resuming from a redirect\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Checking for redirect resume\");\n if (this.authProvider.resumeAuthFromRedirect) {\n const authResult = this.authProvider.resumeAuthFromRedirect();\n if (authResult) {\n this.logger.info(\"EMBEDDED_PROVIDER\", \"Resuming from redirect\", {\n walletId: authResult.walletId,\n provider: authResult.provider,\n });\n return this.completeAuthConnection(authResult);\n }\n }\n\n // Validate auth options\n this.validateAuthOptions(authOptions);\n\n // If no session exists, create new one\n if (!session) {\n this.logger.info(\"EMBEDDED_PROVIDER\", \"No existing session, creating new one\");\n const { organizationId, keypair } = await this.createOrganizationAndKeypair();\n session = await this.handleAuthFlow(organizationId, keypair, authOptions);\n }\n\n // If session is null here, it means we're doing a redirect\n if (!session) {\n // This should not return anything as redirect is happening\n return {\n addresses: [],\n status: \"pending\",\n } as ConnectResult;\n }\n\n // Update session last used timestamp (only for non-redirect flows)\n // For redirect flows, timestamp is updated before redirect to prevent race condition\n if (!authOptions || authOptions.provider === \"jwt\" || this.config.embeddedWalletType === \"app-wallet\") {\n session.lastUsed = Date.now();\n await this.storage.saveSession(session);\n }\n\n // Initialize client and get addresses\n await this.initializeClientFromSession(session);\n\n return {\n walletId: this.walletId!,\n addresses: this.addresses,\n status: \"completed\",\n };\n } catch (error) {\n // Log the full error details for debugging\n this.logger.error(\"EMBEDDED_PROVIDER\", \"Connect failed with error\", {\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : error,\n });\n\n // Enhanced error handling with specific error types\n if (error instanceof Error) {\n // Check for specific error types and provide better error messages\n if (error.message.includes(\"IndexedDB\") || error.message.includes(\"storage\")) {\n throw new Error(\n \"Storage error: Unable to access browser storage. Please ensure storage is available and try again.\",\n );\n }\n\n if (error.message.includes(\"network\") || error.message.includes(\"fetch\")) {\n throw new Error(\n \"Network error: Unable to connect to authentication server. Please check your internet connection and try again.\",\n );\n }\n\n if (error.message.includes(\"JWT\") || error.message.includes(\"jwt\")) {\n throw new Error(`JWT Authentication error: ${error.message}`);\n }\n\n if (error.message.includes(\"Authentication\") || error.message.includes(\"auth\")) {\n throw new Error(`Authentication error: ${error.message}`);\n }\n\n if (error.message.includes(\"organization\") || error.message.includes(\"wallet\")) {\n throw new Error(`Wallet creation error: ${error.message}`);\n }\n\n // Re-throw the original error if it's already well-formatted\n throw error;\n }\n\n // Handle unknown error types\n throw new Error(`Embedded wallet connection failed: ${String(error)}`);\n }\n }\n\n async disconnect(): Promise<void> {\n await this.storage.clearSession();\n this.client = null;\n this.walletId = null;\n this.addresses = [];\n }\n\n async signMessage(params: SignMessageParams): Promise<string> {\n if (!this.client || !this.walletId) {\n throw new Error(\"Not connected\");\n }\n\n // Parse message to base64url format for client\n const parsedMessage = parseMessage(params.message);\n\n return await this.client.signMessage({\n walletId: this.walletId,\n message: parsedMessage.base64url,\n networkId: params.networkId,\n });\n }\n\n async signAndSendTransaction(params: SignAndSendTransactionParams): Promise<SignedTransaction> {\n if (!this.client || !this.walletId) {\n throw new Error(\"Not connected\");\n }\n\n // Parse transaction to base64url format for client based on network\n const parsedTransaction = await parseTransaction(params.transaction, params.networkId);\n\n return await this.client.signAndSendTransaction({\n walletId: this.walletId,\n transaction: parsedTransaction.base64url,\n networkId: params.networkId,\n });\n }\n\n getAddresses(): WalletAddress[] {\n return this.addresses;\n }\n\n isConnected(): boolean {\n return this.client !== null && this.walletId !== null;\n }\n\n /*\n * We use this method to route between different authentication flows based on wallet type and auth options.\n * It handles app-wallet creation directly or routes to JWT/redirect authentication for user-wallets.\n * Returns null for redirect flows since they don't complete synchronously.\n */\n private async handleAuthFlow(\n organizationId: string,\n keypair: any,\n authOptions?: AuthOptions,\n ): Promise<Session | null> {\n if (this.config.embeddedWalletType === \"user-wallet\") {\n this.logger.info(\"EMBEDDED_PROVIDER\", \"Creating user-wallet, routing authentication\", {\n authProvider: authOptions?.provider || \"phantom-connect\",\n });\n\n // Route to appropriate authentication flow based on authOptions\n if (authOptions?.provider === \"jwt\") {\n return await this.handleJWTAuth(organizationId, keypair, authOptions);\n } else {\n // This will redirect, so we don't return a session\n await this.handleRedirectAuth(organizationId, keypair, authOptions);\n return null;\n }\n } else {\n // Create app-wallet directly\n const tempClient = new PhantomClient(\n {\n apiBaseUrl: this.config.apiBaseUrl,\n },\n new ApiKeyStamper({ apiSecretKey: keypair.secretKey }),\n );\n\n const wallet = await tempClient.createWallet(`Wallet ${Date.now()}`);\n const walletId = wallet.walletId;\n\n // Save session with app-wallet info\n const now = Date.now();\n const session = {\n sessionId: generateSessionId(),\n walletId: walletId,\n organizationId: this.config.organizationId,\n keypair,\n authProvider: \"app-wallet\",\n userInfo: { embeddedWalletType: this.config.embeddedWalletType },\n status: \"completed\" as const,\n createdAt: now,\n lastUsed: now,\n };\n await this.storage.saveSession(session);\n return session;\n }\n }\n\n /*\n * We use this method to handle JWT-based authentication for user-wallets.\n * It authenticates using the provided JWT token and creates a completed session.\n */\n private async handleJWTAuth(organizationId: string, keypair: any, authOptions: AuthOptions): Promise<Session> {\n this.logger.info(\"EMBEDDED_PROVIDER\", \"Using JWT authentication flow\");\n\n // Use JWT authentication flow\n if (!authOptions.jwtToken) {\n this.logger.error(\"EMBEDDED_PROVIDER\", \"JWT token missing for JWT authentication\");\n throw new Error(\"JWT token is required for JWT authentication\");\n }\n\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Starting JWT authentication\");\n const authResult = await this.jwtAuth.authenticate({\n organizationId: organizationId,\n parentOrganizationId: this.config.organizationId,\n jwtToken: authOptions.jwtToken,\n customAuthData: authOptions.customAuthData,\n });\n const walletId = authResult.walletId;\n this.logger.info(\"EMBEDDED_PROVIDER\", \"JWT authentication completed\", { walletId });\n\n // Save session with auth info\n const now = Date.now();\n const session = {\n sessionId: generateSessionId(),\n walletId: walletId,\n organizationId: this.config.organizationId,\n keypair,\n authProvider: authResult.provider,\n userInfo: authResult.userInfo,\n status: \"completed\" as const,\n createdAt: now,\n lastUsed: now,\n };\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Saving JWT session\");\n await this.storage.saveSession(session);\n return session;\n }\n\n /*\n * We use this method to handle redirect-based authentication (Google/Apple OAuth).\n * It saves a temporary session before redirecting to prevent losing state during the redirect flow.\n * Session timestamp is updated before redirect to prevent race conditions.\n */\n private async handleRedirectAuth(organizationId: string, keypair: any, authOptions?: AuthOptions): Promise<void> {\n this.logger.info(\"EMBEDDED_PROVIDER\", \"Using Phantom Connect authentication flow (redirect-based)\", {\n provider: authOptions?.provider,\n hasRedirectUrl: !!this.config.authOptions?.redirectUrl,\n authUrl: this.config.authOptions?.authUrl,\n });\n\n // Use Phantom Connect authentication flow (redirect-based)\n // Store session before redirect so we can restore it after redirect\n const now = Date.now();\n const sessionId = generateSessionId();\n const tempSession = {\n sessionId: sessionId,\n walletId: `temp-${now}`, // Temporary ID, will be updated after redirect\n organizationId: organizationId,\n keypair,\n authProvider: \"phantom-connect\",\n userInfo: { provider: authOptions?.provider },\n status: \"pending\" as const,\n createdAt: now,\n lastUsed: now,\n };\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Saving temporary session before redirect\", {\n sessionId: tempSession.sessionId,\n tempWalletId: tempSession.walletId,\n });\n\n // Update session timestamp before redirect (prevents race condition)\n tempSession.lastUsed = Date.now();\n await this.storage.saveSession(tempSession);\n\n this.logger.info(\"EMBEDDED_PROVIDER\", \"Starting Phantom Connect redirect\", {\n organizationId,\n parentOrganizationId: this.config.organizationId,\n provider: authOptions?.provider,\n authUrl: this.config.authOptions?.authUrl,\n });\n\n // Start the authentication flow (this will redirect the user)\n await this.authProvider.authenticate({\n organizationId: organizationId,\n parentOrganizationId: this.config.organizationId,\n provider: authOptions?.provider as \"google\" | \"apple\" | undefined,\n redirectUrl: this.config.authOptions?.redirectUrl,\n customAuthData: authOptions?.customAuthData,\n authUrl: this.config.authOptions?.authUrl,\n sessionId: sessionId,\n });\n }\n\n private async completeAuthConnection(authResult: AuthResult): Promise<ConnectResult> {\n // Check if we have an existing session\n const session = await this.storage.getSession();\n\n if (!session) {\n throw new Error(\"No session found after redirect - session may have expired\");\n }\n\n // Update session with actual wallet ID and auth info from redirect\n session.walletId = authResult.walletId;\n session.authProvider = authResult.provider || session.authProvider;\n session.userInfo = { ...session.userInfo, ...authResult.userInfo };\n session.status = \"completed\";\n session.lastUsed = Date.now();\n await this.storage.saveSession(session);\n\n await this.initializeClientFromSession(session);\n\n return {\n walletId: this.walletId!,\n addresses: this.addresses,\n status: \"completed\",\n };\n }\n\n /*\n * We use this method to initialize the PhantomClient and fetch wallet addresses from a completed session.\n * This is the final step that sets up the provider's client state and retrieves available addresses.\n */\n private async initializeClientFromSession(session: Session): Promise<void> {\n // Create client from session\n this.logger.log(\"EMBEDDED_PROVIDER\", \"Initializing PhantomClient from session\", {\n organizationId: session.organizationId,\n walletId: session.walletId,\n });\n\n const stamper = new ApiKeyStamper({\n apiSecretKey: session.keypair.secretKey,\n });\n\n this.client = new PhantomClient(\n {\n apiBaseUrl: this.config.apiBaseUrl,\n organizationId: session.organizationId,\n },\n stamper,\n );\n\n this.walletId = session.walletId;\n\n // Get wallet addresses and filter by enabled address types with retry\n this.addresses = await this.getAndFilterWalletAddresses(session.walletId);\n }\n}\n","import type { AuthResult, JWTAuthOptions } from \"../interfaces\";\n\nexport class JWTAuth {\n async authenticate(options: JWTAuthOptions): Promise<AuthResult> {\n // Validate JWT token format\n if (!options.jwtToken || typeof options.jwtToken !== \"string\") {\n throw new Error(\"Invalid JWT token: token must be a non-empty string\");\n }\n\n // Basic JWT format validation (3 parts separated by dots)\n const jwtParts = options.jwtToken.split(\".\");\n if (jwtParts.length !== 3) {\n throw new Error(\"Invalid JWT token format: token must have 3 parts separated by dots\");\n }\n\n // JWT authentication flow - direct API call to create wallet with JWT\n try {\n // This would typically make an API call to your backend\n // which would validate the JWT and create/retrieve the wallet\n const response = await fetch(\"/api/auth/jwt\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${options.jwtToken}`,\n },\n body: JSON.stringify({\n organizationId: options.organizationId,\n parentOrganizationId: options.parentOrganizationId,\n customAuthData: options.customAuthData,\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorData = await response.json();\n errorMessage = errorData.message || errorData.error || errorMessage;\n } catch {\n errorMessage = response.statusText || errorMessage;\n }\n\n switch (response.status) {\n case 400:\n throw new Error(`Invalid JWT authentication request: ${errorMessage}`);\n case 401:\n throw new Error(`JWT token is invalid or expired: ${errorMessage}`);\n case 403:\n throw new Error(`JWT authentication forbidden: ${errorMessage}`);\n case 404:\n throw new Error(`JWT authentication endpoint not found: ${errorMessage}`);\n case 429:\n throw new Error(`Too many JWT authentication requests: ${errorMessage}`);\n case 500:\n case 502:\n case 503:\n case 504:\n throw new Error(`JWT authentication server error: ${errorMessage}`);\n default:\n throw new Error(`JWT authentication failed: ${errorMessage}`);\n }\n }\n\n let result;\n try {\n result = await response.json();\n } catch (parseError) {\n throw new Error(\"Invalid response from JWT authentication server: response is not valid JSON\");\n }\n\n if (!result.walletId) {\n throw new Error(\"Invalid JWT authentication response: missing walletId\");\n }\n\n return {\n walletId: result.walletId,\n provider: \"jwt\",\n userInfo: result.userInfo || {},\n };\n } catch (error) {\n if (error instanceof TypeError && error.message.includes(\"fetch\")) {\n throw new Error(\"JWT authentication failed: network error or invalid endpoint\");\n }\n\n if (error instanceof Error) {\n throw error; // Re-throw known errors\n }\n\n throw new Error(`JWT authentication error: ${String(error)}`);\n }\n }\n}\n","export function generateSessionId(): string {\n return (\n \"session_\" +\n Math.random().toString(36).substring(2, 15) +\n Math.random().toString(36).substring(2, 15) +\n \"_\" +\n Date.now()\n );\n}\n","import type { DebugLogger } from \"../interfaces\";\n\nexport async function retryWithBackoff<T>(\n operation: () => Promise<T>,\n operationName: string,\n logger: DebugLogger,\n maxRetries: number = 3,\n baseDelay: number = 1000,\n): Promise<T> {\n let lastError: Error;\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n logger.log(\"EMBEDDED_PROVIDER\", `Attempting ${operationName}`, {\n attempt,\n maxRetries,\n });\n return await operation();\n } catch (error) {\n lastError = error as Error;\n logger.warn(\"EMBEDDED_PROVIDER\", `${operationName} failed`, {\n attempt,\n maxRetries,\n error: error instanceof Error ? error.message : String(error),\n });\n\n if (attempt === maxRetries) {\n logger.error(\"EMBEDDED_PROVIDER\", `${operationName} failed after ${maxRetries} attempts`, {\n finalError: error instanceof Error ? error.message : String(error),\n });\n break;\n }\n\n // Exponential backoff: 1s, 2s, 4s\n const delay = baseDelay * Math.pow(2, attempt - 1);\n logger.log(\"EMBEDDED_PROVIDER\", `Retrying ${operationName} in ${delay}ms`, {\n attempt: attempt + 1,\n delay,\n });\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n\n throw lastError!;\n}\n"],"mappings":"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","names":[]}
|