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,17 +71,29 @@
</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>
<small>
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!
</small>
</div> </div>
<small> {#if hasExtension}
Your private key is a string of random letters and numbers that allow you to prove <div class="flex flex-col gap-1">
you own your account. Write it down and keep it secret! <strong>Public Key</strong>
</small> <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> </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>

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)
}
}) })