From 9b84454f53ff248a1390e9ec7a676093f7669697 Mon Sep 17 00:00:00 2001

From: fsociety

Date: Wed, 2 Oct 2024 00:33:36 +0200

Subject: [PATCH] feat: integrate nostr-login for user authentication

This commit integrates nostr-login for user authentication and removes the previous method. It includes changes in the package.json, Navbar.svelte, users.ts and the creation of a new file nostr-login/index.ts. This new method provides a more streamlined and efficient user authentication process.

---

package.json | 1 +

src/lib/components/Navbar.svelte | 45 +++++++++++++++++----------------------------

src/lib/signers/nostr-login/index.ts | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

src/lib/stores/users.ts | 72 +++++++++++++++++++++++-------------------------------------------------

src/lib/wrappers/Navbar.svelte | 21 ++-------------------

yarn.lock | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------

6 files changed, 240 insertions(+), 109 deletions(-)

create mode 100644 src/lib/signers/nostr-login/index.ts

diff --git a/package.json b/package.json

index 8db6542..2539883 100644

--- a/package.json

+++ b/package.json

@@ -34,6 +34,7 @@

"eslint-config-prettier": "^9.1.0",

"eslint-plugin-prettier": "^5.2.1",

"eslint-plugin-svelte": "^2.44.0",

+ "nostr-login": "^1.6.6",

"postcss": "^8.4.47",

"prettier": "^3.3.3",

"prettier-plugin-svelte": "^3.2.6",

diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte

index e66e96d..d5d3d3c 100644

--- a/src/lib/components/Navbar.svelte

+++ b/src/lib/components/Navbar.svelte

@@ -1,13 +1,28 @@

@@ -35,34 +50,8 @@

class="menu dropdown-content z-[1] -mr-4 rounded-box bg-base-400 p-2 shadow"

