signet-login 0.7.0 → 0.7.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/dist/modal.js +29 -8
- package/dist/signet-login.iife.js +2 -2
- package/package.json +1 -1
package/dist/modal.js
CHANGED
|
@@ -75,7 +75,7 @@ function renderPicker(refs, appName, theme) {
|
|
|
75
75
|
<button data-choice="nostrconnect" style="${buttonStyle(dark)}"><span style="font-size:1.2rem;">📡</span><span><strong>Connect a Nostr signer</strong><br><span style="font-size:0.8rem;color:${muted};">Scan with nsec.app, Amber, Keychat…</span></span></button>
|
|
76
76
|
<button data-choice="nsec" style="${buttonStyle(dark)}"><span style="font-size:1.2rem;">⚠️</span><span><strong>Paste private key</strong><br><span style="font-size:0.8rem;color:${muted};">In-memory only — risky, last resort</span></span></button>
|
|
77
77
|
</div>
|
|
78
|
-
<button data-choice="cancel" style="background:
|
|
78
|
+
<button data-choice="cancel" style="background:transparent;color:${dark ? '#e0e0e0' : '#1a1a2e'};border:1px solid ${dark ? '#3a3a4e' : '#d0d0d0'};border-radius:8px;padding:12px;cursor:pointer;font-size:0.95rem;width:100%;margin-top:12px;text-align:center;">Cancel</button>
|
|
79
79
|
`;
|
|
80
80
|
return new Promise(resolve => {
|
|
81
81
|
refs.dialog.querySelectorAll('button[data-choice]').forEach(btn => {
|
|
@@ -482,15 +482,28 @@ export async function showLoginModal(opts) {
|
|
|
482
482
|
throw new Error('appName-too-long');
|
|
483
483
|
const resolved = resolveOptions(opts);
|
|
484
484
|
const refs = buildModalShell(resolved.theme);
|
|
485
|
+
// Escape and the Android / OS back button fire the dialog's native
|
|
486
|
+
// `cancel` event. Unhandled, the dialog closes visually but the
|
|
487
|
+
// in-flight flow promise never resolves — login() hangs forever and
|
|
488
|
+
// the caller's UI is left stuck behind a dead modal. Racing every
|
|
489
|
+
// flow await against this lets the modal exit cleanly.
|
|
490
|
+
let userAborted = false;
|
|
491
|
+
const aborted = new Promise((resolve) => {
|
|
492
|
+
refs.dialog.addEventListener('cancel', () => { userAborted = true; resolve(null); });
|
|
493
|
+
});
|
|
485
494
|
try {
|
|
486
495
|
while (true) {
|
|
487
496
|
const choice = resolved.preferredMethod
|
|
488
497
|
? resolved.preferredMethod
|
|
489
|
-
: await renderPicker(refs, resolved.appName, resolved.theme);
|
|
490
|
-
if (
|
|
498
|
+
: await Promise.race([renderPicker(refs, resolved.appName, resolved.theme), aborted]);
|
|
499
|
+
if (userAborted)
|
|
500
|
+
return null;
|
|
501
|
+
if (choice === null || choice === 'cancel')
|
|
491
502
|
return null;
|
|
492
503
|
if (choice === 'nip07') {
|
|
493
|
-
const result = await runNip07Flow(refs, resolved);
|
|
504
|
+
const result = await Promise.race([runNip07Flow(refs, resolved), aborted]);
|
|
505
|
+
if (userAborted)
|
|
506
|
+
return null;
|
|
494
507
|
if (!result) {
|
|
495
508
|
if (resolved.preferredMethod)
|
|
496
509
|
return null;
|
|
@@ -537,7 +550,9 @@ export async function showLoginModal(opts) {
|
|
|
537
550
|
return null; // unreachable
|
|
538
551
|
}
|
|
539
552
|
if (choice === 'qr') {
|
|
540
|
-
const result = await runRedirectFlow(refs, resolved);
|
|
553
|
+
const result = await Promise.race([runRedirectFlow(refs, resolved), aborted]);
|
|
554
|
+
if (userAborted)
|
|
555
|
+
return null;
|
|
541
556
|
if (!result) {
|
|
542
557
|
if (resolved.preferredMethod)
|
|
543
558
|
return null;
|
|
@@ -555,7 +570,9 @@ export async function showLoginModal(opts) {
|
|
|
555
570
|
return session;
|
|
556
571
|
}
|
|
557
572
|
if (choice === 'bunker') {
|
|
558
|
-
const signer = await runBunkerFlow(refs, resolved);
|
|
573
|
+
const signer = await Promise.race([runBunkerFlow(refs, resolved), aborted]);
|
|
574
|
+
if (userAborted)
|
|
575
|
+
return null;
|
|
559
576
|
if (!signer) {
|
|
560
577
|
if (resolved.preferredMethod)
|
|
561
578
|
return null;
|
|
@@ -579,7 +596,9 @@ export async function showLoginModal(opts) {
|
|
|
579
596
|
};
|
|
580
597
|
}
|
|
581
598
|
if (choice === 'nostrconnect') {
|
|
582
|
-
const signer = await runNostrConnectFlow(refs, resolved);
|
|
599
|
+
const signer = await Promise.race([runNostrConnectFlow(refs, resolved), aborted]);
|
|
600
|
+
if (userAborted)
|
|
601
|
+
return null;
|
|
583
602
|
if (!signer) {
|
|
584
603
|
if (resolved.preferredMethod)
|
|
585
604
|
return null;
|
|
@@ -606,7 +625,9 @@ export async function showLoginModal(opts) {
|
|
|
606
625
|
};
|
|
607
626
|
}
|
|
608
627
|
if (choice === 'nsec') {
|
|
609
|
-
const signer = await runNsecFlow(refs, resolved);
|
|
628
|
+
const signer = await Promise.race([runNsecFlow(refs, resolved), aborted]);
|
|
629
|
+
if (userAborted)
|
|
630
|
+
return null;
|
|
610
631
|
if (!signer) {
|
|
611
632
|
if (resolved.preferredMethod)
|
|
612
633
|
return null;
|
|
@@ -23,7 +23,7 @@ Minimum version required to store current data is: `+i+`.
|
|
|
23
23
|
<button data-choice="nostrconnect" style="${ae(r)}"><span style="font-size:1.2rem;">\u{1F4E1}</span><span><strong>Connect a Nostr signer</strong><br><span style="font-size:0.8rem;color:${o};">Scan with nsec.app, Amber, Keychat\u2026</span></span></button>
|
|
24
24
|
<button data-choice="nsec" style="${ae(r)}"><span style="font-size:1.2rem;">\u26A0\uFE0F</span><span><strong>Paste private key</strong><br><span style="font-size:0.8rem;color:${o};">In-memory only \u2014 risky, last resort</span></span></button>
|
|
25
25
|
</div>
|
|
26
|
-
<button data-choice="cancel" style="background:
|
|
26
|
+
<button data-choice="cancel" style="background:transparent;color:${r?"#e0e0e0":"#1a1a2e"};border:1px solid ${r?"#3a3a4e":"#d0d0d0"};border-radius:8px;padding:12px;cursor:pointer;font-size:0.95rem;width:100%;margin-top:12px;text-align:center;">Cancel</button>
|
|
27
27
|
`,new Promise(a=>{t.dialog.querySelectorAll("button[data-choice]").forEach(c=>{c.addEventListener("click",()=>{let l=c.dataset.choice;a(l)})})})}async function pd(t,e){let n=Bt(e.theme),r=n?"#888":"#666",o=n?"#e0e0e0":"#1a1a2e";t.dialog.innerHTML=`
|
|
28
28
|
<h2 style="margin:0 0 8px;font-size:1.2rem;">Waiting for your extension</h2>
|
|
29
29
|
<p style="margin:0 0 20px;color:${r};font-size:0.85rem;">Approve the sign-in prompt in bark, Alby, nos2x, or whichever NIP-07 extension you use. Cold-start can take a few seconds.</p>
|
|
@@ -79,7 +79,7 @@ Minimum version required to store current data is: `+i+`.
|
|
|
79
79
|
<button data-action="back" style="${ae(n)}width:auto;flex:0 0 auto;padding:8px 16px;">\u2190 Back</button>
|
|
80
80
|
<button data-action="connect" style="${ae(n,!0)}width:auto;flex:1;padding:8px 16px;text-align:center;">Sign in</button>
|
|
81
81
|
</div>
|
|
82
|
-
`,new Promise(s=>{let a=!1,c=f=>{a||(a=!0,s(f))},l=t.dialog.querySelector("#signet-login-nsec-input"),d=t.dialog.querySelector("#signet-login-nsec-status"),h=t.dialog.querySelector('[data-action="connect"]');t.dialog.querySelector('[data-action="back"]')?.addEventListener("click",()=>{l&&(l.value=""),c(null)}),h?.addEventListener("click",()=>{let f=l?.value??"";if(!f.trim()){d&&(d.textContent="Please paste an nsec.");return}try{let u=Fs(f);l&&(l.value=""),c(u)}catch(u){d&&(d.textContent=`\u2717 ${u instanceof Error?u.message:String(u)}`,d.style.color="#d04848")}})})}function xd(t){let e=t.challenge??ud();if(!/^[0-9a-f]{64}$/i.test(e))throw new Error("challenge-must-be-64-hex");let n=typeof window<"u"?window.location.origin:"http://localhost",r=Math.max(5e3,Math.min(t.timeout??ze.timeout,6e5)),o={appName:t.appName,challenge:e.toLowerCase(),origin:n,relayUrl:t.relayUrl??ze.relayUrl,theme:t.theme??ze.theme,timeout:r,signetAppOrigin:t.signetAppOrigin??ze.signetAppOrigin};return t.preferredMethod!==void 0&&(o.preferredMethod=t.preferredMethod),t.redirectCallback!==void 0&&(o.redirectCallback=t.redirectCallback),o}async function Wc(t){if(!t.appName||t.appName.length===0)throw new Error("appName-required");if(t.appName.length>64)throw new Error("appName-too-long");let e=xd(t),n=dd(e.theme);try{for(;;){let
|
|
82
|
+
`,new Promise(s=>{let a=!1,c=f=>{a||(a=!0,s(f))},l=t.dialog.querySelector("#signet-login-nsec-input"),d=t.dialog.querySelector("#signet-login-nsec-status"),h=t.dialog.querySelector('[data-action="connect"]');t.dialog.querySelector('[data-action="back"]')?.addEventListener("click",()=>{l&&(l.value=""),c(null)}),h?.addEventListener("click",()=>{let f=l?.value??"";if(!f.trim()){d&&(d.textContent="Please paste an nsec.");return}try{let u=Fs(f);l&&(l.value=""),c(u)}catch(u){d&&(d.textContent=`\u2717 ${u instanceof Error?u.message:String(u)}`,d.style.color="#d04848")}})})}function xd(t){let e=t.challenge??ud();if(!/^[0-9a-f]{64}$/i.test(e))throw new Error("challenge-must-be-64-hex");let n=typeof window<"u"?window.location.origin:"http://localhost",r=Math.max(5e3,Math.min(t.timeout??ze.timeout,6e5)),o={appName:t.appName,challenge:e.toLowerCase(),origin:n,relayUrl:t.relayUrl??ze.relayUrl,theme:t.theme??ze.theme,timeout:r,signetAppOrigin:t.signetAppOrigin??ze.signetAppOrigin};return t.preferredMethod!==void 0&&(o.preferredMethod=t.preferredMethod),t.redirectCallback!==void 0&&(o.redirectCallback=t.redirectCallback),o}async function Wc(t){if(!t.appName||t.appName.length===0)throw new Error("appName-required");if(t.appName.length>64)throw new Error("appName-too-long");let e=xd(t),n=dd(e.theme),r=!1,o=new Promise(i=>{n.dialog.addEventListener("cancel",()=>{r=!0,i(null)})});try{for(;;){let i=e.preferredMethod?e.preferredMethod:await Promise.race([gd(n,e.appName,e.theme),o]);if(r||i===null||i==="cancel")return null;if(i==="nip07"){let s=await Promise.race([pd(n,e),o]);if(r)return null;if(!s){if(e.preferredMethod)return null;continue}let a=await ln();return{pubkey:s.pubkey,method:"nip07",signer:a,authEvent:s.authEvent}}if(i==="redirect")return await xr({appName:e.appName,challenge:e.challenge,origin:e.origin,signetAppOrigin:e.signetAppOrigin,...e.redirectCallback!==void 0?{redirectCallback:e.redirectCallback}:{}}),null;if(i==="amber")return await oa({appName:e.appName,challenge:e.challenge,origin:e.origin,...e.redirectCallback!==void 0?{redirectCallback:e.redirectCallback}:{}}),null;if(i==="qr"){let s=await Promise.race([yd(n,e),o]);if(r)return null;if(!s){if(e.preferredMethod)return null;continue}let a=new Ee(s.pubkey,s.authEvent),c={pubkey:s.pubkey,method:"redirect",signer:a,authEvent:s.authEvent};return s.displayName&&(c.displayName=s.displayName),c}if(i==="bunker"){let s=await Promise.race([bd(n,e),o]);if(r)return null;if(!s){if(e.preferredMethod)return null;continue}let a=await s.signEvent({kind:21236,content:"",tags:[["challenge",e.challenge],["origin",e.origin],["app",e.appName]]});return{pubkey:s.pubkey,method:"bunker",signer:s,authEvent:a}}if(i==="nostrconnect"){let s=await Promise.race([md(n,e),o]);if(r)return null;if(!s){if(e.preferredMethod)return null;continue}let a=await s.signEvent({kind:21236,content:"",tags:[["challenge",e.challenge],["origin",e.origin],["app",e.appName]]});return{pubkey:s.pubkey,method:"bunker",signer:s,authEvent:a}}if(i==="nsec"){let s=await Promise.race([wd(n,e),o]);if(r)return null;if(!s){if(e.preferredMethod)return null;continue}let a=await s.signEvent({kind:21236,content:"",tags:[["challenge",e.challenge],["origin",e.origin],["app",e.appName]]});return{pubkey:s.pubkey,method:"nsec",signer:s,authEvent:a}}}}finally{hd(n)}}function Yc(t){let e={};typeof window<"u"&&new URLSearchParams(window.location.search).forEach((o,i)=>{e[i]=o});let n=typeof window<"u"&&!!window.opener&&window.opener!==window;if(n){try{window.opener.postMessage({type:"signet-login-callback",params:e},"*")}catch{}if(t?.closeAfterPost??!0)try{window.close()}catch{}}return{params:e,isPopup:n}}async function Jc(t){if(t.mode==="redirect"){if(typeof window>"u")throw new Error("signet-login: redirect mode requires a browser environment");let n=t.challenge??Ed();if(!/^[0-9a-f]{64}$/i.test(n))throw new Error("challenge-must-be-64-hex");if(!t.appName||t.appName.length===0)throw new Error("appName-required");if(t.appName.length>64)throw new Error("appName-too-long");return xr({appName:t.appName,challenge:n.toLowerCase(),origin:window.location.origin,signetAppOrigin:t.signetAppOrigin??ze.signetAppOrigin,...t.redirectCallback!==void 0?{redirectCallback:t.redirectCallback}:{}})}let e=await Wc(t);return e?(t.persist!==!1&&Nr(e),e):null}function Ed(){let t=new Uint8Array(32);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}async function Xc(t){let e=ea();if(!e)return null;let n;try{n=JSON.parse(e.authEventJson)}catch{return He(),null}if(e.method==="nip07"){if(!cn()){let i=new Ee(e.pubkey,n);return{pubkey:e.pubkey,method:"redirect",signer:i,authEvent:n}}try{let i=await ln();return i.pubkey!==e.pubkey?(He(),null):{pubkey:e.pubkey,method:"nip07",signer:i,authEvent:n}}catch{return He(),null}}if(e.method==="bunker"){if(t?.reconnectBunker===!1){let i=new Ee(e.pubkey,n);return{pubkey:e.pubkey,method:"redirect",signer:i,authEvent:n}}if(!e.bunkerUri||!e.bunkerClientSkHex)return He(),null;try{let i=na(e.bunkerClientSkHex),s=await fn({uri:e.bunkerUri,clientSecretKey:i});return s.pubkey!==e.pubkey?(await s.close(),He(),null):{pubkey:e.pubkey,method:"bunker",signer:s,authEvent:n}}catch{return He(),null}}let r=new Ee(e.pubkey,n),o={pubkey:e.pubkey,method:e.method,signer:r,authEvent:n};return e.displayName&&(o.displayName=e.displayName),o}var Fc=Yc;async function Qc(){let t=ia();if(t.kind==="session")return Nr(t.session),t;if(t.kind!=="no-callback")return t;let e=Ga();if(e.kind!=="session")return e;if(e.bunkerUri)try{let n=await fn({uri:e.bunkerUri});if(n.pubkey.toLowerCase()===e.session.pubkey.toLowerCase()){let r={pubkey:e.session.pubkey,method:"bunker",signer:n,authEvent:e.session.authEvent};return e.session.displayName&&(r.displayName=e.session.displayName),Nr(r),{kind:"session",session:r}}try{await n.close()}catch{}}catch{}return Nr(e.session),e}async function el(t){if(t)try{await t.signer.close()}catch{}He()}function Nr(t){if(t.method==="nsec")return;let e={pubkey:t.pubkey,method:t.method,authEventJson:JSON.stringify(t.authEvent)};if(t.method==="bunker"){let n=t.signer;n.bunkerUri&&n.clientSecretKey instanceof Uint8Array&&(e.bunkerUri=n.bunkerUri,e.bunkerClientSkHex=ta(n.clientSecretKey))}t.expiresAt!==void 0&&(e.expiresAt=t.expiresAt),t.displayName!==void 0&&(e.displayName=t.displayName),Qs(e)}if(typeof window<"u"){let e=window.Signet??{};Object.assign(e,{login:Jc,restoreSession:Xc,logout:el,handleCallback:Fc,handleRedirectCallback:Qc}),window.Signet=e}return ll(vd);})();
|
|
83
83
|
/*! Bundled license information:
|
|
84
84
|
|
|
85
85
|
@noble/hashes/utils.js:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signet-login",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Sign in with Signet — drop-in login SDK for Nostr-aware websites. NIP-07, bunker URI, and Signet redirect/QR in one unified API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/signet-login.js",
|