Browse Source

fix problem after minimizing use of account private data

pull/124/head
Trent Larson 2 weeks ago
parent
commit
7ababb4e1b
  1. 2
      src/components/OnboardingDialog.vue
  2. 42
      src/views/ClaimCertificateView.vue
  3. 89
      src/views/NewEditProjectView.vue

2
src/components/OnboardingDialog.vue

@ -5,7 +5,7 @@
<h1 class="text-xl font-bold text-center mb-4 relative"> <h1 class="text-xl font-bold text-center mb-4 relative">
Welcome to Time Safari Welcome to Time Safari
<br /> <br />
- Showcasing Gratitude & Magnifing Time - Showcasing Gratitude & Magnifying Time
<div <div
class="text-lg text-center leading-none absolute right-0 -top-1" class="text-lg text-center leading-none absolute right-0 -top-1"
@click="onClickClose(true)" @click="onClickClose(true)"

42
src/views/ClaimCertificateView.vue

@ -148,8 +148,36 @@ export default class ClaimCertificateView extends Vue {
); );
} }
// alternatively, show some offer details
if (claimData.claimType === "Offer") {
const presentedText = "To";
ctx.font = "14px Arial";
const presentedWidth = ctx.measureText(presentedText).width;
ctx.fillText(
presentedText,
(CANVAS_WIDTH - presentedWidth) / 2, // Center horizontally
CANVAS_HEIGHT * 0.37,
);
// fulfills
const agentDid =
claimData.claim.agent.identifier || claimData.claim.agent;
const agentText = serverUtil.didInfoForCertificate(
agentDid,
allContacts,
);
ctx.font = "bold 20px Arial";
const agentWidth = ctx.measureText(agentText).width;
ctx.fillText(
agentText,
(CANVAS_WIDTH - agentWidth) / 2, // Center horizontally
CANVAS_HEIGHT * 0.41,
);
}
const descriptionText = const descriptionText =
claimData.claim.name || claimData.claim.description; claimData.claim.name ||
claimData.claim.description ||
claimData.claim.itemOffered?.description; // for Offers
if (descriptionText) { if (descriptionText) {
const descriptionLine = const descriptionLine =
descriptionText.length > 50 descriptionText.length > 50
@ -164,12 +192,12 @@ export default class ClaimCertificateView extends Vue {
); );
} }
if ( const possibleObject =
claimData.claim.object?.amountOfThisGood && claimData.claim.object || // for GiveActions
claimData.claim.object?.unitCode claimData.claim.includesObject; // for Offers
) { if (possibleObject?.amountOfThisGood && possibleObject?.unitCode) {
const amount = claimData.claim.object.amountOfThisGood; const amount = possibleObject.amountOfThisGood;
const unit = claimData.claim.object.unitCode; const unit = possibleObject.unitCode;
const amountText = serverUtil.displayAmount(unit, amount); const amountText = serverUtil.displayAmount(unit, amount);
const amountWidth = ctx.measureText(amountText).width; const amountWidth = ctx.measureText(amountText).width;
// if there was no description then put this in that spot, otherwise put it below the description // if there was no description then put this in that spot, otherwise put it below the description

89
src/views/NewEditProjectView.vue

@ -203,8 +203,12 @@ import "leaflet/dist/leaflet.css";
import { AxiosError, AxiosRequestHeaders } from "axios"; import { AxiosError, AxiosRequestHeaders } from "axios";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { hexToBytes } from "@noble/hashes/utils"; import { hexToBytes } from "@noble/hashes/utils";
import type { EventTemplate, VerifiedEvent } from "nostr-tools/lib/types/core"; // these core imports could also be included as "import type ..."
import { accountFromSeedWords } from "nostr-tools/nip06"; import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/core";
import {
accountFromExtendedKey,
extendedKeysFromSeedWords,
} from "nostr-tools/nip06";
import { finalizeEvent, serializeEvent } from "nostr-tools/pure"; import { finalizeEvent, serializeEvent } from "nostr-tools/pure";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet";
@ -225,7 +229,6 @@ import {
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import { import {
retrieveAccountCount, retrieveAccountCount,
retrieveAccountMetadata,
retrieveFullyDecryptedAccount, retrieveFullyDecryptedAccount,
} from "@/libs/util"; } from "@/libs/util";
@ -472,31 +475,45 @@ export default class NewEditProjectView extends Vue {
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
if (resp.data?.success?.handleId) { if (resp.data?.success?.handleId) {
this.$notify(
{
group: "alert",
type: "success",
title: "Saved",
text: "The project was saved successfully.",
},
3000,
);
this.errorMessage = ""; this.errorMessage = "";
const projectPath = encodeURIComponent(resp.data.success.handleId); const projectPath = encodeURIComponent(resp.data.success.handleId);
if (this.sendToTrustroots || this.sendToTripHopping) { if (this.sendToTrustroots || this.sendToTripHopping) {
if (this.latitude && this.longitude) { if (this.latitude && this.longitude) {
let signedPayload: VerifiedEvent | undefined; // sign something to prove ownership of pubkey let payloadAndKey; // sign something to prove ownership of pubkey
if (this.sendToTrustroots) { if (this.sendToTrustroots) {
signedPayload = await this.signPayload(); payloadAndKey = await this.signSomePayload();
// not going to await... the save was successful, so we'll continue to the next page
this.sendToNostrPartner( this.sendToNostrPartner(
"NOSTR-EVENT-TRUSTROOTS", "NOSTR-EVENT-TRUSTROOTS",
"Trustroots", "Trustroots",
resp.data.success.claimId, resp.data.success.claimId,
signedPayload, payloadAndKey.signedEvent,
payloadAndKey.publicExtendedKey,
); );
} }
if (this.sendToTripHopping) { if (this.sendToTripHopping) {
if (!signedPayload) { if (!payloadAndKey) {
signedPayload = await this.signPayload(); payloadAndKey = await this.signSomePayload();
} }
// not going to await... the save was successful, so we'll continue to the next page
this.sendToNostrPartner( this.sendToNostrPartner(
"NOSTR-EVENT-TRIPHOPPING", "NOSTR-EVENT-TRIPHOPPING",
"TripHopping", "TripHopping",
resp.data.success.claimId, resp.data.success.claimId,
signedPayload, payloadAndKey.signedEvent,
payloadAndKey.publicExtendedKey,
); );
} }
} else { } else {
@ -576,19 +593,28 @@ export default class NewEditProjectView extends Vue {
} }
} }
private async signPayload(): Promise<VerifiedEvent> { /**
* @return a signed payload and an extended public key for later transmission
*/
private async signSomePayload(): Promise<{
signedEvent: VerifiedEvent;
publicExtendedKey: string;
}> {
const account = await retrieveFullyDecryptedAccount(this.activeDid); const account = await retrieveFullyDecryptedAccount(this.activeDid);
// get the last number of the derivationPath // get the last number of the derivationPath
const finalDerNum = account?.derivationPath?.split?.("/")?.reverse()[0]; const finalDerNum = account?.derivationPath?.split?.("/")?.reverse()[0];
// remove any trailing ' // remove any trailing '
const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, ""); const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, "");
const accountNum = Number(finalDerNumNoApostrophe || 0); const accountNum = Number(finalDerNumNoApostrophe || 0);
const pubPri = accountFromSeedWords( const extPubPri = extendedKeysFromSeedWords(
account?.mnemonic as string, account?.mnemonic as string,
"", "",
accountNum, accountNum,
); );
const privateBytes = hexToBytes(pubPri?.privateKey); const publicExtendedKey: string = extPubPri?.publicExtendedKey;
const privateExtendedKey = extPubPri?.privateExtendedKey;
const privateKey = accountFromExtendedKey(privateExtendedKey).privateKey;
const privateBytes = hexToBytes(privateKey);
// No real content is necessary, we just want something signed, // No real content is necessary, we just want something signed,
// so we might as well use nostr libs for nostr functions. // so we might as well use nostr libs for nostr functions.
// Besides: someday we may create real content that we can relay. // Besides: someday we may create real content that we can relay.
@ -598,9 +624,12 @@ export default class NewEditProjectView extends Vue {
content: "", content: "",
created_at: 0, created_at: 0,
}; };
const signedEvent: VerifiedEvent = finalizeEvent(
// Why does IntelliJ not see matching types? // Why does IntelliJ not see matching types?
const signedEvent = finalizeEvent(event, privateBytes); event as EventTemplate,
return signedEvent; privateBytes,
) as VerifiedEvent;
return { signedEvent, publicExtendedKey };
} }
private async sendToNostrPartner( private async sendToNostrPartner(
@ -608,21 +637,9 @@ export default class NewEditProjectView extends Vue {
serviceName: string, serviceName: string,
jwtId: string, jwtId: string,
signedPayload: VerifiedEvent, signedPayload: VerifiedEvent,
publicExtendedKey: string,
) { ) {
// first, get the public key for nostr try {
const account = await retrieveAccountMetadata(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 nostrPubKey = pubPri?.publicKey;
let partnerServer = DEFAULT_PARTNER_API_SERVER; let partnerServer = DEFAULT_PARTNER_API_SERVER;
const settings = await retrieveSettingsForActiveAccount(); const settings = await retrieveSettingsForActiveAccount();
if (settings.partnerApiServer) { if (settings.partnerApiServer) {
@ -631,18 +648,26 @@ export default class NewEditProjectView extends Vue {
const endorserPartnerUrl = partnerServer + "/api/partner/link"; const endorserPartnerUrl = partnerServer + "/api/partner/link";
const timeSafariUrl = window.location.origin + "/claim/" + jwtId; const timeSafariUrl = window.location.origin + "/claim/" + jwtId;
const content = this.fullClaim.name + " - see " + timeSafariUrl; const content = this.fullClaim.name + " - see " + timeSafariUrl;
const publicKeyHex = accountFromExtendedKey(publicExtendedKey).publicKey;
const unsignedPayload: UnsignedEvent = {
// why doesn't "...signedPayload" work?
kind: signedPayload.kind,
tags: signedPayload.tags,
content: signedPayload.content,
created_at: signedPayload.created_at,
pubkey: publicKeyHex,
};
// Why does IntelliJ not see matching types? // Why does IntelliJ not see matching types?
const payload = serializeEvent(signedPayload); const payload = serializeEvent(unsignedPayload as UnsignedEvent);
const partnerParams = { const partnerParams = {
jwtId: jwtId, jwtId: jwtId,
linkCode: linkCode, linkCode: linkCode,
inputJson: JSON.stringify(content), inputJson: JSON.stringify(content),
pubKeyHex: nostrPubKey, pubKeyHex: publicKeyHex,
pubKeyImage: payload, pubKeyImage: payload,
pubKeySigHex: signedPayload.sig, pubKeySigHex: signedPayload.sig,
}; };
const headers = await getHeaders(this.activeDid); const headers = await getHeaders(this.activeDid);
try {
const linkResp = await this.axios.post( const linkResp = await this.axios.post(
endorserPartnerUrl, endorserPartnerUrl,
partnerParams, partnerParams,
@ -731,7 +756,7 @@ export default class NewEditProjectView extends Vue {
group: "alert", group: "alert",
type: "info", type: "info",
title: "About Nostr Events", title: "About Nostr Events",
text: "This will cause a submission to a partner on the nostr network. It will contain your public key data which may allow correlation, so don't allow this if you're not comfortable with that.", text: "This will submit this project to a partner on the nostr network. It will contain your public key data which may allow correlation, so don't allow this if you're not comfortable with that.",
}, },
7000, 7000,
); );

Loading…
Cancel
Save