>

  • -

    -

    -

  • -

    -

    - on:click={() => {

    - logout()

    - }}>Logout

    - >

    -

  • - {:else if nip07_plugin === undefined}

    -

    - {:else if nip07_plugin}

    -

    - on:click={() => {

    - login_function()

    - }}

    - class="btn btn-ghost btn-sm normal-case">Login

    - >

    - {:else}

    -

    - on:click={() => {

    - singup_function()

    - }}

    - class="btn btn-ghost btn-sm normal-case">Sign up

    - >

    {/if}

    diff --git a/src/lib/signers/nostr-login/index.ts b/src/lib/signers/nostr-login/index.ts

    new file mode 100644

    index 0000000..563a8db

    --- /dev/null

    +++ b/src/lib/signers/nostr-login/index.ts

    @@ -0,0 +1,111 @@

    +import NDK, { NDKUser, NDKRelay } from '@nostr-dev-kit/ndk'

    +import type { NDKSigner, NostrEvent } from '@nostr-dev-kit/ndk'

    +

    +export class NDKNostrLoginSigner implements NDKSigner {

    + private _userPromise: Promise | undefined

    +

    + blockUntilReady(): Promise {

    + return new Promise((resolve) => {

    + const eventHandler = (event: Event) => {

    + document.removeEventListener('nlAuth', eventHandler)

    + const customEvent = event as CustomEvent<{ pubkey: string }>

    + resolve(new NDKUser({ pubkey: customEvent.detail.pubkey }))

    + }

    +

    + document.addEventListener('nlAuth', eventHandler)

    + })

    + }

    +

    + user(): Promise {

    + if (!this._userPromise) {

    + this._userPromise = this.blockUntilReady()

    + }

    +

    + return this._userPromise

    + }

    +

    + async sign(event: NostrEvent): Promise {

    + if (!window.nostr) {

    + throw new Error('window.nostr is undefined')

    + }

    + const signedEvent = await window.nostr.signEvent(event)

    + return signedEvent.sig

    + }

    +

    + async relays?(ndk?: NDK): Promise {

    + const relays = (await window.nostr?.getRelays?.()) || {}

    +

    + const activeRelays: string[] = []

    + for (const url of Object.keys(relays)) {

    + // Currently only respects relays that are both readable and writable.

    + if (relays[url].read && relays[url].write) {

    + activeRelays.push(url)

    + }

    + }

    + return activeRelays.map(

    + (url) => new NDKRelay(url, ndk?.relayAuthDefaultPolicy, ndk)

    + )

    + }

    +

    + async encrypt(

    + recipient: NDKUser,

    + value: string,

    + type: string = 'nip04'

    + ): Promise {

    + if (type === 'nip44') {

    + return this.nip44Encrypt(recipient, value)

    + } else {

    + return this.nip04Encrypt(recipient, value)

    + }

    + }

    +

    + async decrypt(

    + sender: NDKUser,

    + value: string,

    + type: string = 'nip04'

    + ): Promise {

    + if (type === 'nip44') {

    + return this.nip44Decrypt(sender, value)

    + } else {

    + return this.nip04Decrypt(sender, value)

    + }

    + }

    +

    + private async nip44Encrypt(

    + recipient: NDKUser,

    + value: string

    + ): Promise {

    + // @ts-expect-error window.nostr.nip44 is not defined in the type definitions

    + if (!window.nostr || !window.nostr.nip44) {

    + throw new Error('window.nostr or window.nostr.nip44 is undefined')

    + }

    + // @ts-expect-error window.nostr.nip44 is not defined in the type definitions

    + return await window.nostr.nip44.encrypt(recipient.pubkey, value)

    + }

    +

    + private async nip04Encrypt(

    + recipient: NDKUser,

    + value: string

    + ): Promise {

    + if (!window.nostr || !window.nostr.nip04) {

    + throw new Error('window.nostr or window.nostr.nip04 is undefined')

    + }

    + return await window.nostr.nip04.encrypt(recipient.pubkey, value)

    + }

    +

    + private async nip44Decrypt(sender: NDKUser, value: string): Promise {

    + // @ts-expect-error window.nostr.nip44 is not defined in the type definitions

    + if (!window.nostr || !window.nostr.nip44) {

    + throw new Error('window.nostr or window.nostr.nip44 is undefined')

    + }

    + // @ts-expect-error window.nostr.nip44 is not defined in the type definitions

    + return await window.nostr.nip44.decrypt(sender.pubkey, value)

    + }

    +

    + private async nip04Decrypt(sender: NDKUser, value: string): Promise {

    + if (!window.nostr || !window.nostr.nip04) {

    + throw new Error('window.nostr or window.nostr.nip04 is undefined')

    + }

    + return await window.nostr.nip04.decrypt(sender.pubkey, value)

    + }

    +}

    diff --git a/src/lib/stores/users.ts b/src/lib/stores/users.ts

    index 608ffed..40ed744 100644

    --- a/src/lib/stores/users.ts

    +++ b/src/lib/stores/users.ts

    @@ -2,13 +2,12 @@ import {

    defaults as user_defaults,

    type UserObject,

    } from '$lib/components/users/type'

    -import {

    - getRelayListForUser,

    - NDKNip07Signer,

    - NDKRelayList,

    -} from '@nostr-dev-kit/ndk'

    +import { getRelayListForUser, NDKRelayList } from '@nostr-dev-kit/ndk'

    import { get, writable, type Unsubscriber, type Writable } from 'svelte/store'

    import { ndk } from './ndk'

    +import { init as initNostrLogin } from 'nostr-login'

    +import { NDKNostrLoginSigner } from '$lib/signers/nostr-login'

    +import type { NostrLoginAuthOptions } from 'nostr-login/dist/types'

    export const users: { [hexpubkey: string]: Writable } = {}

    @@ -85,31 +84,7 @@ export const returnUser = async (hexpubkey: string): Promise => {

    })

    }

    -// nip07_plugin is set in Navbar component

    -export const nip07_plugin: Writable = writable(undefined)

    -

    -export const checkForNip07Plugin = () => {

    - if (window.nostr) {

    - nip07_plugin.set(true)

    - if (localStorage.getItem('nip07pubkey')) login()

    - } else {

    - let timerId: NodeJS.Timeout | undefined = undefined

    - const intervalId = setInterval(() => {

    - if (window.nostr) {

    - clearTimeout(timerId)

    - clearInterval(intervalId)

    - nip07_plugin.set(true)

    - if (localStorage.getItem('nip07pubkey')) login()

    - }

    - }, 100)

    - timerId = setTimeout(() => {

    - clearInterval(intervalId)

    - nip07_plugin.set(false)

    - }, 5000)

    - }

    -}

    -

    -const signer = new NDKNip07Signer(2000)

    +const signer = new NDKNostrLoginSigner()

    export const logged_in_user: Writable =

    writable(undefined)

    @@ -118,24 +93,24 @@ export const login = async (): Promise => {

    return new Promise(async (res, rej) => {

    const user = get(logged_in_user)

    if (user) return res()

    - if (get(nip07_plugin)) {

    - try {

    - const ndk_user = await signer.blockUntilReady()

    - localStorage.setItem('nip07pubkey', ndk_user.pubkey)

    - logged_in_user.set({

    - ...user_defaults,

    - hexpubkey: ndk_user.pubkey,

    - })

    - ndk.signer = signer

    - ensureUser(ndk_user.pubkey).subscribe((user) => {

    - logged_in_user.set({ ...user })

    - })

    - return res()

    - } catch (e) {

    - alert(e)

    - rej()

    - }

    - } else {

    + try {

    + ndk.signer = signer

    + initNostrLogin({

    + onAuth: async (_npub: string, options: NostrLoginAuthOptions) => {

    + const pubkey =

    + typeof options.pubkey === 'string' ? options.pubkey : ''

    + logged_in_user.set({

    + ...user_defaults,

    + hexpubkey: pubkey,

    + })

    + ensureUser(pubkey).subscribe((user) => {

    + logged_in_user.set({ ...user })

    + })

    + return res()

    + },

    + })

    + } catch (e) {

    + alert(e)

    rej()

    }

    })

    @@ -143,7 +118,6 @@ export const login = async (): Promise => {

    export const logout = async (): Promise => {

    logged_in_user.set(undefined)

    - localStorage.removeItem('nip07pubkey')

    ndk.signer = undefined

    }

    diff --git a/src/lib/wrappers/Navbar.svelte b/src/lib/wrappers/Navbar.svelte

    index 4b63bc7..1f7146f 100644

    --- a/src/lib/wrappers/Navbar.svelte

    +++ b/src/lib/wrappers/Navbar.svelte

    @@ -1,23 +1,6 @@

    -

    - logged_in_user={$logged_in_user}

    - nip07_plugin={$nip07_plugin}

    - login_function={login}

    - {singup_function}

    -/>

    +

    diff --git a/yarn.lock b/yarn.lock

    index f75be09..f976ab4 100644

    --- a/yarn.lock

    +++ b/yarn.lock

    @@ -566,6 +566,13 @@ __metadata:

    languageName: node

    linkType: hard

    +"@noble/ciphers@npm:0.2.0":

    + version: 0.2.0

    + resolution: "@noble/ciphers@npm:0.2.0"

    + checksum: 10c0/57dea65c32741df20a1ac24f365d616a558527109d778c1bec877f20b28875a26b80097bce51ae19529f3792ccf8285fe73839ff404733e32a27a6ebf60edd2c

    + languageName: node

    + linkType: hard

    +

    "@noble/ciphers@npm:^0.5.1":

    version: 0.5.3

    resolution: "@noble/ciphers@npm:0.5.3"

    @@ -573,6 +580,15 @@ __metadata:

    languageName: node

    linkType: hard

    +"@noble/curves@npm:1.1.0, @noble/curves@npm:~1.1.0":

    + version: 1.1.0

    + resolution: "@noble/curves@npm:1.1.0"

    + dependencies:

    + "@noble/hashes": "npm:1.3.1"

    + checksum: 10c0/81115c3ebfa7e7da2d7e18d44d686f98dc6d35dbde3964412c05707c92d0994a01545bc265d5c0bc05c8c49333f75b99c9acef6750f5a79b3abcc8e0546acf88

    + languageName: node

    + linkType: hard

    +

    "@noble/curves@npm:1.2.0":

    version: 1.2.0

    resolution: "@noble/curves@npm:1.2.0"

    @@ -582,7 +598,7 @@ __metadata:

    languageName: node

    linkType: hard

    -"@noble/curves@npm:^1.4.0":

    +"@noble/curves@npm:^1.4.0, @noble/curves@npm:^1.6.0":

    version: 1.6.0

    resolution: "@noble/curves@npm:1.6.0"

    dependencies:

    @@ -591,15 +607,6 @@ __metadata:

    languageName: node

    linkType: hard

    -"@noble/curves@npm:~1.1.0":

    - version: 1.1.0

    - resolution: "@noble/curves@npm:1.1.0"

    - dependencies:

    - "@noble/hashes": "npm:1.3.1"

    - checksum: 10c0/81115c3ebfa7e7da2d7e18d44d686f98dc6d35dbde3964412c05707c92d0994a01545bc265d5c0bc05c8c49333f75b99c9acef6750f5a79b3abcc8e0546acf88

    - languageName: node

    - linkType: hard

    -

    "@noble/hashes@npm:1.3.1":

    version: 1.3.1

    resolution: "@noble/hashes@npm:1.3.1"

    @@ -614,7 +621,7 @@ __metadata:

    languageName: node

    linkType: hard

    -"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.3.1":

    +"@noble/hashes@npm:1.5.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.5.0":

    version: 1.5.0

    resolution: "@noble/hashes@npm:1.5.0"

    checksum: 10c0/1b46539695fbfe4477c0822d90c881a04d4fa2921c08c552375b444a48cac9930cb1ee68de0a3c7859e676554d0f3771999716606dc4d8f826e414c11692cdd9

    @@ -628,7 +635,7 @@ __metadata:

    languageName: node

    linkType: hard

    -"@noble/secp256k1@npm:^2.0.0":

    +"@noble/secp256k1@npm:^2.0.0, @noble/secp256k1@npm:^2.1.0":

    version: 2.1.0

    resolution: "@noble/secp256k1@npm:2.1.0"

    checksum: 10c0/b4c7edd2a5ec5acf294546cd06d08dc2a2a2b2ebe34a6da1f2f5104f56983f81dd31c261ad365c6b9757d1c54017fc3363331ee33bba8715ff94c2bc954313cc

    @@ -692,6 +699,25 @@ __metadata:

    languageName: node

    linkType: hard

    +"@nostr-dev-kit/ndk@npm:^2.3.1":

    + version: 2.10.1

    + resolution: "@nostr-dev-kit/ndk@npm:2.10.1"

    + dependencies:

    + "@noble/curves": "npm:^1.6.0"

    + "@noble/hashes": "npm:^1.5.0"

    + "@noble/secp256k1": "npm:^2.1.0"

    + "@scure/base": "npm:^1.1.9"

    + debug: "npm:^4.3.6"

    + light-bolt11-decoder: "npm:^3.2.0"

    + nostr-tools: "npm:^2.7.1"

    + tseep: "npm:^1.2.2"

    + typescript-lru-cache: "npm:^2.0.0"

    + utf8-buffer: "npm:^1.0.0"

    + websocket-polyfill: "npm:^0.0.3"

    + checksum: 10c0/b73cd0e9f036e60893d0470d778c5e672795f93308edca389d42c9487267e95df208b2b2353b20eb766ae606cfd1c328d03a1e43d69351e27159843cdb619cc9

    + languageName: node

    + linkType: hard

    +

    "@npmcli/agent@npm:^2.0.0":

    version: 2.2.2

    resolution: "@npmcli/agent@npm:2.2.2"

    @@ -854,7 +880,7 @@ __metadata:

    languageName: node

    linkType: hard

    -"@scure/base@npm:^1.1.1, @scure/base@npm:~1.1.0":

    +"@scure/base@npm:^1.1.1, @scure/base@npm:^1.1.9, @scure/base@npm:~1.1.0":

    version: 1.1.9

    resolution: "@scure/base@npm:1.1.9"

    checksum: 10c0/77a06b9a2db8144d22d9bf198338893d77367c51b58c72b99df990c0a11f7cadd066d4102abb15e3ca6798d1529e3765f55c4355742465e49aed7a0c01fe76e8

    @@ -3857,6 +3883,7 @@ __metadata:

    eslint-plugin-prettier: "npm:^5.2.1"

    eslint-plugin-svelte: "npm:^2.44.0"

    highlight.js: "npm:^11.10.0"

    + nostr-login: "npm:^1.6.6"

    nostr-tools: "npm:^2.7.2"

    parse-diff: "npm:^0.11.1"

    postcss: "npm:^8.4.47"

    @@ -4492,6 +4519,15 @@ __metadata:

    languageName: node

    linkType: hard

    +"light-bolt11-decoder@npm:^3.2.0":

    + version: 3.2.0

    + resolution: "light-bolt11-decoder@npm:3.2.0"

    + dependencies:

    + "@scure/base": "npm:1.1.1"

    + checksum: 10c0/65c1514b40b3b7fd5f4f0b40412bab35b0135f20f6b4929f5616bc5693f759fa8636d2393ba71fa782b2d9204315015e044628736ee7aea72f71939b0eb486f1

    + languageName: node

    + linkType: hard

    +

    "lilconfig@npm:^2.0.5, lilconfig@npm:^2.1.0":

    version: 2.1.0

    resolution: "lilconfig@npm:2.1.0"

    @@ -5030,6 +5066,36 @@ __metadata:

    languageName: node

    linkType: hard

    +"nostr-login@npm:^1.6.6":

    + version: 1.6.6

    + resolution: "nostr-login@npm:1.6.6"

    + dependencies:

    + "@nostr-dev-kit/ndk": "npm:^2.3.1"

    + nostr-tools: "npm:^1.17.0"

    + tseep: "npm:^1.2.1"

    + checksum: 10c0/f521f906eef377321406838d2ab3be314c9755cbbad90328f32cd65fc5b035bb85729116bc5da313445c399aafb9f1bc12833a789f440e58f12619e84d9ff15f

    + languageName: node

    + linkType: hard

    +

    +"nostr-tools@npm:^1.17.0":

    + version: 1.17.0

    + resolution: "nostr-tools@npm:1.17.0"

    + dependencies:

    + "@noble/ciphers": "npm:0.2.0"

    + "@noble/curves": "npm:1.1.0"

    + "@noble/hashes": "npm:1.3.1"

    + "@scure/base": "npm:1.1.1"

    + "@scure/bip32": "npm:1.3.1"

    + "@scure/bip39": "npm:1.2.1"

    + peerDependencies:

    + typescript: ">=5.0.0"

    + peerDependenciesMeta:

    + typescript:

    + optional: true

    + checksum: 10c0/b52732df3e403ef3c73a41fe1dea89accbff91597b231d811d577c35a9bd9307651de65ec7fbcc9989aef4c35e9c6b1005200fbbfec45544dcd64f928bbfc476

    + languageName: node

    + linkType: hard

    +

    "nostr-tools@npm:^2.7.1, nostr-tools@npm:^2.7.2":

    version: 2.7.2

    resolution: "nostr-tools@npm:2.7.2"

    @@ -6638,6 +6704,13 @@ __metadata:

    languageName: node

    linkType: hard

    +"tseep@npm:^1.2.1, tseep@npm:^1.2.2":

    + version: 1.3.1

    + resolution: "tseep@npm:1.3.1"

    + checksum: 10c0/01b68f1f0e1084ff78bed9f28d83a595b4b6256caecd8c245af62716037dc4e87c9916c90d5d5cd4a2935f7d438a1117b2703dbf8abc79f087bc176a6f247ed6

    + languageName: node

    + linkType: hard

    +

    "tslib@npm:^2.0.1, tslib@npm:^2.6.2, tslib@npm:^2.7.0":

    version: 2.7.0

    resolution: "tslib@npm:2.7.0"

    --

    libgit2 1.8.1

    Reply to this note

    Please Login to reply.

    Discussion

    nostr:naddr1qvzqqqrhnypzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqy88wumn8ghj7mn0wvhxcmmv9uqqkemfw3mk7untwd5x7uqarf775/proposals/note1es7uppvntwfduur0hag7zawjrwyh6nm9q77nz4fqdkxpaams2xas8qpjuc

    I was working with Typescript for the first time and wanted gitworkshop.dev to work with the nostr-login of nostrband.

    This allows you to log in to gitworkshop.dev with Amber, Nsec.app and NIP-07.

    I have written a signer implementation for nostr-ndk.

    nostr:npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr

    I need to test account switching.

    Ok, I've switched to another account, but there is still a bug. "signer required". Will fix that.

    Ok, let's test again. I have changed the signer implementation.

    Very nice. It works. I can now switch accounts with nostr-login.

    I test a refactored version of the signer.

    I test switching accounts with the refactored version.

    Are you sure, you are on the right branch? Did you checkout "pr/nostr-login"?

    Because this is not merged. You need to run the branch locally.

    Thanks very much for your contribution. I'd like more flexibility and control over signing than what this offers. I have always admired nostr::npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr's signing in nostrudel. Now he is abstracting some of nostrudel code into applesuace (great name btw) I'm thinking about using this.

    No problem, that was within my capabilities. It would be nice to get the new methods in, because if I want to use ISSUES to get messages in from plebs, then not everyone has an extension, but rather Amber running.

    Thanks again Ben. I indeed added apple-sauce signing but havn't gotten to the nip46 or Amber clipboard signing just yet. There is an issue open for it. Your welcome to have a go? I can provide some guidance as needed.

    nostr:note1vpd7m3r8yu8q0ndmnau9yaemym58zy29g69zzptn4hmgm2gf82ds84s2p6