diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b8bdd51..e4a923d3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.29-beta] +### Changed +- Send signed data to nostr endpoints to verify public key ownership. +### Changed in DB or environment +- Uses Endorser.ch version 4.1.1 + + ## [0.3.28] ### Added - Posting to nostr apps Trustroots & TripHopping - Display of providers on claim view page ### Changed - Switched BVC-meeting-ending gift to be a gift from the group. +### Changed in DB or environment +- Requires Endorser.ch version 4.1.0 ## [0.3.27] - 2024.09.22 - ee23e6f005e47f5bd6f04d804599f6395371b0e4 diff --git a/README.md b/README.md index 9d07448c9..8c34380f4 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ npm run build * `rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari` -* Commit changes. Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production. +* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production. * [Tag with the new version.](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) diff --git a/package-lock.json b/package-lock.json index 61ca7eb40..3ca99ad2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "TimeSafari", - "version": "0.3.28", + "version": "0.3.28-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "TimeSafari", - "version": "0.3.28", + "version": "0.3.28-beta", "dependencies": { "@dicebear/collection": "^5.4.1", "@dicebear/core": "^5.4.1", diff --git a/package.json b/package.json index bf387299c..7f7a4926c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "TimeSafari", - "version": "0.3.28", + "version": "0.3.28-beta", "scripts": { "dev": "vite", "serve": "vite preview", diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue index fc81dee6f..b89d35360 100644 --- a/src/views/NewEditProjectView.vue +++ b/src/views/NewEditProjectView.vue @@ -105,13 +105,11 @@ {{ zoneName }} -
- +
+
@@ -149,22 +147,12 @@ v-if="showGeneralAdvanced && includeLocation" class="items-center mb-4" > -
- +
+
-
- +
+
@@ -202,7 +190,10 @@ import "leaflet/dist/leaflet.css"; import { AxiosError, AxiosRequestHeaders } from "axios"; import { DateTime } from "luxon"; +import { hexToBytes } from "@noble/hashes/utils"; +import type { EventTemplate, VerifiedEvent } from "nostr-tools/lib/types/core"; import { accountFromSeedWords } from "nostr-tools/nip06"; +import { finalizeEvent, serializeEvent } from "nostr-tools/pure"; import { Component, Vue } from "vue-facing-decorator"; import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; import { Router } from "vue-router"; @@ -456,18 +447,25 @@ export default class NewEditProjectView extends Vue { const projectPath = encodeURIComponent(resp.data.success.handleId); + let signedPayload: VerifiedEvent; // sign something to prove ownership of pubkey if (this.sendToTrustroots) { + signedPayload = await this.signPayload(); this.sendToNostrPartner( "NOSTR-EVENT-TRUSTROOTS", "Trustroots", resp.data.success.claimId, + signedPayload, ); } if (this.sendToTripHopping) { + if (!signedPayload) { + signedPayload = await this.signPayload(); + } this.sendToNostrPartner( "NOSTR-EVENT-TRIPHOPPING", "TripHopping", resp.data.success.claimId, + signedPayload, ); } @@ -535,10 +533,38 @@ export default class NewEditProjectView extends Vue { } } + private async signPayload(): Promise { + const account = await getAccount(this.activeDid); + // get the last number of the derivationPath + const finalDerNum = account?.derivationPath?.split?.("/")?.reverse()[0]; + // remove any trailing ' + const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, ""); + const accountNum = Number(finalDerNumNoApostrophe || 0); + const pubPri = accountFromSeedWords( + account?.mnemonic as string, + "", + accountNum, + ); + const privateBytes = hexToBytes(pubPri?.privateKey); + // No real content is necessary, we just want something signed, + // so we might as well use nostr libs for nostr functions. + // Besides: someday we may create real content that we can relay. + const event: EventTemplate = { + kind: 30402, + tags: [[]], + content: "", + created_at: 0, + }; + // Why does IntelliJ not see matching types? + const signedEvent = finalizeEvent(event, privateBytes); + return signedEvent; + } + private async sendToNostrPartner( linkCode: string, serviceName: string, jwtId: string, + signedPayload: VerifiedEvent, ) { // first, get the public key for nostr const account = await getAccount(this.activeDid); @@ -557,11 +583,15 @@ export default class NewEditProjectView extends Vue { const trustrootsUrl = DEFAULT_PARTNER_API_SERVER + "/api/partner/link"; const timeSafariUrl = window.location.origin + "/claim/" + jwtId; const content = this.fullClaim.name + " - see " + timeSafariUrl; + // Why does IntelliJ not see matching types? + const payload = serializeEvent(signedPayload); const trustrootsParams = { jwtId: jwtId, linkCode: linkCode, inputJson: JSON.stringify(content), - nostrPubKeyHex: nostrPubKey, + pubKeyHex: nostrPubKey, + pubKeyImage: payload, + pubKeySigHex: signedPayload.sig, }; const fullTrustrootsUrl = trustrootsUrl; const headers = await getHeaders(this.activeDid);