signet-login 0.9.14 → 0.9.15

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 CHANGED
@@ -37,7 +37,7 @@ function buildModalShell(theme) {
37
37
  const fg = dark ? '#e0e0e0' : '#1a1a2e';
38
38
  const dialog = document.createElement('dialog');
39
39
  dialog.id = 'signet-login-dialog';
40
- dialog.style.cssText = `border:none;border-radius:16px;padding:32px;max-width:380px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.3);background:${bg};color:${fg};font-family:system-ui,-apple-system,sans-serif;`;
40
+ dialog.style.cssText = `border:none;border-radius:16px;padding:32px;max-width:460px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.3);background:${bg};color:${fg};font-family:system-ui,-apple-system,sans-serif;`;
41
41
  document.body.appendChild(dialog);
42
42
  dialog.showModal();
43
43
  // Gamepad navigation. A booth/kiosk drives this modal with a gamepad, whose
@@ -277,7 +277,7 @@ async function runRedirectFlow(refs, opts, flowOpts = {}) {
277
277
  <h2 style="margin:0 0 8px;font-size:1.2rem;">${sameDevice ? 'Open My Signet' : 'Sign in with Signet'}</h2>
278
278
  <p style="margin:0 0 16px;color:${muted};font-size:0.85rem;">${sameDevice ? 'Approve in My Signet and keep that tab open so it can sign for this app.' : 'Open the link on your phone, or scan the QR if rendered.'}</p>
279
279
  <div style="background:${dark ? '#0f0f1f' : '#f5f5f8'};border-radius:8px;padding:16px;margin-bottom:16px;">
280
- <canvas id="signet-login-qr" width="200" height="200" style="display:block;width:200px;height:200px;margin:0 auto 12px;background:#ffffff;border-radius:6px;box-sizing:border-box;"></canvas>
280
+ <canvas id="signet-login-qr" width="360" height="360" style="display:block;width:360px;height:360px;max-width:100%;margin:0 auto 12px;background:#ffffff;border-radius:6px;box-sizing:border-box;"></canvas>
281
281
  <a id="signet-login-open-signet" href="${escapeHtml(authUrl)}" target="_blank" rel="noopener" style="${sameDevice ? buttonStyle(dark, true) + 'justify-content:center;text-align:center;text-decoration:none;' : 'display:block;color:#5b6dff;font-size:0.75rem;word-break:break-all;text-decoration:none;'}">${sameDevice ? 'Open My Signet' : `${escapeHtml(authUrl.slice(0, 80))}…`}</a>
282
282
  </div>
283
283
  <p id="signet-login-status" style="margin:0 0 12px;color:${muted};font-size:0.85rem;">${sameDevice ? 'Waiting for My Signet approval…' : 'Waiting for approval…'}</p>
@@ -288,13 +288,16 @@ async function runRedirectFlow(refs, opts, flowOpts = {}) {
288
288
  `;
289
289
  // Render the auth URL into the QR canvas. Async, but the dialog has already
290
290
  // surfaced the visible link as a fallback so a slow encode doesn't block UX.
291
- // M error correction tolerates ~15% damage comfortable for camera scans.
291
+ // 360px + H error correction (~30% damage tolerance) so a phone can lock on
292
+ // from across a booth, at an angle, through screen glare. The auth URL is
293
+ // short (origin + relay + session pubkey), so H's denser modules stay large
294
+ // enough to scan at this size.
292
295
  const qrCanvas = refs.dialog.querySelector('#signet-login-qr');
293
296
  if (qrCanvas) {
294
297
  void QRCode.toCanvas(qrCanvas, authUrl, {
295
- width: 200,
298
+ width: 360,
296
299
  margin: 1,
297
- errorCorrectionLevel: 'M',
300
+ errorCorrectionLevel: 'H',
298
301
  color: { dark: '#0a0418', light: '#ffffff' },
299
302
  }).catch(() => {
300
303
  // Encoding failure (URL too long for QR L-Q levels, canvas inaccessible)
@@ -11,7 +11,7 @@ Minimum version required to store current data is: `+i+`.
11
11
  <div id="signet-qr" style="display:flex;justify-content:center;margin-bottom:24px;"></div>
12
12
  <p style="margin:0 0 16px;color:${f};font-size:0.8rem;">Waiting for verification...</p>
13
13
  <button id="signet-cancel" style="background:none;border:1px solid ${f};color:${u};padding:10px 24px;border-radius:8px;cursor:pointer;font-size:0.9rem;">Cancel</button>
14
- `,document.body.appendChild(y),y.showModal();let b=y.querySelector("#signet-qr");if(b){let R=document.createElement("div");R.style.cssText=`width:200px;height:200px;background:${d?"#2a2a3e":"#f0f0f0"};border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:0.75rem;color:${f};word-break:break-all;padding:12px;`,R.textContent=`signet:verify:${a.slice(0,40)}...`,b.appendChild(R)}let v=new BroadcastChannel("signet-verify-"+o);y.querySelector("#signet-cancel")?.addEventListener("click",()=>{v.close(),y.close(),y.remove(),l.remove(),c({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"cancelled"})}),v.onmessage=async R=>{let O=R.data;if(typeof O!="object"||O===null)return;let N=O;if(N.type!=="signet-verify-response"||N.requestId!==o||!N.credential||typeof N.credential!="object"||!Array.isArray(N.credential.tags))return;let I=N.credential,_=await Ef(I),q=vr(I.tags,"age-range"),j=vr(I.tags,"tier"),Z=vr(I.tags,"entity-type"),L=vr(I.tags,"expires"),z=q?vf(q,r.requiredAgeRange):!1,$=await Sf(I.pubkey,r.verifierCheckUrl),T=$?.confirmed??null,x=$?.method??null,p=r.acceptUnconfirmed||T===!0;y.close(),y.remove(),l.remove(),v.close();let h=j?parseInt(j,10):null,m=L?parseInt(L,10):null,E=Math.floor(Date.now()/1e3),B=m===null||!isNaN(m)&&m>E,w;_?B?z?p||(w=T===!1?"verifier-not-confirmed":"verifier-check-unavailable"):w="age-range-not-met":w="credential-expired":w="invalid-credential",c({verified:_&&B&&z&&p,ageRange:q||null,tier:h!==null&&!isNaN(h)?h:null,entityType:Z||null,credentialId:I.id,verifierPubkey:I.pubkey,verifierConfirmed:T,verifierMethod:x,issuedAt:I.created_at,expiresAt:m!==null&&!isNaN(m)?m:null,error:w})},setTimeout(()=>{y.close(),y.remove(),l.remove(),v.close(),c({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"timeout"})},r.timeout)})}function ei(t){let e=JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content]),n=Qo(new TextEncoder().encode(e));return le(n)}async function Bf(t,e){try{let n=Ot(e,t.pubkey),r=un(t.content,n),o=JSON.parse(r);if(typeof o!="object"||o===null)return null;let i=o;if(i.kind!==13||typeof i.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(i.pubkey)||typeof i.created_at!="number"||!Array.isArray(i.tags)||typeof i.content!="string"||typeof i.id!="string"||!/^[0-9a-f]{64}$/i.test(i.id)||typeof i.sig!="string"||!/^[0-9a-f]{128}$/i.test(i.sig)||ei({pubkey:i.pubkey,created_at:i.created_at,kind:13,tags:i.tags,content:i.content})!==i.id.toLowerCase())return null;let a=Ke(i.sig),c=Ke(i.id),l=Ke(i.pubkey);if(!Te.verify(a,c,l))return null;let d=Ot(e,i.pubkey),g=un(i.content,d),u=JSON.parse(g);if(typeof u!="object"||u===null)return null;let f=u;if(typeof f.pubkey!="string"||f.pubkey!==i.pubkey||typeof f.kind!="number"||typeof f.created_at!="number"||!Array.isArray(f.tags)||typeof f.content!="string")return null;let y=typeof f.id=="string"?f.id:ei({pubkey:f.pubkey,created_at:f.created_at,kind:f.kind,tags:f.tags,content:f.content});return{pubkey:f.pubkey,id:y,kind:f.kind,created_at:f.created_at,tags:f.tags,content:f.content}}catch{return null}}function Af(t){if(typeof t!="string")return;let e=t.replace(/[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069]/g,"").trim().slice(0,64);return e.length>0?e:void 0}async function ti(t){if(!/^[0-9a-f]{64}$/i.test(t.requestId))throw new Error("invalid-request-id");if(!(t.sessionPrivKey instanceof Uint8Array)||t.sessionPrivKey.length!==32)throw new Error("invalid-session-privkey");if(!/^wss:\/\//i.test(t.relayUrl)&&!/^ws:\/\/(localhost|127\.0\.0\.1)([:\/]|$)/i.test(t.relayUrl))throw new Error("invalid-relay-url");if(typeof t.expectedOrigin!="string"||t.expectedOrigin.length===0)throw new Error("invalid-expected-origin");let e=le(Te.getPublicKey(t.sessionPrivKey)),n=Math.max(5e3,Math.min(t.timeout??12e4,6e5)),r=t.requestId.toLowerCase(),o=t.expectedOrigin;return new Promise((i,s)=>{let a=`sa-${Math.random().toString(36).slice(2,12)}`,c=!1,l;try{l=new WebSocket(t.relayUrl)}catch{s(new Error("relay-error"));return}let d=u=>{if(!c){c=!0,clearTimeout(g);try{l.close()}catch{}u()}},g=setTimeout(()=>{d(()=>s(new Error("timeout")))},n);l.onopen=()=>{let u=Math.floor(Date.now()/1e3)-60;l.send(JSON.stringify(["REQ",a,{kinds:[1059],"#p":[e],since:u}]))},l.onmessage=async u=>{if(c)return;let f;try{f=JSON.parse(typeof u.data=="string"?u.data:"")}catch{return}if(!Array.isArray(f)||f[0]!=="EVENT"||f[1]!==a)return;let y=f[2];if(typeof y!="object"||y===null)return;let b=y;if(b.kind!==1059||typeof b.pubkey!="string"||typeof b.content!="string")return;let v=await Bf({pubkey:b.pubkey,content:b.content},t.sessionPrivKey);if(!v||v.kind!==29999)return;let R=v.tags.find(h=>h[0]==="session");if(!R||R[1]!==r)return;let O=v.tags.find(h=>h[0]==="status");if(O?.[1]==="rejected"){d(()=>s(new Error("denied")));return}if(O?.[1]!=="approved"||Math.abs(Date.now()/1e3-v.created_at)>300)return;let I;try{let h=JSON.parse(v.content);if(typeof h!="object"||h===null)return;I=h}catch{return}if(I.type!=="signet-auth-response"||I.requestId!==r||typeof I.authEvent!="object"||I.authEvent===null)return;let _=I.authEvent;if(typeof _.id!="string"||!/^[0-9a-f]{64}$/i.test(_.id)||typeof _.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(_.pubkey)||typeof _.sig!="string"||!/^[0-9a-f]{128}$/i.test(_.sig)||_.kind!==21236||typeof _.created_at!="number"||!Array.isArray(_.tags)||typeof _.content!="string"||_.pubkey.toLowerCase()!==v.pubkey.toLowerCase()||ei({pubkey:_.pubkey,created_at:_.created_at,kind:21236,tags:_.tags,content:_.content})!==_.id.toLowerCase())return;let j=!1;try{let h=Ke(_.sig),m=Ke(_.id),E=Ke(_.pubkey);j=Te.verify(h,m,E)}catch{j=!1}if(!j)return;let Z=_.tags,L=Z.find(h=>Array.isArray(h)&&h[0]==="challenge");if(!L||typeof L[1]!="string"||L[1].toLowerCase()!==r)return;let z=Z.find(h=>Array.isArray(h)&&h[0]==="origin");if(!z||z[1]!==o||Math.abs(Date.now()/1e3-_.created_at)>300)return;let T={id:_.id.toLowerCase(),pubkey:_.pubkey.toLowerCase(),kind:21236,created_at:_.created_at,tags:Z,content:_.content,sig:_.sig},x=Af(I.displayName),p=typeof I.bunkerUri=="string"&&/^bunker:\/\//i.test(I.bunkerUri)?I.bunkerUri:void 0;d(()=>i({pubkey:T.pubkey,authEvent:T,credential:I.credential,...x!==void 0?{displayName:x}:{},...p!==void 0?{bunkerUri:p}:{},createdAt:T.created_at}))},l.onerror=()=>{d(()=>s(new Error("relay-error")))}})}typeof window<"u"&&(window.Signet={verifyAge:kf,waitForAuthResponse:ti});var Ai=gl(Gc(),1),xd=8e3;function Bi(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Ed(){let t=new Uint8Array(32);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}function Bt(t){return t==="dark"?!0:t==="light"?!1:typeof window<"u"&&window.matchMedia("(prefers-color-scheme: dark)").matches}function vd(t){let e=document.createElement("style");e.textContent="#signet-login-dialog::backdrop{background:rgba(0,0,0,0.7)}",document.head.appendChild(e);let n=Bt(t),r=n?"#1a1a2e":"#ffffff",o=n?"#e0e0e0":"#1a1a2e",i=document.createElement("dialog");i.id="signet-login-dialog",i.style.cssText=`border:none;border-radius:16px;padding:32px;max-width:380px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.3);background:${r};color:${o};font-family:system-ui,-apple-system,sans-serif;`,document.body.appendChild(i),i.showModal();let s=()=>Array.from(i.querySelectorAll("button")).filter(u=>{if(u.disabled||!u.isConnected)return!1;let f=window.getComputedStyle(u);return f.display!=="none"&&f.visibility!=="hidden"}),a=()=>document.activeElement instanceof HTMLButtonElement&&i.contains(document.activeElement)?document.activeElement:null,c=0,l=()=>{let u=s();u.length!==0&&(c=Math.min(Math.max(c,0),u.length-1),u[c].focus())},d=u=>{if(!i.isConnected||!i.open)return;let f=u.target;if(f&&(f.tagName==="INPUT"||f.tagName==="TEXTAREA")&&u.isTrusted)return;let y=s();if(y.length===0)return;let b=u.key||u.code,v=u.code||u.key,R=b==="ArrowDown"||v==="ArrowDown"||b==="ArrowRight"||v==="ArrowRight"?1:b==="ArrowUp"||v==="ArrowUp"||b==="ArrowLeft"||v==="ArrowLeft"?-1:0;if(R){u.preventDefault(),u.stopImmediatePropagation();let O=a(),N=O?y.indexOf(O):-1;c=((N>=0?N:c)+R+y.length)%y.length,y[c].focus();return}u.isTrusted||(u.key==="Enter"||u.key===" "||u.code==="Space"||u.code==="Enter"?(u.preventDefault(),u.stopImmediatePropagation(),y[Math.min(Math.max(c,0),y.length-1)].click()):(u.key==="Escape"||u.code==="Escape")&&(u.preventDefault(),u.stopImmediatePropagation(),(i.querySelector('[data-action="back"]')??i.querySelector('[data-action="cancel"],[data-choice="cancel"]'))?.click()))};window.addEventListener("keydown",d,!0);let g=new MutationObserver(()=>{c=0,l()});return g.observe(i,{childList:!0}),l(),{dialog:i,style:e,cleanupNav:()=>{window.removeEventListener("keydown",d,!0),g.disconnect()}}}function Sd(t){t.cleanupNav?.();try{t.dialog.close()}catch{}t.dialog.remove(),t.style.remove()}function se(t,e=!1){return e?"background:#2c3e8f;color:white;border:0;padding:12px 16px;border-radius:8px;cursor:pointer;font-size:0.95rem;width:100%;margin-bottom:8px;text-align:left;display:flex;align-items:center;gap:12px;":`background:transparent;color:${t?"#e0e0e0":"#1a1a2e"};border:1px solid ${t?"#3a3a4e":"#d0d0d0"};padding:12px 16px;border-radius:8px;cursor:pointer;font-size:0.95rem;width:100%;margin-bottom:8px;text-align:left;display:flex;align-items:center;gap:12px;`}function kd(t,e,n){let r=Bt(n),o=r?"#888":"#666",i=fn(),s=sr();return t.dialog.innerHTML=`
14
+ `,document.body.appendChild(y),y.showModal();let b=y.querySelector("#signet-qr");if(b){let R=document.createElement("div");R.style.cssText=`width:200px;height:200px;background:${d?"#2a2a3e":"#f0f0f0"};border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:0.75rem;color:${f};word-break:break-all;padding:12px;`,R.textContent=`signet:verify:${a.slice(0,40)}...`,b.appendChild(R)}let v=new BroadcastChannel("signet-verify-"+o);y.querySelector("#signet-cancel")?.addEventListener("click",()=>{v.close(),y.close(),y.remove(),l.remove(),c({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"cancelled"})}),v.onmessage=async R=>{let O=R.data;if(typeof O!="object"||O===null)return;let N=O;if(N.type!=="signet-verify-response"||N.requestId!==o||!N.credential||typeof N.credential!="object"||!Array.isArray(N.credential.tags))return;let I=N.credential,_=await Ef(I),q=vr(I.tags,"age-range"),j=vr(I.tags,"tier"),Z=vr(I.tags,"entity-type"),L=vr(I.tags,"expires"),z=q?vf(q,r.requiredAgeRange):!1,$=await Sf(I.pubkey,r.verifierCheckUrl),T=$?.confirmed??null,x=$?.method??null,p=r.acceptUnconfirmed||T===!0;y.close(),y.remove(),l.remove(),v.close();let h=j?parseInt(j,10):null,m=L?parseInt(L,10):null,E=Math.floor(Date.now()/1e3),B=m===null||!isNaN(m)&&m>E,w;_?B?z?p||(w=T===!1?"verifier-not-confirmed":"verifier-check-unavailable"):w="age-range-not-met":w="credential-expired":w="invalid-credential",c({verified:_&&B&&z&&p,ageRange:q||null,tier:h!==null&&!isNaN(h)?h:null,entityType:Z||null,credentialId:I.id,verifierPubkey:I.pubkey,verifierConfirmed:T,verifierMethod:x,issuedAt:I.created_at,expiresAt:m!==null&&!isNaN(m)?m:null,error:w})},setTimeout(()=>{y.close(),y.remove(),l.remove(),v.close(),c({verified:!1,ageRange:null,tier:null,entityType:null,credentialId:null,verifierPubkey:null,verifierConfirmed:null,verifierMethod:null,issuedAt:null,expiresAt:null,error:"timeout"})},r.timeout)})}function ei(t){let e=JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content]),n=Qo(new TextEncoder().encode(e));return le(n)}async function Bf(t,e){try{let n=Ot(e,t.pubkey),r=un(t.content,n),o=JSON.parse(r);if(typeof o!="object"||o===null)return null;let i=o;if(i.kind!==13||typeof i.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(i.pubkey)||typeof i.created_at!="number"||!Array.isArray(i.tags)||typeof i.content!="string"||typeof i.id!="string"||!/^[0-9a-f]{64}$/i.test(i.id)||typeof i.sig!="string"||!/^[0-9a-f]{128}$/i.test(i.sig)||ei({pubkey:i.pubkey,created_at:i.created_at,kind:13,tags:i.tags,content:i.content})!==i.id.toLowerCase())return null;let a=Ke(i.sig),c=Ke(i.id),l=Ke(i.pubkey);if(!Te.verify(a,c,l))return null;let d=Ot(e,i.pubkey),g=un(i.content,d),u=JSON.parse(g);if(typeof u!="object"||u===null)return null;let f=u;if(typeof f.pubkey!="string"||f.pubkey!==i.pubkey||typeof f.kind!="number"||typeof f.created_at!="number"||!Array.isArray(f.tags)||typeof f.content!="string")return null;let y=typeof f.id=="string"?f.id:ei({pubkey:f.pubkey,created_at:f.created_at,kind:f.kind,tags:f.tags,content:f.content});return{pubkey:f.pubkey,id:y,kind:f.kind,created_at:f.created_at,tags:f.tags,content:f.content}}catch{return null}}function Af(t){if(typeof t!="string")return;let e=t.replace(/[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069]/g,"").trim().slice(0,64);return e.length>0?e:void 0}async function ti(t){if(!/^[0-9a-f]{64}$/i.test(t.requestId))throw new Error("invalid-request-id");if(!(t.sessionPrivKey instanceof Uint8Array)||t.sessionPrivKey.length!==32)throw new Error("invalid-session-privkey");if(!/^wss:\/\//i.test(t.relayUrl)&&!/^ws:\/\/(localhost|127\.0\.0\.1)([:\/]|$)/i.test(t.relayUrl))throw new Error("invalid-relay-url");if(typeof t.expectedOrigin!="string"||t.expectedOrigin.length===0)throw new Error("invalid-expected-origin");let e=le(Te.getPublicKey(t.sessionPrivKey)),n=Math.max(5e3,Math.min(t.timeout??12e4,6e5)),r=t.requestId.toLowerCase(),o=t.expectedOrigin;return new Promise((i,s)=>{let a=`sa-${Math.random().toString(36).slice(2,12)}`,c=!1,l;try{l=new WebSocket(t.relayUrl)}catch{s(new Error("relay-error"));return}let d=u=>{if(!c){c=!0,clearTimeout(g);try{l.close()}catch{}u()}},g=setTimeout(()=>{d(()=>s(new Error("timeout")))},n);l.onopen=()=>{let u=Math.floor(Date.now()/1e3)-60;l.send(JSON.stringify(["REQ",a,{kinds:[1059],"#p":[e],since:u}]))},l.onmessage=async u=>{if(c)return;let f;try{f=JSON.parse(typeof u.data=="string"?u.data:"")}catch{return}if(!Array.isArray(f)||f[0]!=="EVENT"||f[1]!==a)return;let y=f[2];if(typeof y!="object"||y===null)return;let b=y;if(b.kind!==1059||typeof b.pubkey!="string"||typeof b.content!="string")return;let v=await Bf({pubkey:b.pubkey,content:b.content},t.sessionPrivKey);if(!v||v.kind!==29999)return;let R=v.tags.find(h=>h[0]==="session");if(!R||R[1]!==r)return;let O=v.tags.find(h=>h[0]==="status");if(O?.[1]==="rejected"){d(()=>s(new Error("denied")));return}if(O?.[1]!=="approved"||Math.abs(Date.now()/1e3-v.created_at)>300)return;let I;try{let h=JSON.parse(v.content);if(typeof h!="object"||h===null)return;I=h}catch{return}if(I.type!=="signet-auth-response"||I.requestId!==r||typeof I.authEvent!="object"||I.authEvent===null)return;let _=I.authEvent;if(typeof _.id!="string"||!/^[0-9a-f]{64}$/i.test(_.id)||typeof _.pubkey!="string"||!/^[0-9a-f]{64}$/i.test(_.pubkey)||typeof _.sig!="string"||!/^[0-9a-f]{128}$/i.test(_.sig)||_.kind!==21236||typeof _.created_at!="number"||!Array.isArray(_.tags)||typeof _.content!="string"||_.pubkey.toLowerCase()!==v.pubkey.toLowerCase()||ei({pubkey:_.pubkey,created_at:_.created_at,kind:21236,tags:_.tags,content:_.content})!==_.id.toLowerCase())return;let j=!1;try{let h=Ke(_.sig),m=Ke(_.id),E=Ke(_.pubkey);j=Te.verify(h,m,E)}catch{j=!1}if(!j)return;let Z=_.tags,L=Z.find(h=>Array.isArray(h)&&h[0]==="challenge");if(!L||typeof L[1]!="string"||L[1].toLowerCase()!==r)return;let z=Z.find(h=>Array.isArray(h)&&h[0]==="origin");if(!z||z[1]!==o||Math.abs(Date.now()/1e3-_.created_at)>300)return;let T={id:_.id.toLowerCase(),pubkey:_.pubkey.toLowerCase(),kind:21236,created_at:_.created_at,tags:Z,content:_.content,sig:_.sig},x=Af(I.displayName),p=typeof I.bunkerUri=="string"&&/^bunker:\/\//i.test(I.bunkerUri)?I.bunkerUri:void 0;d(()=>i({pubkey:T.pubkey,authEvent:T,credential:I.credential,...x!==void 0?{displayName:x}:{},...p!==void 0?{bunkerUri:p}:{},createdAt:T.created_at}))},l.onerror=()=>{d(()=>s(new Error("relay-error")))}})}typeof window<"u"&&(window.Signet={verifyAge:kf,waitForAuthResponse:ti});var Ai=gl(Gc(),1),xd=8e3;function Bi(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Ed(){let t=new Uint8Array(32);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}function Bt(t){return t==="dark"?!0:t==="light"?!1:typeof window<"u"&&window.matchMedia("(prefers-color-scheme: dark)").matches}function vd(t){let e=document.createElement("style");e.textContent="#signet-login-dialog::backdrop{background:rgba(0,0,0,0.7)}",document.head.appendChild(e);let n=Bt(t),r=n?"#1a1a2e":"#ffffff",o=n?"#e0e0e0":"#1a1a2e",i=document.createElement("dialog");i.id="signet-login-dialog",i.style.cssText=`border:none;border-radius:16px;padding:32px;max-width:460px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.3);background:${r};color:${o};font-family:system-ui,-apple-system,sans-serif;`,document.body.appendChild(i),i.showModal();let s=()=>Array.from(i.querySelectorAll("button")).filter(u=>{if(u.disabled||!u.isConnected)return!1;let f=window.getComputedStyle(u);return f.display!=="none"&&f.visibility!=="hidden"}),a=()=>document.activeElement instanceof HTMLButtonElement&&i.contains(document.activeElement)?document.activeElement:null,c=0,l=()=>{let u=s();u.length!==0&&(c=Math.min(Math.max(c,0),u.length-1),u[c].focus())},d=u=>{if(!i.isConnected||!i.open)return;let f=u.target;if(f&&(f.tagName==="INPUT"||f.tagName==="TEXTAREA")&&u.isTrusted)return;let y=s();if(y.length===0)return;let b=u.key||u.code,v=u.code||u.key,R=b==="ArrowDown"||v==="ArrowDown"||b==="ArrowRight"||v==="ArrowRight"?1:b==="ArrowUp"||v==="ArrowUp"||b==="ArrowLeft"||v==="ArrowLeft"?-1:0;if(R){u.preventDefault(),u.stopImmediatePropagation();let O=a(),N=O?y.indexOf(O):-1;c=((N>=0?N:c)+R+y.length)%y.length,y[c].focus();return}u.isTrusted||(u.key==="Enter"||u.key===" "||u.code==="Space"||u.code==="Enter"?(u.preventDefault(),u.stopImmediatePropagation(),y[Math.min(Math.max(c,0),y.length-1)].click()):(u.key==="Escape"||u.code==="Escape")&&(u.preventDefault(),u.stopImmediatePropagation(),(i.querySelector('[data-action="back"]')??i.querySelector('[data-action="cancel"],[data-choice="cancel"]'))?.click()))};window.addEventListener("keydown",d,!0);let g=new MutationObserver(()=>{c=0,l()});return g.observe(i,{childList:!0}),l(),{dialog:i,style:e,cleanupNav:()=>{window.removeEventListener("keydown",d,!0),g.disconnect()}}}function Sd(t){t.cleanupNav?.();try{t.dialog.close()}catch{}t.dialog.remove(),t.style.remove()}function se(t,e=!1){return e?"background:#2c3e8f;color:white;border:0;padding:12px 16px;border-radius:8px;cursor:pointer;font-size:0.95rem;width:100%;margin-bottom:8px;text-align:left;display:flex;align-items:center;gap:12px;":`background:transparent;color:${t?"#e0e0e0":"#1a1a2e"};border:1px solid ${t?"#3a3a4e":"#d0d0d0"};padding:12px 16px;border-radius:8px;cursor:pointer;font-size:0.95rem;width:100%;margin-bottom:8px;text-align:left;display:flex;align-items:center;gap:12px;`}function kd(t,e,n){let r=Bt(n),o=r?"#888":"#666",i=fn(),s=sr();return t.dialog.innerHTML=`
15
15
  <h2 style="margin:0 0 8px;font-size:1.3rem;">Sign in to ${Bi(e)}</h2>
16
16
  <p style="margin:0 0 24px;color:${o};font-size:0.9rem;">Choose how you want to sign in. Your keys never leave your control.</p>
17
17
  <div style="display:flex;flex-direction:column;">
@@ -40,7 +40,7 @@ Minimum version required to store current data is: `+i+`.
40
40
  <h2 style="margin:0 0 8px;font-size:1.2rem;">${i?"Open My Signet":"Sign in with Signet"}</h2>
41
41
  <p style="margin:0 0 16px;color:${o};font-size:0.85rem;">${i?"Approve in My Signet and keep that tab open so it can sign for this app.":"Open the link on your phone, or scan the QR if rendered."}</p>
42
42
  <div style="background:${r?"#0f0f1f":"#f5f5f8"};border-radius:8px;padding:16px;margin-bottom:16px;">
43
- <canvas id="signet-login-qr" width="200" height="200" style="display:block;width:200px;height:200px;margin:0 auto 12px;background:#ffffff;border-radius:6px;box-sizing:border-box;"></canvas>
43
+ <canvas id="signet-login-qr" width="360" height="360" style="display:block;width:360px;height:360px;max-width:100%;margin:0 auto 12px;background:#ffffff;border-radius:6px;box-sizing:border-box;"></canvas>
44
44
  <a id="signet-login-open-signet" href="${Bi(l)}" target="_blank" rel="noopener" style="${i?se(r,!0)+"justify-content:center;text-align:center;text-decoration:none;":"display:block;color:#5b6dff;font-size:0.75rem;word-break:break-all;text-decoration:none;"}">${i?"Open My Signet":`${Bi(l.slice(0,80))}\u2026`}</a>
45
45
  </div>
46
46
  <p id="signet-login-status" style="margin:0 0 12px;color:${o};font-size:0.85rem;">${i?"Waiting for My Signet approval\u2026":"Waiting for approval\u2026"}</p>
@@ -48,7 +48,7 @@ Minimum version required to store current data is: `+i+`.
48
48
  <button data-action="back" style="${se(r)}width:auto;flex:0 0 auto;padding:8px 16px;">\u2190 Back</button>
49
49
  <button data-action="cancel" style="${se(r)}width:auto;flex:0 0 auto;padding:8px 16px;">Cancel</button>
50
50
  </div>
51
- `;let d=t.dialog.querySelector("#signet-login-qr");if(d&&Ai.default.toCanvas(d,l,{width:200,margin:1,errorCorrectionLevel:"M",color:{dark:"#0a0418",light:"#ffffff"}}).catch(()=>{}),i&&typeof window<"u")try{window.open(l,"_blank","noopener,noreferrer")}catch{}return new Promise(g=>{let u=!1,f=y=>{u||(u=!0,g(y))};t.dialog.querySelector('[data-action="back"]')?.addEventListener("click",()=>{f(null)}),t.dialog.querySelector('[data-action="cancel"]')?.addEventListener("click",()=>{f(null)}),ti({requestId:e.challenge,relayUrl:e.relayUrl,sessionPrivKey:s,expectedOrigin:e.origin,timeout:e.timeout}).then(y=>{let b=y,v={id:b.authEvent.id,pubkey:b.authEvent.pubkey,kind:21236,created_at:b.authEvent.created_at,tags:b.authEvent.tags,content:b.authEvent.content,sig:b.authEvent.sig},R={pubkey:b.pubkey,authEvent:v};b.displayName&&(R.displayName=b.displayName),b.bunkerUri&&(R.bunkerUri=b.bunkerUri),f(R)}).catch(y=>{let b=t.dialog.querySelector("#signet-login-status");b&&(b.textContent=`\u2717 ${y instanceof Error?y.message:String(y)}`,b.style.color="#d04848")})})}async function Yc(t,e,n){let r=new Ee(e.pubkey,e.authEvent),o="redirect";if(e.bunkerUri){let s=ht(),a=e.pubkey,c=t.dialog.querySelector("#signet-login-status");c&&(c.textContent="Connecting signer...");try{let l=await Pt({uri:e.bunkerUri,clientSecretKey:s,timeoutMs:xd});l.pubkey.toLowerCase()!==a.toLowerCase()?(console.warn("[signet-login] Signet relay upgrade: bunker pubkey mismatch \u2014 continuing identity-only",{connected:l.pubkey,expected:a}),l.close().catch(()=>{})):(r=l,o="bunker")}catch(l){console.warn("[signet-login] Signet relay upgrade: createBunkerSigner failed \u2014 continuing identity-only (auth-only).",l)}}else console.warn("[signet-login] Signet relay login carried no bunkerUri \u2014 auth-only ephemeral (cannot sign). The signer device must have its NIP-46 server enabled to hand back a bunker:// URI.");let i={pubkey:e.pubkey,method:o,signer:r,authEvent:e.authEvent};return e.displayName&&(i.displayName=e.displayName),i}async function Ad(t,e){let n=Bt(e.theme),r=n?"#888":"#666",o=n?"#0f0f1f":"#f5f5f8",i=n?"#e0e0e0":"#1a1a2e";return t.dialog.innerHTML=`
51
+ `;let d=t.dialog.querySelector("#signet-login-qr");if(d&&Ai.default.toCanvas(d,l,{width:360,margin:1,errorCorrectionLevel:"H",color:{dark:"#0a0418",light:"#ffffff"}}).catch(()=>{}),i&&typeof window<"u")try{window.open(l,"_blank","noopener,noreferrer")}catch{}return new Promise(g=>{let u=!1,f=y=>{u||(u=!0,g(y))};t.dialog.querySelector('[data-action="back"]')?.addEventListener("click",()=>{f(null)}),t.dialog.querySelector('[data-action="cancel"]')?.addEventListener("click",()=>{f(null)}),ti({requestId:e.challenge,relayUrl:e.relayUrl,sessionPrivKey:s,expectedOrigin:e.origin,timeout:e.timeout}).then(y=>{let b=y,v={id:b.authEvent.id,pubkey:b.authEvent.pubkey,kind:21236,created_at:b.authEvent.created_at,tags:b.authEvent.tags,content:b.authEvent.content,sig:b.authEvent.sig},R={pubkey:b.pubkey,authEvent:v};b.displayName&&(R.displayName=b.displayName),b.bunkerUri&&(R.bunkerUri=b.bunkerUri),f(R)}).catch(y=>{let b=t.dialog.querySelector("#signet-login-status");b&&(b.textContent=`\u2717 ${y instanceof Error?y.message:String(y)}`,b.style.color="#d04848")})})}async function Yc(t,e,n){let r=new Ee(e.pubkey,e.authEvent),o="redirect";if(e.bunkerUri){let s=ht(),a=e.pubkey,c=t.dialog.querySelector("#signet-login-status");c&&(c.textContent="Connecting signer...");try{let l=await Pt({uri:e.bunkerUri,clientSecretKey:s,timeoutMs:xd});l.pubkey.toLowerCase()!==a.toLowerCase()?(console.warn("[signet-login] Signet relay upgrade: bunker pubkey mismatch \u2014 continuing identity-only",{connected:l.pubkey,expected:a}),l.close().catch(()=>{})):(r=l,o="bunker")}catch(l){console.warn("[signet-login] Signet relay upgrade: createBunkerSigner failed \u2014 continuing identity-only (auth-only).",l)}}else console.warn("[signet-login] Signet relay login carried no bunkerUri \u2014 auth-only ephemeral (cannot sign). The signer device must have its NIP-46 server enabled to hand back a bunker:// URI.");let i={pubkey:e.pubkey,method:o,signer:r,authEvent:e.authEvent};return e.displayName&&(i.displayName=e.displayName),i}async function Ad(t,e){let n=Bt(e.theme),r=n?"#888":"#666",o=n?"#0f0f1f":"#f5f5f8",i=n?"#e0e0e0":"#1a1a2e";return t.dialog.innerHTML=`
52
52
  <h2 style="margin:0 0 8px;font-size:1.2rem;">Paste bunker URI</h2>
53
53
  <p style="margin:0 0 16px;color:${r};font-size:0.85rem;">Connect to your NIP-46 bunker (Heartwood, nsecBunker, or any compatible signer).</p>
54
54
  <textarea id="signet-login-bunker-input" placeholder="bunker://..." rows="3" style="width:100%;background:${o};color:${i};border:1px solid ${n?"#3a3a4e":"#d0d0d0"};border-radius:8px;padding:10px;font-size:0.85rem;font-family:ui-monospace,monospace;box-sizing:border-box;resize:vertical;margin-bottom:12px;"></textarea>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signet-login",
3
- "version": "0.9.14",
3
+ "version": "0.9.15",
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",