NIP-07 (window.nostr) support.

This commit is contained in:
fiatjaf 2022-11-27 12:45:05 -03:00
parent 756924c1bb
commit 54f8ecb2fd
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
5 changed files with 61 additions and 18 deletions

View File

@ -45,6 +45,7 @@
trying to find you on nostr. trying to find you on nostr.
</p> </p>
</div> </div>
{#if $user.privkey}
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<strong>Private Key</strong> <strong>Private Key</strong>
<Input disabled type="password" value={$user.privkey}> <Input disabled type="password" value={$user.privkey}>
@ -57,6 +58,7 @@
using <Anchor external href={delegationUrl}>delegation keys</Anchor> instead. using <Anchor external href={delegationUrl}>delegation keys</Anchor> instead.
</p> </p>
</div> </div>
{/if}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
<script> <script>
import {onMount} from 'svelte';
import {navigate} from 'svelte-routing' import {navigate} from 'svelte-routing'
import {generatePrivateKey} from 'nostr-tools' import {generatePrivateKey, getPublicKey} from 'nostr-tools'
import {copyToClipboard} from "src/util/html" import {copyToClipboard} from "src/util/html"
import Anchor from "src/partials/Anchor.svelte" import Anchor from "src/partials/Anchor.svelte"
import Input from "src/partials/Input.svelte" import Input from "src/partials/Input.svelte"
@ -8,23 +9,43 @@
import {dispatch} from "src/state/dispatch" import {dispatch} from "src/state/dispatch"
import {relays} from "src/state/nostr" import {relays} from "src/state/nostr"
let privKey = '' let privkey = ''
let pubkey = ''
let hasExtension = false
onMount(() => {
setTimeout(() => {
hasExtension = !!window.nostr
}, 1000)
})
const copyKey = () => { const copyKey = () => {
copyToClipboard(privKey) copyToClipboard(privkey)
toast.show("info", "Your private key has been copied to the clipboard.") toast.show("info", "Your private key has been copied to the clipboard.")
} }
const generateKey = () => { const generateKey = () => {
privKey = generatePrivateKey() privkey = generatePrivateKey()
toast.show("info", "Your private key has been re-generated.") toast.show("info", "Your private key has been re-generated.")
} }
const getKeyFromExtension = async () => {
pubkey = await window.nostr.getPublicKey()
privkey = ''
}
const logIn = async () => { const logIn = async () => {
if (!privKey.match(/[a-z0-9]{64}/)) { if (!privkey.match(/[a-z0-9]{64}/) && pubkey === '') {
toast.show("error", "Sorry, but that's an invalid private key.") toast.show("error", "Sorry, but that's an invalid private key.")
} else if (!pubkey.match(/[a-z0-9]{64}/)) {
toast.show("error", "Sorry, but that's an invalid public key.")
} else { } else {
const {found} = await dispatch("account/init", privKey) // Generate a public key from the private key
if (!pubkey && privkey) {
pubkey = getPublicKey(privkey)
}
const {found} = await dispatch("account/init", { privkey, pubkey })
if ($relays.length === 0) { if ($relays.length === 0) {
navigate('/settings/relays') navigate('/settings/relays')
@ -50,18 +71,30 @@
</small> </small>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<strong>Private Key</strong> <strong>Private Key</strong>
<Input type="password" bind:value={privKey} placeholder="Enter your private key"> <Input type="password" bind:value={privkey} placeholder="Enter your private key">
<i slot="after" class="fa-solid fa-copy" on:click={copyKey} /> <i slot="after" class="fa-solid fa-copy" on:click={copyKey} />
</Input> </Input>
<Anchor on:click={generateKey} class="text-right"> <Anchor on:click={generateKey} class="text-right">
<small>Generate new key</small> <small>Generate new key</small>
</Anchor> </Anchor>
</div>
<small> <small>
Your private key is a string of random letters and numbers that allow you to prove Your private key is a string of random letters and numbers that allow you to prove
you own your account. Write it down and keep it secret! you own your account. Write it down and keep it secret!
</small> </small>
</div> </div>
{#if hasExtension}
<div class="flex flex-col gap-1">
<strong>Public Key</strong>
<Input readonly bind:value={pubkey} placeholder="Your public key will show up here" />
<Anchor on:click={getKeyFromExtension} class="text-right">
<small>Get from extension</small>
</Anchor>
<small>
You can also use a <Anchor href="https://github.com/nostr-protocol/nips/blob/master/07.md" external>compatible browser extension</Anchor> to sign events without having to paste your private key here.
</small>
</div>
{/if}
</div>
<Anchor class="text-center" type="button" on:click={logIn}>Log In</Anchor> <Anchor class="text-center" type="button" on:click={logIn}>Log In</Anchor>
</div> </div>
</div> </div>

View File

@ -1,5 +1,4 @@
import {identity, last, without} from 'ramda' import {identity, last, without} from 'ramda'
import {getPublicKey} from 'nostr-tools'
import {get} from 'svelte/store' import {get} from 'svelte/store'
import {first, defmulti} from "hurdak/lib/hurdak" import {first, defmulti} from "hurdak/lib/hurdak"
import {user} from "src/state/user" import {user} from "src/state/user"
@ -13,10 +12,7 @@ import {ensureAccounts} from 'src/state/app'
export const dispatch = defmulti("dispatch", identity) export const dispatch = defmulti("dispatch", identity)
dispatch.addMethod("account/init", async (topic, privkey) => { dispatch.addMethod("account/init", async (topic, { privkey, pubkey }) => {
// Generate a public key
const pubkey = getPublicKey(privkey)
// Set what we know about the user to our store // Set what we know about the user to our store
user.set({name: pubkey.slice(0, 8), privkey, pubkey}) user.set({name: pubkey.slice(0, 8), privkey, pubkey})

View File

@ -84,8 +84,16 @@ nostr.login = privkey => {
nostr._privkey = privkey nostr._privkey = privkey
} }
nostr.pubkeyLogin = pubkey => {
nostr.registerSigningFunction( async (event) => {
const {sig} = await window.nostr.signEvent(event)
return sig
})
nostr._pubkey = pubkey
}
nostr.event = (kind, content = '', tags = []) => { nostr.event = (kind, content = '', tags = []) => {
const pubkey = getPublicKey(nostr._privkey) const pubkey = nostr._pubkey || getPublicKey(nostr._privkey)
const createdAt = Math.round(new Date().valueOf() / 1000) const createdAt = Math.round(new Date().valueOf() / 1000)
return {kind, content, tags, pubkey, created_at: createdAt} return {kind, content, tags, pubkey, created_at: createdAt}

View File

@ -8,6 +8,10 @@ user.subscribe($user => {
setLocalJson("coracle/user", $user) setLocalJson("coracle/user", $user)
// Keep nostr in sync // Keep nostr in sync
nostr.login($user?.privkey) if ($user?.privkey) {
nostr.login($user.privkey)
} else if ($user?.pubkey) {
nostr.pubkeyLogin($user.pubkey)
}
}) })