mirror of
https://github.com/coracle-social/coracle.git
synced 2024-10-02 18:00:52 +00:00
Add ndk and nsecbunker login
This commit is contained in:
parent
8b3de91408
commit
a2157a392c
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -38,6 +38,7 @@
|
||||
"@capacitor/ios": "^4.7.3",
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@nostr-dev-kit/ndk": "^0.7.0",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"classnames": "^2.3.2",
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {nip19, nip04, getPublicKey, getEventHash, signEvent} from "nostr-tools"
|
||||
import {nip19, nip04, getPublicKey, getEventHash, signEvent, generatePrivateKey} from "nostr-tools"
|
||||
import NDK, {NDKEvent, NDKNip46Signer, NDKPrivateKeySigner} from "@nostr-dev-kit/ndk"
|
||||
import {get} from "svelte/store"
|
||||
import {error} from "src/util/logger"
|
||||
import {synced} from "src/util/misc"
|
||||
@ -6,22 +7,56 @@ import {synced} from "src/util/misc"
|
||||
const method = synced("agent/keys/method")
|
||||
const pubkey = synced("agent/keys/pubkey")
|
||||
const privkey = synced("agent/keys/privkey")
|
||||
const bunkerKey = synced("agent/keys/bunkerKey")
|
||||
const getExtension = () => (window as {nostr?: any}).nostr
|
||||
const canSign = () => ["privkey", "extension"].includes(get(method))
|
||||
const canSign = () => ["bunker", "privkey", "extension"].includes(get(method))
|
||||
|
||||
// Validate the key before setting it to state by encoding it using bech32.
|
||||
// This will error if invalid (this works whether it's a public or a private key)
|
||||
const validate = key => nip19.npubEncode(key)
|
||||
|
||||
let ndk, remoteSigner
|
||||
|
||||
const prepareNdk = async (token?: string) => {
|
||||
const localSigner = new NDKPrivateKeySigner(get(bunkerKey))
|
||||
|
||||
ndk = new NDK({
|
||||
explicitRelayUrls: ["wss://relay.f7z.io", "wss://relay.damus.io", "wss://relay.nsecbunker.com"],
|
||||
})
|
||||
|
||||
remoteSigner = new NDKNip46Signer(ndk, get(pubkey), localSigner)
|
||||
remoteSigner.token = token
|
||||
|
||||
ndk.signer = remoteSigner
|
||||
|
||||
await ndk.connect(5000)
|
||||
await remoteSigner.blockUntilReady()
|
||||
}
|
||||
|
||||
const getNDK = async () => {
|
||||
if (!ndk) {
|
||||
await prepareNdk()
|
||||
}
|
||||
|
||||
return ndk
|
||||
}
|
||||
|
||||
const login = ($method, key) => {
|
||||
method.set($method)
|
||||
|
||||
pubkey.set(null)
|
||||
privkey.set(null)
|
||||
bunkerKey.set(null)
|
||||
|
||||
if ($method === "privkey") {
|
||||
privkey.set(key)
|
||||
pubkey.set(getPublicKey(key))
|
||||
} else {
|
||||
privkey.set(null)
|
||||
} else if (["pubkey", "extension"].includes($method)) {
|
||||
pubkey.set(key)
|
||||
} else if ($method === "bunker") {
|
||||
pubkey.set(key.pubkey)
|
||||
bunkerKey.set(generatePrivateKey())
|
||||
prepareNdk(key.token)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,12 +66,20 @@ const clear = () => {
|
||||
privkey.set(null)
|
||||
}
|
||||
|
||||
const sign = event => {
|
||||
const sign = async event => {
|
||||
const $method = get(method)
|
||||
|
||||
event.pubkey = get(pubkey)
|
||||
event.id = getEventHash(event)
|
||||
|
||||
if ($method === "bunker") {
|
||||
const ndkEvent = new NDKEvent(await getNDK(), event)
|
||||
|
||||
await ndkEvent.sign(remoteSigner)
|
||||
|
||||
return ndkEvent.rawEvent()
|
||||
}
|
||||
|
||||
if ($method === "privkey") {
|
||||
return Object.assign(event, {
|
||||
sig: signEvent(event, get(privkey)),
|
||||
@ -53,6 +96,23 @@ const sign = event => {
|
||||
const getCrypt = () => {
|
||||
const $method = get(method)
|
||||
|
||||
if ($method === "bunker") {
|
||||
return {
|
||||
encrypt: async (pubkey, message) => {
|
||||
const ndk = await getNDK()
|
||||
const user = ndk.getUser({hexpubkey: pubkey})
|
||||
|
||||
return ndk.signer.encrypt(user, message)
|
||||
},
|
||||
decrypt: async (pubkey, message) => {
|
||||
const ndk = await getNDK()
|
||||
const user = ndk.getUser({hexpubkey: pubkey})
|
||||
|
||||
return ndk.signer.decrypt(user, message)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ($method === "privkey") {
|
||||
const $privkey = get(privkey)
|
||||
|
||||
@ -104,6 +164,7 @@ export default {
|
||||
method,
|
||||
pubkey,
|
||||
privkey,
|
||||
bunkerKey,
|
||||
canSign,
|
||||
validate,
|
||||
login,
|
||||
|
@ -4,7 +4,9 @@
|
||||
import ChatEdit from "src/app/views/ChatEdit.svelte"
|
||||
import LoginConnect from "src/app/views/LoginConnect.svelte"
|
||||
import LoginPrivKey from "src/app/views/LoginPrivKey.svelte"
|
||||
import LoginAdvanced from "src/app/views/LoginAdvanced.svelte"
|
||||
import LoginPubKey from "src/app/views/LoginPubKey.svelte"
|
||||
import LoginBunker from "src/app/views/LoginBunker.svelte"
|
||||
import Onboarding from "src/app/views/Onboarding.svelte"
|
||||
import NoteCreate from "src/app/views/NoteCreate.svelte"
|
||||
import NoteDelete from "src/app/views/NoteDelete.svelte"
|
||||
@ -50,8 +52,12 @@
|
||||
<ChatEdit {...m} />
|
||||
{:else if m.type === "login/privkey"}
|
||||
<LoginPrivKey />
|
||||
{:else if m.type === "login/advanced"}
|
||||
<LoginAdvanced />
|
||||
{:else if m.type === "login/pubkey"}
|
||||
<LoginPubKey />
|
||||
{:else if m.type === "login/bunker"}
|
||||
<LoginBunker />
|
||||
{:else if m.type === "login/connect"}
|
||||
<LoginConnect />
|
||||
{:else if m.type === "person/feed"}
|
||||
|
@ -32,8 +32,6 @@
|
||||
const links = getLinks(shortContent)
|
||||
const extraLinks = without(links, getLinks(fullContent))
|
||||
|
||||
console.log(fullContent, shortContent)
|
||||
|
||||
export const isNewline = i =>
|
||||
!shortContent[i] ||
|
||||
shortContent[i].type === NEWLINE ||
|
||||
|
@ -25,8 +25,8 @@
|
||||
modal.push({type: "onboarding", stage: "intro"})
|
||||
}
|
||||
|
||||
const pubkeyLogIn = () => {
|
||||
modal.push({type: "login/pubkey"})
|
||||
const advancedLogIn = () => {
|
||||
modal.push({type: "login/advanced"})
|
||||
}
|
||||
|
||||
if (user.getPubkey()) {
|
||||
@ -56,7 +56,7 @@
|
||||
>Log In</Anchor>
|
||||
<Anchor class="w-32 text-center" theme="button" on:click={signUp}>Sign Up</Anchor>
|
||||
</div>
|
||||
<Anchor on:click={pubkeyLogIn}>
|
||||
<Anchor on:click={advancedLogIn}>
|
||||
<i class="fa fa-cogs" /> Advanced Login
|
||||
</Anchor>
|
||||
</div>
|
||||
|
42
src/app/views/LoginAdvanced.svelte
Normal file
42
src/app/views/LoginAdvanced.svelte
Normal file
@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
import {modal} from "src/partials/state"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Card from "src/partials/Card.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
|
||||
const BUNKER_URL = "https://nsecbunker.com"
|
||||
|
||||
const pubkeyLogIn = () => {
|
||||
modal.push({type: "login/pubkey"})
|
||||
}
|
||||
|
||||
const bunkerLogIn = () => {
|
||||
modal.push({type: "login/bunker"})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Content size="lg">
|
||||
<Content class="text-center">
|
||||
<Heading>Advanced Login</Heading>
|
||||
<p>Find more options for logging in to nostr below.</p>
|
||||
</Content>
|
||||
<Content gap="gap-4">
|
||||
<Card interactive on:click={pubkeyLogIn}>
|
||||
<Content gap="gap-4">
|
||||
<h2 class="text-lg font-bold">Log in with public key</h2>
|
||||
<p>Use this to access a read-only view of your account (or someone else's).</p>
|
||||
</Content>
|
||||
</Card>
|
||||
<Card interactive on:click={bunkerLogIn}>
|
||||
<Content gap="gap-4">
|
||||
<h2 class="text-lg font-bold">Log in with Nsec Bunker</h2>
|
||||
<p>
|
||||
Keep your keys secure by storing them in a <Anchor external href={BUNKER_URL}
|
||||
>bunker</Anchor
|
||||
>.
|
||||
</p>
|
||||
</Content>
|
||||
</Card>
|
||||
</Content>
|
||||
</Content>
|
43
src/app/views/LoginBunker.svelte
Normal file
43
src/app/views/LoginBunker.svelte
Normal file
@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import {toHex} from "src/util/nostr"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
import keys from "src/agent/keys"
|
||||
import {toast} from "src/partials/state"
|
||||
import {login} from "src/app/state"
|
||||
|
||||
let input = ""
|
||||
|
||||
const logIn = () => {
|
||||
const [npub, token] = input.split("#")
|
||||
const pubkey = npub.startsWith("npub") ? toHex(npub) : npub
|
||||
|
||||
try {
|
||||
keys.validate(pubkey)
|
||||
} catch (e) {
|
||||
toast.show("error", "Sorry, but that's an invalid public key.")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
login("bunker", {pubkey, token})
|
||||
}
|
||||
</script>
|
||||
|
||||
<Content size="lg" class="text-center">
|
||||
<Heading>Login with NsecBunker</Heading>
|
||||
<p>
|
||||
To log in remotely, enter your nsec bunker token or pubkey below. If you're not using a token,
|
||||
you'll need to approve authorization requests in your bunker's admin interface.
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-grow">
|
||||
<Input bind:value={input} placeholder="npub...">
|
||||
<i slot="before" class="fa fa-key" />
|
||||
</Input>
|
||||
</div>
|
||||
<Anchor theme="button" on:click={logIn}>Log In</Anchor>
|
||||
</div>
|
||||
</Content>
|
@ -12,7 +12,7 @@
|
||||
import keys from "src/agent/keys"
|
||||
import {toast} from "src/partials/state"
|
||||
|
||||
const {pubkey, privkey} = keys
|
||||
const {pubkey, privkey, bunkerKey} = keys
|
||||
const nip07 = "https://github.com/nostr-protocol/nips/blob/master/07.md"
|
||||
const keypairUrl = "https://www.cloudflare.com/learning/ssl/how-does-public-key-encryption-work/"
|
||||
|
||||
@ -87,6 +87,21 @@
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $bunkerKey}
|
||||
<div class="flex flex-col gap-1">
|
||||
<strong>Bunker Key</strong>
|
||||
<Input disabled type="password" value={$bunkerKey}>
|
||||
<button
|
||||
slot="after"
|
||||
class="fa-solid fa-copy cursor-pointer"
|
||||
on:click={() => copyKey("bunker", $bunkerKey)} />
|
||||
</Input>
|
||||
<p class="text-sm text-gray-1">
|
||||
Your bunker key is used to authorize Coracle with your nsec bunker to sign events on
|
||||
your behalf. Save this if you would like to log in elsewhere without re-authorizing.
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Content>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user