@bounded-sh/client 0.0.103 → 0.0.105
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 +15 -3
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/oidc-auth.d.ts +17 -13
- package/dist/auth/providers/offchain-auth-provider.d.ts +7 -9
- package/dist/{email-auth-provider-Cr3I4Po5.js → email-auth-provider-CB5ZBjJL.js} +2 -2
- package/dist/{email-auth-provider-Cr3I4Po5.js.map → email-auth-provider-CB5ZBjJL.js.map} +1 -1
- package/dist/{email-auth-provider-DCZ93FYg.js → email-auth-provider-Czo-xM6n.js} +2 -2
- package/dist/{email-auth-provider-DCZ93FYg.js.map → email-auth-provider-Czo-xM6n.js.map} +1 -1
- package/dist/{email-auth-provider-Ct3YA68i.mjs → email-auth-provider-D9SQu9c1.mjs} +2 -2
- package/dist/{email-auth-provider-Ct3YA68i.mjs.map → email-auth-provider-D9SQu9c1.mjs.map} +1 -1
- package/dist/{email-auth-provider-BJXeBUwp.mjs → email-auth-provider-sA_9RTRl.mjs} +2 -2
- package/dist/{email-auth-provider-BJXeBUwp.mjs.map → email-auth-provider-sA_9RTRl.mjs.map} +1 -1
- package/dist/global.d.ts +2 -0
- package/dist/{guest-auth-provider-CUo6sgop.mjs → guest-auth-provider-1mrlNF2G.mjs} +2 -2
- package/dist/{guest-auth-provider-CUo6sgop.mjs.map → guest-auth-provider-1mrlNF2G.mjs.map} +1 -1
- package/dist/{guest-auth-provider--Sqlucfr.js → guest-auth-provider-DJ5nCGjq.js} +2 -2
- package/dist/{guest-auth-provider--Sqlucfr.js.map → guest-auth-provider-DJ5nCGjq.js.map} +1 -1
- package/dist/{guest-auth-provider-CPj-w7_R.js → guest-auth-provider-DXNCZ0R6.js} +2 -2
- package/dist/{guest-auth-provider-CPj-w7_R.js.map → guest-auth-provider-DXNCZ0R6.js.map} +1 -1
- package/dist/{guest-auth-provider-DMtDTZeA.mjs → guest-auth-provider-DsFTB844.mjs} +2 -2
- package/dist/{guest-auth-provider-DMtDTZeA.mjs.map → guest-auth-provider-DsFTB844.mjs.map} +1 -1
- package/dist/{index-CaZx7sd8.mjs → index-Bu6vnwxM.mjs} +385 -471
- package/dist/index-Bu6vnwxM.mjs.map +1 -0
- package/dist/{index-Pyb2SLgv.js → index-yb-jFt5B.js} +385 -471
- package/dist/index-yb-jFt5B.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{index.native-3T0IcOed.mjs → index.native-BSOqaKIz.mjs} +321 -447
- package/dist/index.native-BSOqaKIz.mjs.map +1 -0
- package/dist/{index.native-PGrxu7RO.js → index.native-BpcNwWQX.js} +321 -447
- package/dist/index.native-BpcNwWQX.js.map +1 -0
- package/dist/index.native.d.ts +1 -1
- package/dist/index.native.js +2 -2
- package/dist/index.native.mjs +1 -1
- package/dist/{injected-evm-wallet-provider-B3Mtkdnu.mjs → injected-evm-wallet-provider-BuDISWSq.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-B3Mtkdnu.mjs.map → injected-evm-wallet-provider-BuDISWSq.mjs.map} +1 -1
- package/dist/{injected-evm-wallet-provider-U0XJu5HM.js → injected-evm-wallet-provider-D7tQrDaO.js} +2 -2
- package/dist/{injected-evm-wallet-provider-U0XJu5HM.js.map → injected-evm-wallet-provider-D7tQrDaO.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-C8rBnyO1.mjs → injected-evm-wallet-provider-Xb2G-ZGI.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-C8rBnyO1.mjs.map → injected-evm-wallet-provider-Xb2G-ZGI.mjs.map} +1 -1
- package/dist/{injected-evm-wallet-provider-BNu6Z-iN.js → injected-evm-wallet-provider-_UNpc_hM.js} +2 -2
- package/dist/{injected-evm-wallet-provider-BNu6Z-iN.js.map → injected-evm-wallet-provider-_UNpc_hM.js.map} +1 -1
- package/dist/{injected-wallet-provider-DR8xVudg.mjs → injected-wallet-provider-BDCLkdR_.mjs} +2 -2
- package/dist/{injected-wallet-provider-DR8xVudg.mjs.map → injected-wallet-provider-BDCLkdR_.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-29eTkM-A.js → injected-wallet-provider-C7Q0haGY.js} +2 -2
- package/dist/{injected-wallet-provider-29eTkM-A.js.map → injected-wallet-provider-C7Q0haGY.js.map} +1 -1
- package/dist/{injected-wallet-provider-Cv9RELO6.js → injected-wallet-provider-CFwJYxh2.js} +2 -2
- package/dist/{injected-wallet-provider-Cv9RELO6.js.map → injected-wallet-provider-CFwJYxh2.js.map} +1 -1
- package/dist/{injected-wallet-provider-BnmYbm79.mjs → injected-wallet-provider-DZOwvKgd.mjs} +2 -2
- package/dist/{injected-wallet-provider-BnmYbm79.mjs.map → injected-wallet-provider-DZOwvKgd.mjs.map} +1 -1
- package/package.json +2 -2
- package/dist/index-CaZx7sd8.mjs.map +0 -1
- package/dist/index-Pyb2SLgv.js.map +0 -1
- package/dist/index.native-3T0IcOed.mjs.map +0 -1
- package/dist/index.native-PGrxu7RO.js.map +0 -1
|
@@ -190,12 +190,46 @@ function setPlatform(adapter) {
|
|
|
190
190
|
function getPlatform() {
|
|
191
191
|
return _platform;
|
|
192
192
|
}
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Mobile / Android detection helpers
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
/**
|
|
197
|
+
* Detect mobile device even when Chrome "Request Desktop Site" is active.
|
|
198
|
+
*
|
|
199
|
+
* Desktop mode on Android strips "Android" and "Mobile" from the UA, leaving
|
|
200
|
+
* something like "Linux x86_64 …". The fallback detects this by looking for
|
|
201
|
+
* touch capability + small viewport + a UA that doesn't belong to a known
|
|
202
|
+
* desktop OS (ChromeOS, Windows, macOS).
|
|
203
|
+
*/
|
|
204
|
+
function detectMobile() {
|
|
205
|
+
const ua = _platform.getUserAgent();
|
|
206
|
+
if (/Android|iPhone|iPad|iPod/i.test(ua))
|
|
207
|
+
return true;
|
|
208
|
+
if (typeof navigator !== 'undefined' && typeof window !== 'undefined') {
|
|
209
|
+
const hasTouch = navigator.maxTouchPoints > 0;
|
|
210
|
+
// Macs don't have touch screens, so "Macintosh" + touch = iOS desktop mode
|
|
211
|
+
// (also catches iPadOS 13+ which always reports as Macintosh)
|
|
212
|
+
if (/Macintosh/i.test(ua) && hasTouch)
|
|
213
|
+
return true;
|
|
214
|
+
// Android desktop mode: Linux UA + touch + small viewport, excluding
|
|
215
|
+
// known desktop OSes (ChromeOS, Windows, macOS)
|
|
216
|
+
const isSmallViewport = window.innerWidth <= 1024;
|
|
217
|
+
const isDesktopOS = /CrOS|Windows|Macintosh/i.test(ua);
|
|
218
|
+
if (hasTouch && isSmallViewport && !isDesktopOS)
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
193
223
|
|
|
194
224
|
/**
|
|
195
|
-
* OffchainAuthProvider wraps a real auth provider (
|
|
225
|
+
* OffchainAuthProvider wraps a real auth provider (Turnkey, Phantom, ...) for the
|
|
226
|
+
* poofnet environment.
|
|
196
227
|
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
228
|
+
* Poofnet has no chain, but its onchain writes are still signed: the worker
|
|
229
|
+
* issues an unsendable approval message and verifies the wallet's signature
|
|
230
|
+
* before committing. So `signTransaction` and `signMessage` go straight to the
|
|
231
|
+
* wrapped wallet, whose own prompt is the only one the user sees. Only a raw
|
|
232
|
+
* submit - which needs a real cluster - is refused here.
|
|
199
233
|
*/
|
|
200
234
|
class OffchainAuthProvider {
|
|
201
235
|
constructor(wrappedProvider, config = {}) {
|
|
@@ -230,10 +264,7 @@ class OffchainAuthProvider {
|
|
|
230
264
|
return null;
|
|
231
265
|
}
|
|
232
266
|
async signTransaction(tx) {
|
|
233
|
-
|
|
234
|
-
await this.showUnsupportedTransactionModal();
|
|
235
|
-
}
|
|
236
|
-
throw new Error('Poofnet does not support real Solana transactions. Deploy your project to mainnet to use this feature.');
|
|
267
|
+
return this.wrappedProvider.signTransaction(tx);
|
|
237
268
|
}
|
|
238
269
|
/**
|
|
239
270
|
* Sign and submit transaction - not supported in poofnet environment.
|
|
@@ -258,21 +289,11 @@ class OffchainAuthProvider {
|
|
|
258
289
|
}
|
|
259
290
|
await this.wrappedProvider.exportWallet(address);
|
|
260
291
|
}
|
|
261
|
-
// ============ signMessage delegates to wrapped provider ============
|
|
262
292
|
async signMessage(message) {
|
|
263
293
|
var _a, _b, _c, _d, _e, _f;
|
|
264
294
|
(_b = (_a = this.config).onSigningStart) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
265
295
|
try {
|
|
266
|
-
// On RN (no DOM), auto-confirm — there's no modal to show.
|
|
267
|
-
// On web, show the confirmation modal.
|
|
268
|
-
if (getPlatform().hasDOM) {
|
|
269
|
-
const confirmed = await this.showTransactionModal(message);
|
|
270
|
-
if (!confirmed) {
|
|
271
|
-
throw new Error("Transaction rejected by user");
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
296
|
const signature = await this.wrappedProvider.signMessage(message);
|
|
275
|
-
// Callback: signing complete
|
|
276
297
|
(_d = (_c = this.config).onSigningComplete) === null || _d === void 0 ? void 0 : _d.call(_c, signature);
|
|
277
298
|
return signature;
|
|
278
299
|
}
|
|
@@ -363,99 +384,6 @@ class OffchainAuthProvider {
|
|
|
363
384
|
document.addEventListener("keydown", escHandler);
|
|
364
385
|
});
|
|
365
386
|
}
|
|
366
|
-
async showTransactionModal(message) {
|
|
367
|
-
return new Promise((resolve) => {
|
|
368
|
-
// Parse the transaction message for display
|
|
369
|
-
let parsed = {};
|
|
370
|
-
try {
|
|
371
|
-
parsed = JSON.parse(message);
|
|
372
|
-
}
|
|
373
|
-
catch (_a) {
|
|
374
|
-
// If not JSON, just show raw message
|
|
375
|
-
}
|
|
376
|
-
// Create modal container. Captured in this closure so every teardown
|
|
377
|
-
// path acts on exactly this prompt, never on a newer one that may have
|
|
378
|
-
// replaced a shared field.
|
|
379
|
-
const container = document.createElement("div");
|
|
380
|
-
container.id = "poofnet-tx-modal";
|
|
381
|
-
container.appendChild(this.buildTransactionModalContent(parsed, message));
|
|
382
|
-
document.body.appendChild(container);
|
|
383
|
-
// Add styles
|
|
384
|
-
const style = document.createElement("style");
|
|
385
|
-
style.textContent = this.getModalStyles();
|
|
386
|
-
container.appendChild(style);
|
|
387
|
-
// Mark all OTHER body children as inert to disable focus traps from
|
|
388
|
-
// other modals, recording only the ones this modal changed (skipping
|
|
389
|
-
// any already-inert) so cleanup restores exactly what we set and never
|
|
390
|
-
// re-enables app-owned inert regions.
|
|
391
|
-
const selfInerted = [];
|
|
392
|
-
document.body.childNodes.forEach((child) => {
|
|
393
|
-
if (child !== container &&
|
|
394
|
-
child instanceof HTMLElement &&
|
|
395
|
-
!child.hasAttribute("inert")) {
|
|
396
|
-
child.setAttribute("inert", "");
|
|
397
|
-
selfInerted.push(child);
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
|
-
// Animate in
|
|
401
|
-
requestAnimationFrame(() => {
|
|
402
|
-
const overlay = container.querySelector(".poofnet-modal-overlay");
|
|
403
|
-
const content = container.querySelector(".poofnet-modal-content");
|
|
404
|
-
if (overlay)
|
|
405
|
-
overlay.style.opacity = "1";
|
|
406
|
-
if (content) {
|
|
407
|
-
content.style.opacity = "1";
|
|
408
|
-
content.style.transform = "translateY(0)";
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
// Single idempotent teardown for every settlement path: removes the key
|
|
412
|
-
// handler, restores only self-added inert, and removes exactly this
|
|
413
|
-
// captured container after the exit animation.
|
|
414
|
-
let settled = false;
|
|
415
|
-
const escHandler = (e) => {
|
|
416
|
-
if (e.key === "Escape")
|
|
417
|
-
settle(false);
|
|
418
|
-
};
|
|
419
|
-
const cleanup = () => {
|
|
420
|
-
if (settled)
|
|
421
|
-
return;
|
|
422
|
-
settled = true;
|
|
423
|
-
document.removeEventListener("keydown", escHandler);
|
|
424
|
-
for (const el of selfInerted)
|
|
425
|
-
el.removeAttribute("inert");
|
|
426
|
-
const overlay = container.querySelector(".poofnet-modal-overlay");
|
|
427
|
-
const content = container.querySelector(".poofnet-modal-content");
|
|
428
|
-
if (overlay)
|
|
429
|
-
overlay.style.opacity = "0";
|
|
430
|
-
if (content) {
|
|
431
|
-
content.style.opacity = "0";
|
|
432
|
-
content.style.transform = "translateY(20px)";
|
|
433
|
-
}
|
|
434
|
-
setTimeout(() => container.remove(), 200);
|
|
435
|
-
};
|
|
436
|
-
const settle = (confirmed) => {
|
|
437
|
-
cleanup();
|
|
438
|
-
resolve(confirmed);
|
|
439
|
-
};
|
|
440
|
-
// Handle confirm
|
|
441
|
-
const confirmBtn = container.querySelector("#poofnet-confirm-btn");
|
|
442
|
-
confirmBtn === null || confirmBtn === void 0 ? void 0 : confirmBtn.addEventListener("click", () => settle(true));
|
|
443
|
-
// Handle cancel
|
|
444
|
-
const cancelBtn = container.querySelector("#poofnet-cancel-btn");
|
|
445
|
-
cancelBtn === null || cancelBtn === void 0 ? void 0 : cancelBtn.addEventListener("click", () => settle(false));
|
|
446
|
-
// Handle close button
|
|
447
|
-
const closeBtn = container.querySelector("#poofnet-close-btn");
|
|
448
|
-
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.addEventListener("click", () => settle(false));
|
|
449
|
-
// Handle overlay click
|
|
450
|
-
const overlay = container.querySelector(".poofnet-modal-overlay");
|
|
451
|
-
overlay === null || overlay === void 0 ? void 0 : overlay.addEventListener("click", (e) => {
|
|
452
|
-
if (e.target === overlay)
|
|
453
|
-
settle(false);
|
|
454
|
-
});
|
|
455
|
-
// Handle escape key
|
|
456
|
-
document.addEventListener("keydown", escHandler);
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
387
|
createElement(tag, className, text) {
|
|
460
388
|
const el = document.createElement(tag);
|
|
461
389
|
if (className)
|
|
@@ -510,113 +438,6 @@ class OffchainAuthProvider {
|
|
|
510
438
|
footer.appendChild(close);
|
|
511
439
|
return overlay;
|
|
512
440
|
}
|
|
513
|
-
formatPath(path) {
|
|
514
|
-
const parts = path.split("/").filter(Boolean);
|
|
515
|
-
if (parts.length >= 2) {
|
|
516
|
-
return {
|
|
517
|
-
collection: parts[0],
|
|
518
|
-
documentId: parts[parts.length - 1],
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
return { collection: parts[0] || path, documentId: null };
|
|
522
|
-
}
|
|
523
|
-
formatFieldValue(value) {
|
|
524
|
-
if (value === null || value === undefined)
|
|
525
|
-
return "null";
|
|
526
|
-
if (typeof value === "string") {
|
|
527
|
-
return value.length > 30
|
|
528
|
-
? `"${value.slice(0, 30)}..."`
|
|
529
|
-
: `"${value}"`;
|
|
530
|
-
}
|
|
531
|
-
if (typeof value === "number" || typeof value === "boolean")
|
|
532
|
-
return String(value);
|
|
533
|
-
if (Array.isArray(value))
|
|
534
|
-
return `[${value.length} items]`;
|
|
535
|
-
if (typeof value === "object")
|
|
536
|
-
return `{${Object.keys(value).length} fields}`;
|
|
537
|
-
return String(value);
|
|
538
|
-
}
|
|
539
|
-
getActionDescription(type, collection, isNew) {
|
|
540
|
-
if (type === "delete")
|
|
541
|
-
return `Delete from ${collection}`;
|
|
542
|
-
return isNew ? `Create in ${collection}` : `Update ${collection}`;
|
|
543
|
-
}
|
|
544
|
-
buildTransactionModalContent(parsed, rawMessage) {
|
|
545
|
-
const instructions = parsed.instructions || [];
|
|
546
|
-
// Group instructions by type for summary
|
|
547
|
-
const sets = instructions.filter((i) => i.type === "set");
|
|
548
|
-
const deletes = instructions.filter((i) => i.type === "delete");
|
|
549
|
-
// Create summary text
|
|
550
|
-
let summaryText = "";
|
|
551
|
-
if (sets.length > 0 && deletes.length > 0) {
|
|
552
|
-
summaryText = `${sets.length} update${sets.length > 1 ? "s" : ""}, ${deletes.length} delete${deletes.length > 1 ? "s" : ""}`;
|
|
553
|
-
}
|
|
554
|
-
else if (sets.length > 0) {
|
|
555
|
-
summaryText = `${sets.length} update${sets.length > 1 ? "s" : ""}`;
|
|
556
|
-
}
|
|
557
|
-
else if (deletes.length > 0) {
|
|
558
|
-
summaryText = `${deletes.length} delete${deletes.length > 1 ? "s" : ""}`;
|
|
559
|
-
}
|
|
560
|
-
const { overlay, body, footer } = this.buildModalShell("🔐", "Confirm Transaction");
|
|
561
|
-
if (instructions.length > 0) {
|
|
562
|
-
if (summaryText) {
|
|
563
|
-
this.appendTextElement(body, "div", "poofnet-summary", summaryText);
|
|
564
|
-
}
|
|
565
|
-
const list = this.createElement("div", "poofnet-instructions-list");
|
|
566
|
-
for (const inst of instructions) {
|
|
567
|
-
list.appendChild(this.buildInstructionElement(inst));
|
|
568
|
-
}
|
|
569
|
-
body.appendChild(list);
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
const section = this.createElement("div", "poofnet-section");
|
|
573
|
-
this.appendTextElement(section, "h3", "poofnet-section-title", "Message");
|
|
574
|
-
const raw = this.createElement("pre", "poofnet-raw-message", rawMessage.slice(0, 200) + (rawMessage.length > 200 ? "..." : ""));
|
|
575
|
-
section.appendChild(raw);
|
|
576
|
-
body.appendChild(section);
|
|
577
|
-
}
|
|
578
|
-
body.appendChild(this.buildInfoBox("This is a simulated transaction. No real fees will be charged."));
|
|
579
|
-
const cancel = this.createElement("button", "poofnet-btn poofnet-btn-cancel", "Cancel");
|
|
580
|
-
cancel.id = "poofnet-cancel-btn";
|
|
581
|
-
cancel.type = "button";
|
|
582
|
-
footer.appendChild(cancel);
|
|
583
|
-
const confirm = this.createElement("button", "poofnet-btn poofnet-btn-confirm", "Confirm");
|
|
584
|
-
confirm.id = "poofnet-confirm-btn";
|
|
585
|
-
confirm.type = "button";
|
|
586
|
-
footer.appendChild(confirm);
|
|
587
|
-
return overlay;
|
|
588
|
-
}
|
|
589
|
-
buildInstructionElement(inst) {
|
|
590
|
-
const { collection, documentId } = this.formatPath(inst.path);
|
|
591
|
-
const isDelete = inst.type === "delete";
|
|
592
|
-
const card = this.createElement("div", `poofnet-instruction ${isDelete ? "poofnet-action-delete" : "poofnet-action-set"}`);
|
|
593
|
-
const header = this.createElement("div", "poofnet-instruction-header");
|
|
594
|
-
this.appendTextElement(header, "span", "poofnet-instruction-icon", isDelete ? "🗑️" : "✏️");
|
|
595
|
-
this.appendTextElement(header, "span", "poofnet-instruction-action", isDelete ? "Delete" : "Save");
|
|
596
|
-
this.appendTextElement(header, "span", "poofnet-instruction-collection", collection);
|
|
597
|
-
card.appendChild(header);
|
|
598
|
-
if (documentId) {
|
|
599
|
-
this.appendTextElement(card, "div", "poofnet-instruction-id", `ID: ${documentId}`);
|
|
600
|
-
}
|
|
601
|
-
if (inst.data && !isDelete) {
|
|
602
|
-
const fieldKeys = Object.keys(inst.data).filter((key) => !key.startsWith("_"));
|
|
603
|
-
const visibleFields = fieldKeys.slice(0, 4);
|
|
604
|
-
if (visibleFields.length > 0) {
|
|
605
|
-
const fields = this.createElement("div", "poofnet-fields");
|
|
606
|
-
for (const key of visibleFields) {
|
|
607
|
-
const row = this.createElement("div", "poofnet-field");
|
|
608
|
-
this.appendTextElement(row, "span", "poofnet-field-key", `${key}:`);
|
|
609
|
-
this.appendTextElement(row, "span", "poofnet-field-value", this.formatFieldValue(inst.data[key]));
|
|
610
|
-
fields.appendChild(row);
|
|
611
|
-
}
|
|
612
|
-
if (fieldKeys.length > 4) {
|
|
613
|
-
this.appendTextElement(fields, "div", "poofnet-field-more", `+${fieldKeys.length - 4} more fields`);
|
|
614
|
-
}
|
|
615
|
-
card.appendChild(fields);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return card;
|
|
619
|
-
}
|
|
620
441
|
getModalStyles() {
|
|
621
442
|
return `
|
|
622
443
|
/* Ensure inert elements remain visible while being non-interactive */
|
|
@@ -713,130 +534,6 @@ class OffchainAuthProvider {
|
|
|
713
534
|
flex: 1;
|
|
714
535
|
}
|
|
715
536
|
|
|
716
|
-
.poofnet-summary {
|
|
717
|
-
font-size: 13px;
|
|
718
|
-
color: #6b7280;
|
|
719
|
-
margin-bottom: 12px;
|
|
720
|
-
text-align: center;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
.poofnet-section {
|
|
724
|
-
margin-bottom: 16px;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
.poofnet-section-title {
|
|
728
|
-
font-size: 12px;
|
|
729
|
-
font-weight: 600;
|
|
730
|
-
color: #666;
|
|
731
|
-
text-transform: uppercase;
|
|
732
|
-
letter-spacing: 0.5px;
|
|
733
|
-
margin: 0 0 8px;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
.poofnet-instructions-list {
|
|
737
|
-
display: flex;
|
|
738
|
-
flex-direction: column;
|
|
739
|
-
gap: 10px;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
.poofnet-instruction {
|
|
743
|
-
background: #f9fafb;
|
|
744
|
-
border-radius: 10px;
|
|
745
|
-
padding: 12px;
|
|
746
|
-
border-left: 3px solid #8b5cf6;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
.poofnet-instruction.poofnet-action-delete {
|
|
750
|
-
border-left-color: #ef4444;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
.poofnet-instruction-header {
|
|
754
|
-
display: flex;
|
|
755
|
-
align-items: center;
|
|
756
|
-
gap: 8px;
|
|
757
|
-
margin-bottom: 4px;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
.poofnet-instruction-icon {
|
|
761
|
-
font-size: 16px;
|
|
762
|
-
flex-shrink: 0;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
.poofnet-instruction-action {
|
|
766
|
-
font-size: 13px;
|
|
767
|
-
font-weight: 600;
|
|
768
|
-
color: #8b5cf6;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
.poofnet-action-delete .poofnet-instruction-action {
|
|
772
|
-
color: #ef4444;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
.poofnet-instruction-collection {
|
|
776
|
-
font-size: 13px;
|
|
777
|
-
font-weight: 500;
|
|
778
|
-
color: #374151;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
.poofnet-instruction-id {
|
|
782
|
-
font-size: 11px;
|
|
783
|
-
color: #9ca3af;
|
|
784
|
-
font-family: monospace;
|
|
785
|
-
margin-bottom: 8px;
|
|
786
|
-
word-break: break-all;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
.poofnet-fields {
|
|
790
|
-
background: white;
|
|
791
|
-
border-radius: 6px;
|
|
792
|
-
padding: 8px 10px;
|
|
793
|
-
margin-top: 8px;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
.poofnet-field {
|
|
797
|
-
display: flex;
|
|
798
|
-
gap: 8px;
|
|
799
|
-
padding: 4px 0;
|
|
800
|
-
font-size: 12px;
|
|
801
|
-
border-bottom: 1px solid #f3f4f6;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
.poofnet-field:last-child {
|
|
805
|
-
border-bottom: none;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
.poofnet-field-key {
|
|
809
|
-
color: #6b7280;
|
|
810
|
-
font-weight: 500;
|
|
811
|
-
flex-shrink: 0;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
.poofnet-field-value {
|
|
815
|
-
color: #374151;
|
|
816
|
-
word-break: break-all;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
.poofnet-field-more {
|
|
820
|
-
font-size: 11px;
|
|
821
|
-
color: #9ca3af;
|
|
822
|
-
padding-top: 4px;
|
|
823
|
-
font-style: italic;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
.poofnet-raw-message {
|
|
827
|
-
background: #f9fafb;
|
|
828
|
-
border-radius: 8px;
|
|
829
|
-
padding: 12px;
|
|
830
|
-
font-size: 11px;
|
|
831
|
-
font-family: monospace;
|
|
832
|
-
color: #4b5563;
|
|
833
|
-
white-space: pre-wrap;
|
|
834
|
-
word-break: break-all;
|
|
835
|
-
margin: 0;
|
|
836
|
-
max-height: 120px;
|
|
837
|
-
overflow-y: auto;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
537
|
.poofnet-info-box {
|
|
841
538
|
display: flex;
|
|
842
539
|
align-items: center;
|
|
@@ -885,15 +582,6 @@ class OffchainAuthProvider {
|
|
|
885
582
|
.poofnet-btn-cancel:hover {
|
|
886
583
|
background: #e5e7eb;
|
|
887
584
|
}
|
|
888
|
-
|
|
889
|
-
.poofnet-btn-confirm {
|
|
890
|
-
background: #8b5cf6;
|
|
891
|
-
color: white;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
.poofnet-btn-confirm:hover {
|
|
895
|
-
background: #7c3aed;
|
|
896
|
-
}
|
|
897
585
|
`;
|
|
898
586
|
}
|
|
899
587
|
}
|
|
@@ -5358,9 +5046,18 @@ const OIDC_NO_WALLET = 'This session has no Turnkey wallet. Sign in again to pro
|
|
|
5358
5046
|
// same tab (redirect + popup, or two popups) each own a distinct record, so a second
|
|
5359
5047
|
// attempt cannot clobber the first's verifier/state/redirectUri.
|
|
5360
5048
|
const PKCE_STORE_PREFIX = 'bounded.oidc.pkce';
|
|
5049
|
+
const STATE_PREFIX = 'bounded.';
|
|
5050
|
+
const LOGIN_TIMEOUT_MS = 10 * 60000;
|
|
5051
|
+
const CALLBACK_PARAMS = ['code', 'state', 'error', 'error_description', 'iss'];
|
|
5361
5052
|
function pkceKey(state) {
|
|
5362
5053
|
return `${PKCE_STORE_PREFIX}.${state}`;
|
|
5363
5054
|
}
|
|
5055
|
+
function removeAttempt(state) {
|
|
5056
|
+
try {
|
|
5057
|
+
sessionStorage.removeItem(pkceKey(state));
|
|
5058
|
+
}
|
|
5059
|
+
catch ( /* Storage may have been disabled mid-login. */_a) { /* Storage may have been disabled mid-login. */ }
|
|
5060
|
+
}
|
|
5364
5061
|
const ISSUER_SESSION_KIND_KEY = 'bounded_issuer_session_kind';
|
|
5365
5062
|
// Active session store (Web on web, RN on React Native) — selected by config,
|
|
5366
5063
|
// not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
|
|
@@ -5469,6 +5166,165 @@ async function makePkce() {
|
|
|
5469
5166
|
function randomState() {
|
|
5470
5167
|
return b64url(getPlatform().getRandomBytes(16));
|
|
5471
5168
|
}
|
|
5169
|
+
let pendingCompletion;
|
|
5170
|
+
let loginReturnError;
|
|
5171
|
+
/** The last OAuth return failure in this initialization, or null. Initialization
|
|
5172
|
+
* remains usable so the UI can display this error and offer a fresh sign-in. */
|
|
5173
|
+
function getLoginReturnError() {
|
|
5174
|
+
return (loginReturnError === null || loginReturnError === void 0 ? void 0 : loginReturnError.generation) === getInitializationGeneration() ? loginReturnError.error : null;
|
|
5175
|
+
}
|
|
5176
|
+
// A hosted page can contain both an app bundle and the platform widget's SDK.
|
|
5177
|
+
// They share one code exchange, while adopting the session in their own registries.
|
|
5178
|
+
const COMPLETIONS = Symbol.for('bounded.oidc.completions');
|
|
5179
|
+
function cleanCallbackUrl(url) {
|
|
5180
|
+
const clean = new URL(url);
|
|
5181
|
+
for (const key of CALLBACK_PARAMS)
|
|
5182
|
+
clean.searchParams.delete(key);
|
|
5183
|
+
return clean;
|
|
5184
|
+
}
|
|
5185
|
+
async function validateAttempt(pkce, callback) {
|
|
5186
|
+
const cfg = await getConfig();
|
|
5187
|
+
if (!pkce || typeof pkce.verifier !== 'string' || !pkce.verifier ||
|
|
5188
|
+
pkce.appId !== cfg.appId || pkce.issuer !== await issuerBase()) {
|
|
5189
|
+
throw new Error('Login belongs to a different app or auth server. Start sign-in again.');
|
|
5190
|
+
}
|
|
5191
|
+
if (!Number.isFinite(pkce.createdAt) || Date.now() - pkce.createdAt > LOGIN_TIMEOUT_MS || pkce.createdAt > Date.now()) {
|
|
5192
|
+
throw new Error('Login expired. Start sign-in again.');
|
|
5193
|
+
}
|
|
5194
|
+
if (callback && (cleanCallbackUrl(callback).href !== cleanCallbackUrl(new URL(pkce.redirectUri)).href ||
|
|
5195
|
+
(callback.searchParams.has('iss') && callback.searchParams.get('iss') !== pkce.issuer))) {
|
|
5196
|
+
throw new Error('Login returned to a different address or from a different auth server. Start sign-in again.');
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
// The receiver acknowledges only after checking origin, window and state. An
|
|
5200
|
+
// absent/disconnected opener must never leave a popup claiming success.
|
|
5201
|
+
async function handOffPopup(url, state) {
|
|
5202
|
+
const cfg = await getConfig();
|
|
5203
|
+
const issuer = await issuerBase();
|
|
5204
|
+
const opener = window.opener;
|
|
5205
|
+
if (!opener || opener === window || opener.closed) {
|
|
5206
|
+
throw new Error('The original sign-in window is unavailable. Close this window and sign in again in the original tab.');
|
|
5207
|
+
}
|
|
5208
|
+
return new Promise((resolve, reject) => {
|
|
5209
|
+
const cleanup = () => { clearTimeout(timer); window.removeEventListener('message', onMessage); };
|
|
5210
|
+
const onMessage = (event) => {
|
|
5211
|
+
var _a;
|
|
5212
|
+
if (event.source !== opener || event.origin !== url.origin ||
|
|
5213
|
+
((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) !== 'bounded-oidc-accepted' || event.data.state !== state)
|
|
5214
|
+
return;
|
|
5215
|
+
cleanup();
|
|
5216
|
+
try {
|
|
5217
|
+
window.close();
|
|
5218
|
+
}
|
|
5219
|
+
catch ( /* The opener also closes its own popup handle. */_b) { /* The opener also closes its own popup handle. */ }
|
|
5220
|
+
resolve();
|
|
5221
|
+
};
|
|
5222
|
+
const timer = setTimeout(() => {
|
|
5223
|
+
cleanup();
|
|
5224
|
+
reject(new Error('Could not reach the original sign-in tab. Close this window and start sign-in again.'));
|
|
5225
|
+
}, 10000);
|
|
5226
|
+
window.addEventListener('message', onMessage);
|
|
5227
|
+
try {
|
|
5228
|
+
opener.postMessage({ type: 'bounded-oidc-code', state, code: url.searchParams.get('code'),
|
|
5229
|
+
error: url.searchParams.get('error'), errorDescription: url.searchParams.get('error_description'),
|
|
5230
|
+
callbackUrl: url.href, appId: cfg.appId, issuer }, url.origin);
|
|
5231
|
+
}
|
|
5232
|
+
catch (error) {
|
|
5233
|
+
cleanup();
|
|
5234
|
+
reject(error);
|
|
5235
|
+
}
|
|
5236
|
+
});
|
|
5237
|
+
}
|
|
5238
|
+
async function finishLoginReturn(url, state) {
|
|
5239
|
+
try {
|
|
5240
|
+
if (!/^bounded\.(popup|redirect)\.[A-Za-z0-9_-]{22}$/.test(state)) {
|
|
5241
|
+
throw new Error('Invalid login state. Start sign-in again.');
|
|
5242
|
+
}
|
|
5243
|
+
if (state.startsWith('bounded.popup.')) {
|
|
5244
|
+
await handOffPopup(url, state);
|
|
5245
|
+
return { handled: true, user: null };
|
|
5246
|
+
}
|
|
5247
|
+
const raw = sessionStorage.getItem(pkceKey(state));
|
|
5248
|
+
if (!raw)
|
|
5249
|
+
throw new Error('Missing login attempt. Start sign-in again in this tab.');
|
|
5250
|
+
const pkce = JSON.parse(raw);
|
|
5251
|
+
if (pkce.state !== state)
|
|
5252
|
+
throw new Error('OAuth state mismatch. Start sign-in again.');
|
|
5253
|
+
await validateAttempt(pkce, url);
|
|
5254
|
+
const error = url.searchParams.get('error');
|
|
5255
|
+
if (error)
|
|
5256
|
+
throw new Error(url.searchParams.get('error_description') || `Login failed: ${error}`);
|
|
5257
|
+
const user = await exchangeCode(url.searchParams.get('code'), pkce);
|
|
5258
|
+
return { handled: true, user, idToken: sessionManager().getIdToken() };
|
|
5259
|
+
}
|
|
5260
|
+
finally {
|
|
5261
|
+
removeAttempt(state);
|
|
5262
|
+
const clean = cleanCallbackUrl(url);
|
|
5263
|
+
if (window.location.href === url.href) {
|
|
5264
|
+
window.history.replaceState(window.history.state, document.title, clean.pathname + clean.search + clean.hash);
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
/** Internal initialization step. Unrelated OAuth callbacks are left untouched. */
|
|
5269
|
+
function completePendingLogin() {
|
|
5270
|
+
if (!hasDOM$2())
|
|
5271
|
+
return Promise.resolve({ handled: false, user: null });
|
|
5272
|
+
const url = new URL(window.location.href);
|
|
5273
|
+
const state = url.searchParams.get('state');
|
|
5274
|
+
if (!(state === null || state === void 0 ? void 0 : state.startsWith(STATE_PREFIX)) || (!url.searchParams.get('code') && !url.searchParams.get('error'))) {
|
|
5275
|
+
return Promise.resolve({ handled: false, user: null });
|
|
5276
|
+
}
|
|
5277
|
+
const generation = getInitializationGeneration();
|
|
5278
|
+
if ((pendingCompletion === null || pendingCompletion === void 0 ? void 0 : pendingCompletion.href) === url.href && pendingCompletion.generation === generation)
|
|
5279
|
+
return pendingCompletion.promise;
|
|
5280
|
+
const promise = finishSharedLoginReturn(url, state).catch((error) => {
|
|
5281
|
+
loginReturnError = { generation, error: error instanceof Error ? error : new Error(String(error)) };
|
|
5282
|
+
throw loginReturnError.error;
|
|
5283
|
+
}).finally(() => {
|
|
5284
|
+
if ((pendingCompletion === null || pendingCompletion === void 0 ? void 0 : pendingCompletion.promise) === promise)
|
|
5285
|
+
pendingCompletion = undefined;
|
|
5286
|
+
});
|
|
5287
|
+
pendingCompletion = { href: url.href, generation, promise };
|
|
5288
|
+
return promise;
|
|
5289
|
+
}
|
|
5290
|
+
async function finishSharedLoginReturn(url, state) {
|
|
5291
|
+
var _a;
|
|
5292
|
+
var _b;
|
|
5293
|
+
const cfg = await getConfig();
|
|
5294
|
+
const issuer = await issuerBase();
|
|
5295
|
+
if (state.startsWith('bounded.redirect.')) {
|
|
5296
|
+
const raw = sessionStorage.getItem(pkceKey(state));
|
|
5297
|
+
// A second SDK configured for another app/issuer on this page does not own
|
|
5298
|
+
// this return. Leave its URL and proof available to the initiating app.
|
|
5299
|
+
try {
|
|
5300
|
+
const attempt = raw && JSON.parse(raw);
|
|
5301
|
+
if (attempt && (attempt.appId !== cfg.appId || attempt.issuer !== issuer))
|
|
5302
|
+
return { handled: false, user: null };
|
|
5303
|
+
}
|
|
5304
|
+
catch ( /* The owning completion reports malformed attempt data. */_c) { /* The owning completion reports malformed attempt data. */ }
|
|
5305
|
+
}
|
|
5306
|
+
const shared = (_a = (_b = window)[COMPLETIONS]) !== null && _a !== void 0 ? _a : (_b[COMPLETIONS] = new Map());
|
|
5307
|
+
const key = JSON.stringify([url.href, cfg.appId, issuer]);
|
|
5308
|
+
const existing = shared.get(key);
|
|
5309
|
+
if (!existing) {
|
|
5310
|
+
const completion = finishLoginReturn(url, state);
|
|
5311
|
+
shared.set(key, completion);
|
|
5312
|
+
return completion;
|
|
5313
|
+
}
|
|
5314
|
+
const generation = getInitializationGeneration();
|
|
5315
|
+
const result = await existing;
|
|
5316
|
+
if (result.user) {
|
|
5317
|
+
if (!result.idToken || sessionManager().getIdToken() !== result.idToken) {
|
|
5318
|
+
throw new Error('The completed login session has changed. Start sign-in again.');
|
|
5319
|
+
}
|
|
5320
|
+
await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc',
|
|
5321
|
+
user: Object.assign(Object.assign({}, result.user), { provider: oidcProviderSingleton }), beforeCommit: () => {
|
|
5322
|
+
if (generation !== getInitializationGeneration())
|
|
5323
|
+
throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
|
|
5324
|
+
} });
|
|
5325
|
+
}
|
|
5326
|
+
return result;
|
|
5327
|
+
}
|
|
5472
5328
|
function normalizeMethod(value) {
|
|
5473
5329
|
const normalized = String(value || '').trim().toLowerCase();
|
|
5474
5330
|
return /^[a-z0-9_-]{1,40}$/.test(normalized) ? normalized : null;
|
|
@@ -5511,7 +5367,8 @@ function appendHostedLoginHints(params, opts) {
|
|
|
5511
5367
|
// native (expo-web-browser) path. The caller decides where the PkceState lives
|
|
5512
5368
|
// (web → sessionStorage across the full-page redirect; native → in-memory across
|
|
5513
5369
|
// the awaited browser session).
|
|
5514
|
-
async function buildAuthorize(opts) {
|
|
5370
|
+
async function buildAuthorize(opts, mode = 'redirect') {
|
|
5371
|
+
loginReturnError = undefined;
|
|
5515
5372
|
const cfg = await getConfig();
|
|
5516
5373
|
if (!cfg.appId)
|
|
5517
5374
|
throw new Error('appId is not configured (init the SDK with your appId)');
|
|
@@ -5520,15 +5377,23 @@ async function buildAuthorize(opts) {
|
|
|
5520
5377
|
const redirectUri = opts.redirectUri || (hasDOM$2() ? window.location.origin + window.location.pathname : undefined);
|
|
5521
5378
|
if (!redirectUri)
|
|
5522
5379
|
throw new Error('redirectUri is required on React Native — pass the https universal link the app owner registered.');
|
|
5380
|
+
if (hasDOM$2()) {
|
|
5381
|
+
const redirect = new URL(redirectUri);
|
|
5382
|
+
if (redirect.origin !== window.location.origin || redirect.hash || CALLBACK_PARAMS.some((key) => redirect.searchParams.has(key))) {
|
|
5383
|
+
throw new Error('redirectUri must be on this app origin, without a fragment or OAuth callback parameters.');
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5523
5386
|
const { verifier, challenge } = await makePkce();
|
|
5524
|
-
const state = randomState()
|
|
5387
|
+
const state = `${STATE_PREFIX}${mode}.${randomState()}`;
|
|
5525
5388
|
const base = await issuerBase();
|
|
5526
5389
|
const params = new URLSearchParams({
|
|
5527
5390
|
client_id: cfg.appId, redirect_uri: redirectUri, response_type: 'code', scope: 'openid profile email',
|
|
5528
5391
|
code_challenge: challenge, code_challenge_method: 'S256', state,
|
|
5529
5392
|
});
|
|
5530
5393
|
appendHostedLoginHints(params, opts);
|
|
5531
|
-
return { url: `${base}/api/auth/oauth2/authorize?${params.toString()}`, pkce: {
|
|
5394
|
+
return { url: `${base}/api/auth/oauth2/authorize?${params.toString()}`, pkce: {
|
|
5395
|
+
verifier, state, redirectUri, appId: cfg.appId, issuer: base, createdAt: Date.now(),
|
|
5396
|
+
} };
|
|
5532
5397
|
}
|
|
5533
5398
|
// Are we on React Native (no DOM)? OIDC dispatch uses this to choose the native
|
|
5534
5399
|
// expo-web-browser driver vs. the web full-page-redirect/popup driver.
|
|
@@ -5577,8 +5442,7 @@ function parseCallbackParams(callbackUrl) {
|
|
|
5577
5442
|
}
|
|
5578
5443
|
// Full native login: open the hosted page in the system auth session, await the
|
|
5579
5444
|
// universal-link callback, verify state, and run the SAME PKCE code→token exchange
|
|
5580
|
-
// as web. Returns the signed-in User
|
|
5581
|
-
// completeLoginFromRedirect() round-trip the way the web full-page redirect needs).
|
|
5445
|
+
// as web. Returns the signed-in User inline; web redirects complete on the next init().
|
|
5582
5446
|
async function runNativeLogin(opts) {
|
|
5583
5447
|
var _a, _b;
|
|
5584
5448
|
const { url, pkce } = await buildAuthorize(opts);
|
|
@@ -5605,7 +5469,7 @@ async function runNativeLogin(opts) {
|
|
|
5605
5469
|
if (!returnedState || returnedState !== pkce.state) {
|
|
5606
5470
|
throw new Error('OAuth state mismatch — possible CSRF; aborting');
|
|
5607
5471
|
}
|
|
5608
|
-
return await exchangeCode(code, pkce
|
|
5472
|
+
return await exchangeCode(code, pkce);
|
|
5609
5473
|
}
|
|
5610
5474
|
finally {
|
|
5611
5475
|
setAuthLoading(false);
|
|
@@ -5614,7 +5478,7 @@ async function runNativeLogin(opts) {
|
|
|
5614
5478
|
/** Start hosted login (Google / Apple / email / hosted phone). Works on WEB and REACT NATIVE.
|
|
5615
5479
|
*
|
|
5616
5480
|
* WEB: navigates the whole page to the hosted Bounded login and resolves (void) as the page
|
|
5617
|
-
* unloads;
|
|
5481
|
+
* unloads; init() completes the return to redirectUri automatically.
|
|
5618
5482
|
*
|
|
5619
5483
|
* REACT NATIVE: opens the hosted page via expo-web-browser, awaits the https universal-link
|
|
5620
5484
|
* callback, runs the PKCE exchange INLINE, and resolves with the signed-in User. No
|
|
@@ -5632,63 +5496,32 @@ async function loginWithRedirect(opts) {
|
|
|
5632
5496
|
// is per-tab and cleared on close — appropriate for a short-lived auth handshake. Keyed by
|
|
5633
5497
|
// this attempt's own state so a second concurrent login cannot overwrite it.
|
|
5634
5498
|
sessionStorage.setItem(pkceKey(pkce.state), JSON.stringify(pkce));
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
/** Finish login on the redirect_uri page (WEB ONLY): exchange ?code= for a token (PKCE-verified),
|
|
5639
|
-
* store the session, and return the User. Idempotent: returns null if there's no ?code= in the URL.
|
|
5640
|
-
*
|
|
5641
|
-
* On REACT NATIVE this returns null and is a no-op: native login completes INLINE inside
|
|
5642
|
-
* loginWithRedirect() (which resolves with the User), so there is no separate redirect
|
|
5643
|
-
* page to finish from. Calling it on RN is harmless. */
|
|
5644
|
-
async function completeLoginFromRedirect() {
|
|
5645
|
-
if (!hasDOM$2())
|
|
5646
|
-
return null;
|
|
5647
|
-
const url = new URL(window.location.href);
|
|
5648
|
-
const code = url.searchParams.get('code');
|
|
5649
|
-
const returnedState = url.searchParams.get('state');
|
|
5650
|
-
if (!code)
|
|
5651
|
-
return null;
|
|
5652
|
-
// POPUP path: if we're the redirect_uri page running INSIDE the loginWithPopup() window,
|
|
5653
|
-
// hand the code to the opener and close — no separate completeLoginInPopup() call needed.
|
|
5654
|
-
// The opener is the same app/origin, so post to our own origin (the opener's message
|
|
5655
|
-
// handler checks ev.origin === redirect_uri origin). One call now finishes BOTH flows.
|
|
5656
|
-
if (window.opener && window.opener !== window) {
|
|
5657
|
-
try {
|
|
5658
|
-
window.opener.postMessage({ type: 'bounded-oidc-code', code, state: returnedState }, window.location.origin);
|
|
5659
|
-
}
|
|
5660
|
-
catch ( /* ignore */_a) { /* ignore */ }
|
|
5661
|
-
try {
|
|
5662
|
-
window.close();
|
|
5663
|
-
}
|
|
5664
|
-
catch ( /* ignore */_b) { /* ignore */ }
|
|
5665
|
-
return null;
|
|
5666
|
-
}
|
|
5499
|
+
// This promise represents dispatch, not authentication on the next page.
|
|
5500
|
+
// Browsers can cancel navigation (for example at beforeunload) without firing
|
|
5501
|
+
// pageshow. Never leave this document's SDK loading after dispatch settles.
|
|
5667
5502
|
setAuthLoading(true);
|
|
5668
5503
|
try {
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
const pkce = JSON.parse(raw);
|
|
5675
|
-
if (!returnedState || returnedState !== pkce.state)
|
|
5676
|
-
throw new Error('OAuth state mismatch — possible CSRF; aborting');
|
|
5677
|
-
const user = await exchangeCode(code, pkce.verifier, pkce.redirectUri);
|
|
5678
|
-
sessionStorage.removeItem(pkceKey(returnedState));
|
|
5679
|
-
// Strip code/state from the URL so a reload doesn't re-exchange (codes are single-use anyway).
|
|
5680
|
-
url.searchParams.delete('code');
|
|
5681
|
-
url.searchParams.delete('state');
|
|
5682
|
-
window.history.replaceState({}, document.title, url.pathname + url.search + url.hash);
|
|
5683
|
-
return user;
|
|
5504
|
+
window.location.href = url;
|
|
5505
|
+
}
|
|
5506
|
+
catch (error) {
|
|
5507
|
+
removeAttempt(pkce.state);
|
|
5508
|
+
throw error;
|
|
5684
5509
|
}
|
|
5685
5510
|
finally {
|
|
5686
5511
|
setAuthLoading(false);
|
|
5687
5512
|
}
|
|
5688
5513
|
}
|
|
5514
|
+
/** Optional explicit completion. Normal apps only await init(), which already finishes login.
|
|
5515
|
+
* Concurrent calls share one exchange. Returns null when no Bounded return is present. */
|
|
5516
|
+
async function completeLoginFromRedirect() {
|
|
5517
|
+
const result = await completePendingLogin();
|
|
5518
|
+
const error = getLoginReturnError();
|
|
5519
|
+
if (error)
|
|
5520
|
+
throw error;
|
|
5521
|
+
return result.user;
|
|
5522
|
+
}
|
|
5689
5523
|
/** Popup variant: open the hosted login in a popup; resolves with the User once the popup's
|
|
5690
|
-
* redirect_uri page
|
|
5691
|
-
* completeLoginFromRedirect() (which auto-detects the popup) — no separate call needed. */
|
|
5524
|
+
* redirect_uri page initializes the SDK, which hands the code back automatically. */
|
|
5692
5525
|
async function loginWithPopup(opts) {
|
|
5693
5526
|
if (!hasDOM$2())
|
|
5694
5527
|
throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
|
|
@@ -5707,7 +5540,7 @@ async function loginWithPopup(opts) {
|
|
|
5707
5540
|
throw new Error('Popup blocked — use loginWithRedirect() instead');
|
|
5708
5541
|
let authorization;
|
|
5709
5542
|
try {
|
|
5710
|
-
authorization = await buildAuthorize(opts);
|
|
5543
|
+
authorization = await buildAuthorize(opts, 'popup');
|
|
5711
5544
|
}
|
|
5712
5545
|
catch (error) {
|
|
5713
5546
|
try {
|
|
@@ -5742,6 +5575,7 @@ async function loginWithPopup(opts) {
|
|
|
5742
5575
|
popup.location.href = url;
|
|
5743
5576
|
}
|
|
5744
5577
|
catch (_d) {
|
|
5578
|
+
removeAttempt(pkce.state);
|
|
5745
5579
|
try {
|
|
5746
5580
|
popup.close();
|
|
5747
5581
|
}
|
|
@@ -5757,17 +5591,21 @@ async function loginWithPopup(opts) {
|
|
|
5757
5591
|
let settled = false;
|
|
5758
5592
|
let timer;
|
|
5759
5593
|
let navigationTimer;
|
|
5594
|
+
let completionTimer;
|
|
5760
5595
|
const cleanup = () => {
|
|
5761
5596
|
settled = true;
|
|
5762
5597
|
clearInterval(timer);
|
|
5763
5598
|
clearTimeout(navigationTimer);
|
|
5599
|
+
clearTimeout(completionTimer);
|
|
5764
5600
|
window.removeEventListener('message', onMsg);
|
|
5601
|
+
removeAttempt(pkce.state);
|
|
5765
5602
|
};
|
|
5766
5603
|
const onMsg = async (ev) => {
|
|
5767
|
-
if (ev.origin !== expectedOrigin)
|
|
5768
|
-
return;
|
|
5604
|
+
if (settled || ev.origin !== expectedOrigin || ev.source !== popup)
|
|
5605
|
+
return;
|
|
5769
5606
|
const d = ev.data;
|
|
5770
|
-
if (!d || d.type !== 'bounded-oidc-code' ||
|
|
5607
|
+
if (!d || d.type !== 'bounded-oidc-code' ||
|
|
5608
|
+
(typeof d.code !== 'string' && typeof d.error !== 'string'))
|
|
5771
5609
|
return;
|
|
5772
5610
|
// Validate state BEFORE tearing down this listener, against the record THIS
|
|
5773
5611
|
// attempt captured in the closure (`pkce`), not a shared sessionStorage reread.
|
|
@@ -5775,10 +5613,15 @@ async function loginWithPopup(opts) {
|
|
|
5775
5613
|
// stay registered for our own code, instead of cleaning up on another's message.
|
|
5776
5614
|
if (d.state !== pkce.state)
|
|
5777
5615
|
return;
|
|
5616
|
+
if (d.appId !== pkce.appId || d.issuer !== pkce.issuer)
|
|
5617
|
+
return;
|
|
5778
5618
|
cleanup();
|
|
5779
5619
|
try {
|
|
5780
|
-
|
|
5781
|
-
|
|
5620
|
+
await validateAttempt(pkce, new URL(d.callbackUrl));
|
|
5621
|
+
popup.postMessage({ type: 'bounded-oidc-accepted', state: pkce.state }, expectedOrigin);
|
|
5622
|
+
if (d.error)
|
|
5623
|
+
throw new Error(typeof d.errorDescription === 'string' ? d.errorDescription : `Login failed: ${d.error}`);
|
|
5624
|
+
const user = await exchangeCode(d.code, pkce);
|
|
5782
5625
|
try {
|
|
5783
5626
|
popup.close();
|
|
5784
5627
|
}
|
|
@@ -5786,10 +5629,22 @@ async function loginWithPopup(opts) {
|
|
|
5786
5629
|
resolve(user);
|
|
5787
5630
|
}
|
|
5788
5631
|
catch (e) {
|
|
5632
|
+
try {
|
|
5633
|
+
popup.close();
|
|
5634
|
+
}
|
|
5635
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
5789
5636
|
reject(e);
|
|
5790
5637
|
}
|
|
5791
5638
|
};
|
|
5792
5639
|
window.addEventListener('message', onMsg);
|
|
5640
|
+
completionTimer = setTimeout(() => {
|
|
5641
|
+
cleanup();
|
|
5642
|
+
try {
|
|
5643
|
+
popup.close();
|
|
5644
|
+
}
|
|
5645
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
5646
|
+
reject(new Error('Login expired or its popup disconnected. Start sign-in again.'));
|
|
5647
|
+
}, LOGIN_TIMEOUT_MS);
|
|
5793
5648
|
// Some browser/extension combinations can return a real popup handle but
|
|
5794
5649
|
// strand the window at about:blank instead of navigating to the hosted
|
|
5795
5650
|
// authorize URL. The old code then waited forever because the popup was
|
|
@@ -5847,25 +5702,6 @@ async function loginWithPopup(opts) {
|
|
|
5847
5702
|
setAuthLoading(false);
|
|
5848
5703
|
}
|
|
5849
5704
|
}
|
|
5850
|
-
/** Call this on the redirect_uri page when login was opened via loginWithPopup(): posts the
|
|
5851
|
-
* code back to the opener and closes. (For the redirect variant use completeLoginFromRedirect.) */
|
|
5852
|
-
function completeLoginInPopup(openerOrigin) {
|
|
5853
|
-
if (!hasDOM$2() || !window.opener)
|
|
5854
|
-
return;
|
|
5855
|
-
const url = new URL(window.location.href);
|
|
5856
|
-
const code = url.searchParams.get('code');
|
|
5857
|
-
const state = url.searchParams.get('state');
|
|
5858
|
-
if (!code)
|
|
5859
|
-
return;
|
|
5860
|
-
try {
|
|
5861
|
-
window.opener.postMessage({ type: 'bounded-oidc-code', code, state }, openerOrigin);
|
|
5862
|
-
}
|
|
5863
|
-
catch ( /* ignore */_a) { /* ignore */ }
|
|
5864
|
-
try {
|
|
5865
|
-
window.close();
|
|
5866
|
-
}
|
|
5867
|
-
catch ( /* ignore */_b) { /* ignore */ }
|
|
5868
|
-
}
|
|
5869
5705
|
// Establish a Bounded session from a server-minted inline-session response (the same
|
|
5870
5706
|
// { idToken, accessToken, refreshToken } shape as /session/refresh). Used by the
|
|
5871
5707
|
// Turnkey-native login path (authMode:"turnkey"), which mints the session server-side
|
|
@@ -5908,14 +5744,20 @@ async function inlineAuthEndpoint() {
|
|
|
5908
5744
|
const cfg = await getConfig();
|
|
5909
5745
|
return { base: await issuerBase(), appId: cfg.appId };
|
|
5910
5746
|
}
|
|
5911
|
-
async function exchangeCode(code,
|
|
5912
|
-
const
|
|
5913
|
-
const
|
|
5747
|
+
async function exchangeCode(code, pkce) {
|
|
5748
|
+
const generation = getInitializationGeneration();
|
|
5749
|
+
const beforeCommit = () => {
|
|
5750
|
+
if (generation !== getInitializationGeneration())
|
|
5751
|
+
throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
|
|
5752
|
+
};
|
|
5753
|
+
await validateAttempt(pkce);
|
|
5754
|
+
beforeCommit();
|
|
5755
|
+
const base = pkce.issuer;
|
|
5914
5756
|
// OIDC token endpoint: application/x-www-form-urlencoded (the standard). Public client (no
|
|
5915
5757
|
// secret) authenticated by PKCE. The code is single-use + short-TTL on the server.
|
|
5916
5758
|
const body = new URLSearchParams({
|
|
5917
|
-
grant_type: 'authorization_code', code, redirect_uri: redirectUri,
|
|
5918
|
-
client_id:
|
|
5759
|
+
grant_type: 'authorization_code', code, redirect_uri: pkce.redirectUri,
|
|
5760
|
+
client_id: pkce.appId, code_verifier: pkce.verifier,
|
|
5919
5761
|
});
|
|
5920
5762
|
let res;
|
|
5921
5763
|
try {
|
|
@@ -5939,10 +5781,20 @@ async function exchangeCode(code, verifier, redirectUri) {
|
|
|
5939
5781
|
const accessToken = tokenField(data, 'access_token', 'accessToken') || idToken;
|
|
5940
5782
|
const refreshToken = boundedRefreshTokenField(data);
|
|
5941
5783
|
const user = userFromIdToken(idToken);
|
|
5942
|
-
|
|
5784
|
+
// Reinitialization while the request was in flight must not install this
|
|
5785
|
+
// app's result under a different app's session manager/configuration.
|
|
5786
|
+
beforeCommit();
|
|
5787
|
+
const rollback = await sessionManager().storeSession(String(user.address || ''), accessToken, idToken, refreshToken, base, beforeCommit);
|
|
5943
5788
|
// Adopt on EVERY session surface (see establishInlineSession) - hosted
|
|
5944
5789
|
// popup/redirect completions all land here, so this closes their gap too.
|
|
5945
|
-
|
|
5790
|
+
try {
|
|
5791
|
+
await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
|
|
5792
|
+
}
|
|
5793
|
+
catch (error) {
|
|
5794
|
+
if (typeof rollback === 'function')
|
|
5795
|
+
await rollback();
|
|
5796
|
+
throw error;
|
|
5797
|
+
}
|
|
5946
5798
|
// OIDC login does establish the issuer's Better Auth browser cookie. Record
|
|
5947
5799
|
// that fact separately from the provider name so logout clears the cookie
|
|
5948
5800
|
// and returns through the registered app origin. Stamped AFTER the adoption
|
|
@@ -6308,7 +6160,7 @@ async function getAuthProvider(config) {
|
|
|
6308
6160
|
// manager as legacy email sessions. Human login should be started
|
|
6309
6161
|
// with loginWithRedirect(); this provider is kept as the restore and
|
|
6310
6162
|
// logout surface for those sessions.
|
|
6311
|
-
currentAuthProvider = new (await import('./email-auth-provider-
|
|
6163
|
+
currentAuthProvider = new (await import('./email-auth-provider-sA_9RTRl.mjs')).EmailAuthProvider();
|
|
6312
6164
|
break;
|
|
6313
6165
|
case "phantom": {
|
|
6314
6166
|
// First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
|
|
@@ -6350,7 +6202,7 @@ async function getAuthProvider(config) {
|
|
|
6350
6202
|
// (bounded-login-widget.ts), so registration never counts as
|
|
6351
6203
|
// evidence the user has a wallet.
|
|
6352
6204
|
await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
|
|
6353
|
-
const { InjectedWalletProvider } = await import('./injected-wallet-provider-
|
|
6205
|
+
const { InjectedWalletProvider } = await import('./injected-wallet-provider-BDCLkdR_.mjs');
|
|
6354
6206
|
currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
|
|
6355
6207
|
break;
|
|
6356
6208
|
}
|
|
@@ -6363,7 +6215,7 @@ async function getAuthProvider(config) {
|
|
|
6363
6215
|
// Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
|
|
6364
6216
|
// to point at a specific wallet or set chainId/domain.
|
|
6365
6217
|
const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
|
|
6366
|
-
const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-
|
|
6218
|
+
const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-BuDISWSq.mjs');
|
|
6367
6219
|
currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
|
|
6368
6220
|
break;
|
|
6369
6221
|
}
|
|
@@ -6388,7 +6240,7 @@ async function getAuthProvider(config) {
|
|
|
6388
6240
|
break;
|
|
6389
6241
|
case "guest":
|
|
6390
6242
|
// Anonymous auth: a device-local ed25519 keypair signs the challenge.
|
|
6391
|
-
currentAuthProvider = new (await import('./guest-auth-provider-
|
|
6243
|
+
currentAuthProvider = new (await import('./guest-auth-provider-1mrlNF2G.mjs')).GuestAuthProvider();
|
|
6392
6244
|
break;
|
|
6393
6245
|
}
|
|
6394
6246
|
if (!currentAuthProvider) {
|
|
@@ -6450,7 +6302,7 @@ async function signInAnonymously() {
|
|
|
6450
6302
|
// Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
|
|
6451
6303
|
// the main bundle for data-only apps. Specifier is a static string literal
|
|
6452
6304
|
// so bundle-splitting can see it.
|
|
6453
|
-
const provider = new (await import('./guest-auth-provider-
|
|
6305
|
+
const provider = new (await import('./guest-auth-provider-1mrlNF2G.mjs')).GuestAuthProvider();
|
|
6454
6306
|
setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
|
|
6455
6307
|
// adoptSessionProvider awaits only getConfig() (already resolved here) and then
|
|
6456
6308
|
// runs every write - provider/method/config/marker/user - in one synchronous
|
|
@@ -6621,7 +6473,7 @@ function ensureSharedWalletLoginReady() {
|
|
|
6621
6473
|
if (!sharedWalletReady) {
|
|
6622
6474
|
sharedWalletReady = (async () => {
|
|
6623
6475
|
await getConfig();
|
|
6624
|
-
await import('./injected-wallet-provider-
|
|
6476
|
+
await import('./injected-wallet-provider-BDCLkdR_.mjs');
|
|
6625
6477
|
})().catch((error) => {
|
|
6626
6478
|
sharedWalletReady = null;
|
|
6627
6479
|
throw error;
|
|
@@ -6724,7 +6576,7 @@ async function loginWithWallet(options) {
|
|
|
6724
6576
|
await ensureSharedWalletLoginReady();
|
|
6725
6577
|
else
|
|
6726
6578
|
await ensureWalletLoginReady();
|
|
6727
|
-
const { InjectedWalletProvider } = await import('./injected-wallet-provider-
|
|
6579
|
+
const { InjectedWalletProvider } = await import('./injected-wallet-provider-BDCLkdR_.mjs');
|
|
6728
6580
|
// Keep the RAW wallet provider: the offchain wrapper forwards only a fixed
|
|
6729
6581
|
// set of methods, so cleanup below must speak to the provider that actually
|
|
6730
6582
|
// holds the config rather than to whatever is wrapping it.
|
|
@@ -6977,13 +6829,14 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
6977
6829
|
WalletConfigError: WalletConfigError,
|
|
6978
6830
|
clearIncompatibleSession: clearIncompatibleSession,
|
|
6979
6831
|
completeLoginFromRedirect: completeLoginFromRedirect,
|
|
6980
|
-
|
|
6832
|
+
completePendingLogin: completePendingLogin,
|
|
6981
6833
|
ensureSharedWalletLoginReady: ensureSharedWalletLoginReady,
|
|
6982
6834
|
ensureWalletLoginReady: ensureWalletLoginReady,
|
|
6983
6835
|
getAuthProvider: getAuthProvider,
|
|
6984
6836
|
getConfiguredSolanaNetwork: getConfiguredSolanaNetwork,
|
|
6985
6837
|
getCurrentAuthMethod: getCurrentAuthMethod,
|
|
6986
6838
|
getInitConfig: getInitConfig,
|
|
6839
|
+
getLoginReturnError: getLoginReturnError,
|
|
6987
6840
|
isEmailRequired: isEmailRequired,
|
|
6988
6841
|
isWalletLoginDisabled: isWalletLoginDisabled,
|
|
6989
6842
|
isWalletLoginEnabled: isWalletLoginEnabled,
|
|
@@ -7060,6 +6913,10 @@ let authLoadingListeners = [];
|
|
|
7060
6913
|
let initCompleted = false;
|
|
7061
6914
|
let isAuthLoading = false;
|
|
7062
6915
|
let authReadyPromise = null;
|
|
6916
|
+
let initializingConfig = null;
|
|
6917
|
+
let initializationGeneration = 0;
|
|
6918
|
+
/** Internal guard for a login result arriving after the app reconfigured the SDK. */
|
|
6919
|
+
function getInitializationGeneration() { return initializationGeneration; }
|
|
7063
6920
|
// This file acts as a middleware for the global state of the SDK
|
|
7064
6921
|
// This mostly involves setting up the AuthProvider and managing the current user
|
|
7065
6922
|
async function initialize(newConfig) {
|
|
@@ -7085,6 +6942,19 @@ async function initialize(newConfig) {
|
|
|
7085
6942
|
authProviderInstance = await getAuthProvider(newConfig);
|
|
7086
6943
|
// Initialize config with auth provider
|
|
7087
6944
|
await init$1(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance }));
|
|
6945
|
+
// A return establishes its own provider/session, or hands off a popup.
|
|
6946
|
+
// Restoring the old provider afterwards would overwrite the new login.
|
|
6947
|
+
try {
|
|
6948
|
+
if ((await completePendingLogin()).handled) {
|
|
6949
|
+
initCompleted = true;
|
|
6950
|
+
return;
|
|
6951
|
+
}
|
|
6952
|
+
}
|
|
6953
|
+
catch (_a) {
|
|
6954
|
+
// A cancelled/failed return is a login failure, not a broken SDK.
|
|
6955
|
+
// The completion layer keeps it in getLoginReturnError(); restore
|
|
6956
|
+
// any existing session and leave sign-in/readiness usable for retry.
|
|
6957
|
+
}
|
|
7088
6958
|
// Now that core is initialized, update the auth method on the config
|
|
7089
6959
|
const authMethod = getCurrentAuthMethod();
|
|
7090
6960
|
if (authMethod) {
|
|
@@ -7116,7 +6986,42 @@ async function initialize(newConfig) {
|
|
|
7116
6986
|
setAuthLoading(false);
|
|
7117
6987
|
}
|
|
7118
6988
|
}
|
|
6989
|
+
// Compare data options by value, but preserve identity for callbacks and class
|
|
6990
|
+
// instances such as providers. Comparing only app/endpoints would silently drop
|
|
6991
|
+
// a concurrent change to wallet policy or provider configuration.
|
|
6992
|
+
function sameConfigValue(a, b, seen = new WeakMap()) {
|
|
6993
|
+
var _a;
|
|
6994
|
+
if (Object.is(a, b))
|
|
6995
|
+
return true;
|
|
6996
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object')
|
|
6997
|
+
return false;
|
|
6998
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
6999
|
+
return false;
|
|
7000
|
+
if (Array.isArray(a) && a.length !== b.length)
|
|
7001
|
+
return false;
|
|
7002
|
+
if (!Array.isArray(a)) {
|
|
7003
|
+
for (const value of [a, b]) {
|
|
7004
|
+
const prototype = Object.getPrototypeOf(value);
|
|
7005
|
+
if (prototype !== null && ((_a = prototype.constructor) === null || _a === void 0 ? void 0 : _a.name) !== 'Object')
|
|
7006
|
+
return false;
|
|
7007
|
+
}
|
|
7008
|
+
}
|
|
7009
|
+
if (seen.has(a))
|
|
7010
|
+
return seen.get(a) === b;
|
|
7011
|
+
seen.set(a, b);
|
|
7012
|
+
const left = Object.keys(a), right = Object.keys(b);
|
|
7013
|
+
return left.length === right.length && left.every((key) => Object.prototype.hasOwnProperty.call(b, key)
|
|
7014
|
+
&& sameConfigValue(a[key], b[key], seen));
|
|
7015
|
+
}
|
|
7119
7016
|
function init(newConfig) {
|
|
7017
|
+
if (initializingConfig) {
|
|
7018
|
+
if (sameConfigValue(initializingConfig, newConfig)) {
|
|
7019
|
+
return authReadyPromise;
|
|
7020
|
+
}
|
|
7021
|
+
return Promise.reject(new Error('SDK initialization is in progress. Await init() before changing its configuration.'));
|
|
7022
|
+
}
|
|
7023
|
+
initializingConfig = Object.assign({}, newConfig);
|
|
7024
|
+
initializationGeneration++;
|
|
7120
7025
|
let resolveReady;
|
|
7121
7026
|
let rejectReady;
|
|
7122
7027
|
const ready = new Promise((resolve, reject) => {
|
|
@@ -7127,7 +7032,7 @@ function init(newConfig) {
|
|
|
7127
7032
|
// auth-loading listener to call waitForAuthReady() synchronously when init()
|
|
7128
7033
|
// announces that session restoration has begun.
|
|
7129
7034
|
authReadyPromise = ready;
|
|
7130
|
-
void initialize(
|
|
7035
|
+
void initialize(initializingConfig).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
|
|
7131
7036
|
return ready;
|
|
7132
7037
|
}
|
|
7133
7038
|
/**
|
|
@@ -7854,7 +7759,7 @@ function runOtpFrame(run) {
|
|
|
7854
7759
|
// Backpack) via loginWithWallet(), run ENTIRELY on the app page (injected
|
|
7855
7760
|
// providers only exist there, never inside a cross-origin frame).
|
|
7856
7761
|
//
|
|
7857
|
-
//
|
|
7762
|
+
// Mobile uses a full-page redirect; desktop keeps a popup with redirect fallback.
|
|
7858
7763
|
// The same modal is reused for Turnkey wallet signing, so login -> first
|
|
7859
7764
|
// signature is one continuous surface.
|
|
7860
7765
|
// Palette + type ladder mirror the hosted auth page (login-page.ts): dark editorial
|
|
@@ -8097,6 +8002,16 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8097
8002
|
var _a, _b, _c, _d, _e, _f;
|
|
8098
8003
|
// Imported lazily to avoid a static cycle with the auth index barrel.
|
|
8099
8004
|
const authApi = await untilCancelled(Promise.resolve().then(function () { return index; }), pending);
|
|
8005
|
+
const hostedLogin = (options) => {
|
|
8006
|
+
if (detectMobile())
|
|
8007
|
+
return authApi.loginWithRedirect(options);
|
|
8008
|
+
// Invoke immediately inside the click handler to retain popup activation.
|
|
8009
|
+
return authApi.loginWithPopup(options).catch((error) => {
|
|
8010
|
+
if (shouldFallbackToRedirect(error))
|
|
8011
|
+
return authApi.loginWithRedirect(options);
|
|
8012
|
+
throw error;
|
|
8013
|
+
});
|
|
8014
|
+
};
|
|
8100
8015
|
// BEFORE any side effect. Registering the mobile wallet from an empty config
|
|
8101
8016
|
// would take the chainless mainnet default, and that registration cannot be
|
|
8102
8017
|
// withdrawn - so opening the widget before init() would pin the page to the
|
|
@@ -8190,6 +8105,7 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8190
8105
|
// asserts rather than fixing a bug - which is exactly why it is worth keeping.
|
|
8191
8106
|
pendingOpens.delete(pending);
|
|
8192
8107
|
return new Promise((resolve, reject) => {
|
|
8108
|
+
var _a;
|
|
8193
8109
|
// Per-open ownership generation (DS3-0402): a later open bumps the modal's
|
|
8194
8110
|
// generation, so this open's token stops matching and all of its callbacks
|
|
8195
8111
|
// (backdrop, success, cancel, and the async view swap) no-op instead of
|
|
@@ -8408,23 +8324,20 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8408
8324
|
loginHint: input.value,
|
|
8409
8325
|
redirectUri: opts.redirectUri,
|
|
8410
8326
|
};
|
|
8411
|
-
|
|
8327
|
+
hostedLogin(emailOpts)
|
|
8412
8328
|
// A hosted login that RESOLVES minted a real session, so it settles
|
|
8413
8329
|
// the card even if another lane has since taken it - dropping it
|
|
8414
8330
|
// would leave the app signed in behind a widget that says it is not.
|
|
8415
8331
|
// Only the failure path belongs to one attempt's UI.
|
|
8416
|
-
.
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
return authApi.loginWithRedirect(emailOpts).then((u) => { if (u)
|
|
8425
|
-
succeed(u); });
|
|
8332
|
+
.then((u) => {
|
|
8333
|
+
if (u) {
|
|
8334
|
+
settleAttempt(attempt);
|
|
8335
|
+
succeed(u);
|
|
8336
|
+
}
|
|
8337
|
+
else if (settleAttempt(attempt)) {
|
|
8338
|
+
submit.disabled = false;
|
|
8339
|
+
submit.textContent = "Continue";
|
|
8426
8340
|
}
|
|
8427
|
-
throw err;
|
|
8428
8341
|
})
|
|
8429
8342
|
.catch((err) => {
|
|
8430
8343
|
if (!settleAttempt(attempt))
|
|
@@ -8461,15 +8374,16 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8461
8374
|
}
|
|
8462
8375
|
setBusy(b, "Opening secure sign-in\u2026");
|
|
8463
8376
|
const socialOpts = { provider: method, redirectUri: opts.redirectUri };
|
|
8464
|
-
|
|
8465
|
-
.
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8377
|
+
hostedLogin(socialOpts)
|
|
8378
|
+
.then((u) => {
|
|
8379
|
+
if (u) {
|
|
8380
|
+
settleAttempt(attempt);
|
|
8381
|
+
succeed(u);
|
|
8382
|
+
}
|
|
8383
|
+
else if (settleAttempt(attempt)) {
|
|
8384
|
+
b.disabled = false;
|
|
8385
|
+
setLabel(b, method);
|
|
8471
8386
|
}
|
|
8472
|
-
throw err;
|
|
8473
8387
|
})
|
|
8474
8388
|
.catch((err) => {
|
|
8475
8389
|
if (!settleAttempt(attempt))
|
|
@@ -8733,7 +8647,7 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8733
8647
|
fitPanelToContent(m);
|
|
8734
8648
|
});
|
|
8735
8649
|
};
|
|
8736
|
-
swap(renderChoose());
|
|
8650
|
+
swap(renderChoose((_a = authApi.getLoginReturnError()) === null || _a === void 0 ? void 0 : _a.message));
|
|
8737
8651
|
showModal(m);
|
|
8738
8652
|
});
|
|
8739
8653
|
}
|
|
@@ -11027,5 +10941,5 @@ async function connectServiceSession(ticket, options) {
|
|
|
11027
10941
|
}
|
|
11028
10942
|
}
|
|
11029
10943
|
|
|
11030
|
-
export { onrampStatus as $, getAuthProvider as A, signInAnonymously as B, loginWithPrivy as C, loginWithWallet as D, ensureWalletLoginReady as E, ensureSharedWalletLoginReady as F, preloadWalletLogin as G, loginWithRedirect as H, completeLoginFromRedirect as I,
|
|
11031
|
-
//# sourceMappingURL=index-
|
|
10944
|
+
export { onrampStatus as $, getAuthProvider as A, signInAnonymously as B, loginWithPrivy as C, loginWithWallet as D, ensureWalletLoginReady as E, ensureSharedWalletLoginReady as F, preloadWalletLogin as G, loginWithRedirect as H, completeLoginFromRedirect as I, getLoginReturnError as J, loginWithPopup as K, openBoundedWidget as L, ensureSignedIn as M, useAuth as N, useQuery as O, useBuildRun as P, apps as Q, signSolanaMessageViaTurnkey as R, getOrCreateTurnkeyWallet as S, openTurnkeyKeyExport as T, getTurnkeyWalletSessionState as U, getTurnkeyWalletRecoveryUrl as V, WalletConfigError as W, startTurnkeyEmailLogin as X, getIdToken as Y, onramp as Z, onrampQuote as _, signMessageWithTurnkey as a, setPlatform as a0, OffchainAuthProvider as a1, PrivyExpoProvider as a2, requestEip6963Providers as a3, connectBroadcast as a4, connectServiceSession as a5, builds as a6, BuildsApiError as a7, mediaAgentTools as a8, NON_TERMINAL_STATES as a9, TERMINAL_STATES as aa, RUN_STATES as ab, PARK_REASONS as ac, FAILURE_REASONS as ad, BUILD_REVIEW_CAPABILITY_SCHEMA as ae, BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA as af, BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA as ag, signTransactionWithTurnkey as b, signTransactionV1WithTurnkey as c, signAndSubmitTransactionWithTurnkey as d, bs58 as e, defaultInjectedProvider as f, getPlatform as g, hasTurnkeyWallet as h, firstStandardWalletProvider as i, setAuthLoading as j, bufferExports as k, setStoredAuthMethod as l, confirmAndCheckTransaction as m, normalizeSolanaRpcUrl as n, defaultInjectedEvmProvider as o, init as p, getCurrentUser as q, resolveSolanaRpcUrl as r, setCurrentUser as s, transactionHasSignature as t, onAuthStateChanged as u, onAuthLoadingChanged as v, getAuthLoading as w, waitForAuthReady as x, login as y, logout as z };
|
|
10945
|
+
//# sourceMappingURL=index-Bu6vnwxM.mjs.map
|