aboutsummaryrefslogtreecommitdiffstats
path: root/src/nostr-login.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/nostr-login.js')
-rw-r--r--src/nostr-login.js33
1 files changed, 32 insertions, 1 deletions
diff --git a/src/nostr-login.js b/src/nostr-login.js
index 077d9a6..b142dd7 100644
--- a/src/nostr-login.js
+++ b/src/nostr-login.js
@@ -38,7 +38,7 @@ import { entropyToMnemonic } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/english';
import { getPublicKey, finalizeEvent, nip44 } from './nostr.js';
import { SimplePool } from 'nostr-tools/pool';
-import { BunkerSigner, parseBunkerInput } from 'nostr-tools/nip46';
+import { BunkerSigner, parseBunkerInput, createNostrConnectURI } from 'nostr-tools/nip46';
import { generateSecretKey } from 'nostr-tools/pure';
const SEED_DTAG = 'coinos:wallet:v1';
@@ -102,6 +102,10 @@ export async function bunkerSigner(uri, { onAuth } = {}) {
});
await signer.connect();
const pubkey = await signer.getPublicKey();
+ return bunkerAdapter(signer, pubkey);
+}
+
+function bunkerAdapter(signer, pubkey) {
return {
kind: 'bunker', pubkey, label: 'remote signer',
signEvent: (e) => signer.signEvent(e),
@@ -113,6 +117,33 @@ export async function bunkerSigner(uri, { onAuth } = {}) {
};
}
+// Client-initiated NIP-46: WE mint a nostrconnect:// URI; a signer app
+// (Amber, nsec.app, …) opens or scans it and connects back to us over the
+// relay — no bunker URL to copy. Returns the URI (render it as a deep link
+// and a QR), a promise that resolves to a signer adapter when the app
+// answers, and a cancel.
+export function nostrConnect({ relays = ['wss://relay.coinos.io', 'wss://nos.lol'], timeoutMs = 180_000 } = {}) {
+ const local = generateSecretKey();
+ const secret = hex.encode(crypto.getRandomValues(new Uint8Array(16)));
+ const uri = createNostrConnectURI({
+ clientPubkey: getPublicKey(local),
+ relays,
+ secret,
+ name: 'coinos',
+ url: 'https://v3.coinos.io',
+ perms: ['get_public_key', 'sign_event', 'nip44_encrypt', 'nip44_decrypt'],
+ });
+ const controller = new AbortController();
+ const timer = setTimeout(() => controller.abort(new Error('signer connect timed out')), timeoutMs);
+ const ready = (async () => {
+ const signer = await BunkerSigner.fromURI(local, uri, {}, controller.signal);
+ const pubkey = await signer.getPublicKey();
+ return bunkerAdapter(signer, pubkey);
+ })();
+ ready.finally(() => clearTimeout(timer)).catch(() => {});
+ return { uri, ready, cancel: (reason) => controller.abort(reason || new Error('cancelled')) };
+}
+
// A pasted key: signing happens locally, and the key is never persisted.
export function keySigner(sk) {
const pubkey = getPublicKey(sk);