From 7f74ec80af8645590efa7a80c17bb8e320273724 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 11 Feb 2024 12:45:33 -0600 Subject: [PATCH] Let signer be undefined if there's no way to sign --- src/actions/accounts.ts | 2 +- src/actions/nostr.ts | 4 ++ src/api/hooks/nostr/useSignerStream.ts | 14 +++--- src/features/nostr/sign.ts | 69 +++----------------------- 4 files changed, 18 insertions(+), 71 deletions(-) diff --git a/src/actions/accounts.ts b/src/actions/accounts.ts index 4b38bab80..2099cb63f 100644 --- a/src/actions/accounts.ts +++ b/src/actions/accounts.ts @@ -134,7 +134,7 @@ const createAccount = (params: Record) => async (dispatch: AppDispatch, getState: () => RootState) => { const { instance } = getState(); const { nostrSignup } = getFeatures(instance); - const pubkey = nostrSignup ? await signer.getPublicKey() : undefined; + const pubkey = (signer && nostrSignup) ? await signer.getPublicKey() : undefined; dispatch({ type: ACCOUNT_CREATE_REQUEST, params }); return api(getState, 'app').post('/api/v1/accounts', params, { diff --git a/src/actions/nostr.ts b/src/actions/nostr.ts index 882717505..21044140b 100644 --- a/src/actions/nostr.ts +++ b/src/actions/nostr.ts @@ -8,6 +8,10 @@ import { verifyCredentials } from './auth'; /** Log in with a Nostr pubkey. */ function nostrLogIn() { return async (dispatch: AppDispatch) => { + if (!signer) { + throw new Error('No Nostr signer available'); + } + const pubkey = await signer.getPublicKey(); const npub = nip19.npubEncode(pubkey); diff --git a/src/api/hooks/nostr/useSignerStream.ts b/src/api/hooks/nostr/useSignerStream.ts index ff41aadac..cc5d6403d 100644 --- a/src/api/hooks/nostr/useSignerStream.ts +++ b/src/api/hooks/nostr/useSignerStream.ts @@ -1,5 +1,5 @@ import { NiceRelay } from 'nostr-machina'; -import { type Event } from 'nostr-tools'; +import { type NostrEvent } from 'nspec'; import { useEffect, useMemo } from 'react'; import { signer } from 'soapbox/features/nostr/sign'; @@ -14,13 +14,13 @@ function useSignerStream() { const pubkey = instance.nostr?.pubkey; const relay = useMemo(() => { - if (relayUrl) { + if (relayUrl && signer) { return new NiceRelay(relayUrl); } - }, [relayUrl]); + }, [relayUrl, !!signer]); - async function handleConnectEvent(event: Event) { - if (!relay || !pubkey) return; + async function handleConnectEvent(event: NostrEvent) { + if (!relay || !pubkey || !signer) return; const decrypted = await signer.nip04!.decrypt(pubkey, event.content); const reqMsg = jsonSchema.pipe(connectRequestSchema).safeParse(decrypted); @@ -45,8 +45,8 @@ function useSignerStream() { relay.send(['EVENT', respEvent]); } - async function handleWalletEvent(event: Event) { - if (!relay || !pubkey) return; + async function handleWalletEvent(event: NostrEvent) { + if (!relay || !pubkey || !signer) return; const decrypted = await signer.nip04!.decrypt(pubkey, event.content); diff --git a/src/features/nostr/sign.ts b/src/features/nostr/sign.ts index 392285901..d33cd926a 100644 --- a/src/features/nostr/sign.ts +++ b/src/features/nostr/sign.ts @@ -1,70 +1,13 @@ -import { - type Event, - type EventTemplate, - generatePrivateKey, - getPublicKey as _getPublicKey, - finishEvent, - nip04 as _nip04, -} from 'nostr-tools'; import { type NostrSigner } from 'nspec'; -import { powWorker } from 'soapbox/workers'; +import { SoapboxSigner } from './SoapboxSigner'; -/** localStorage key for the Nostr private key (if not using NIP-07). */ -const LOCAL_KEY = 'soapbox:nostr:privateKey'; +let signer: NostrSigner | undefined; -/** Get the private key from the browser, or generate one. */ -const getPrivateKey = (): string => { - const local = localStorage.getItem(LOCAL_KEY); - - if (!local) { - const key = generatePrivateKey(); - localStorage.setItem(LOCAL_KEY, key); - return key; - } - - return local; -}; - -/** Get the user's public key from NIP-07, or generate one. */ -async function getPublicKey(): Promise { - return window.nostr ? window.nostr.getPublicKey() : _getPublicKey(getPrivateKey()); +try { + signer = new SoapboxSigner(); +} catch (_) { + // No signer available } -interface SignEventOpts { - pow?: number; -} - -/** Sign an event with NIP-07, or the locally generated key. */ -async function signEvent(template: EventTemplate, opts: SignEventOpts = {}): Promise> { - if (opts.pow) { - const event = await powWorker.mine({ ...template, pubkey: await getPublicKey() }, opts.pow) as Omit, 'sig'>; - return window.nostr ? window.nostr.signEvent(event) as Promise> : finishEvent(event, getPrivateKey()) ; - } else { - return window.nostr ? window.nostr.signEvent(template) as Promise> : finishEvent(template, getPrivateKey()) ; - } -} - -/** Crypto function with NIP-07, or the local key. */ -const nip04 = { - /** Encrypt with NIP-07, or the local key. */ - encrypt: async (pubkey: string, content: string) => { - return window.nostr?.nip04 - ? window.nostr.nip04.encrypt(pubkey, content) - : _nip04.encrypt(getPrivateKey(), pubkey, content); - }, - /** Decrypt with NIP-07, or the local key. */ - decrypt: async (pubkey: string, content: string) => { - return window.nostr?.nip04 - ? window.nostr.nip04.decrypt(pubkey, content) - : _nip04.decrypt(getPrivateKey(), pubkey, content); - }, -}; - -const signer: NostrSigner = { - getPublicKey, - signEvent, - nip04, -}; - export { signer }; \ No newline at end of file