diff --git a/src/libs/crypto/vc/didPeer.ts b/src/libs/crypto/vc/didPeer.ts index 9c0fa73c0..3c62dd0f2 100644 --- a/src/libs/crypto/vc/didPeer.ts +++ b/src/libs/crypto/vc/didPeer.ts @@ -1,10 +1,10 @@ -import {Buffer} from "buffer/"; -import {decode as cborDecode} from "cbor-x"; -import {bytesToMultibase, multibaseToBytes} from "did-jwt"; +import { Buffer } from "buffer/"; +import { decode as cborDecode } from "cbor-x"; +import { bytesToMultibase, multibaseToBytes } from "did-jwt"; -import {getWebCrypto} from "@/libs/crypto/vc/passkeyHelpers"; +import { getWebCrypto } from "@/libs/crypto/vc/passkeyHelpers"; -const PEER_DID_PREFIX = "did:peer:"; +export const PEER_DID_PREFIX = "did:peer:"; const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0"; /** @@ -93,4 +93,4 @@ export function createPeerDid(publicKeyBytes: Uint8Array) { "p256-pub", ); return PEER_DID_MULTIBASE_PREFIX + methodSpecificId; -} \ No newline at end of file +} diff --git a/src/libs/crypto/vc/index.ts b/src/libs/crypto/vc/index.ts index 7736708db..308f71c4b 100644 --- a/src/libs/crypto/vc/index.ts +++ b/src/libs/crypto/vc/index.ts @@ -13,6 +13,8 @@ import * as u8a from "uint8arrays"; import { createDidPeerJwt } from "@/libs/crypto/vc/passkeyDidPeer"; +export const ETHR_DID_PREFIX = "did:ethr:"; + /** * Meta info about a key */ diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index ff4bbd600..26954cd69 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -6,7 +6,7 @@ import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app"; import { Contact } from "@/db/tables/contacts"; import { accessToken } from "@/libs/crypto"; import { NonsensitiveDexie } from "@/db/index"; -import { getAccount, getIdentity } from "@/libs/util"; +import { getAccount } from "@/libs/util"; import { createEndorserJwtForKey, KeyMeta } from "@/libs/crypto/vc"; export const SCHEMA_ORG_CONTEXT = "https://schema.org"; @@ -1001,8 +1001,7 @@ export async function setVisibilityUtil( } const url = apiServer + "/api/report/" + (visibility ? "canSeeMe" : "cannotSeeMe"); - const identity = await getIdentity(activeDid); - const headers = await getHeaders(identity.did); + const headers = await getHeaders(activeDid); const payload = JSON.stringify({ did: contact.did }); try { diff --git a/src/libs/util.ts b/src/libs/util.ts index c5f3b39b7..c0292ac07 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -211,18 +211,6 @@ export const getAccount = async ( return account; }; -export const getIdentity = async (activeDid: string): Promise => { - const account = await getAccount(activeDid); - const identity = JSON.parse(account?.identity || "null"); - - if (!identity) { - throw new Error( - `Attempted to load identity ${activeDid} but no identifier was found`, - ); - } - return identity; -}; - /** * Generates a new identity, saves it to the database, and sets it as the active identity. * @return {Promise} with the DID of the new identity diff --git a/src/views/ContactAmountsView.vue b/src/views/ContactAmountsView.vue index cefb27ce1..157f2438f 100644 --- a/src/views/ContactAmountsView.vue +++ b/src/views/ContactAmountsView.vue @@ -124,7 +124,6 @@ import { GiveVerifiableCredential, SCHEMA_ORG_CONTEXT, } from "@/libs/endorserServer"; -import * as libsUtil from "@/libs/util"; @Component({ components: { QuickNav } }) export default class ContactAmountssView extends Vue { @@ -175,12 +174,11 @@ export default class ContactAmountssView extends Vue { async loadGives(activeDid: string, contact: Contact) { try { - const identity = await libsUtil.getIdentity(this.activeDid); let result: Array = []; const url = this.apiServer + "/api/v2/report/gives?agentDid=" + - encodeURIComponent(identity.did) + + encodeURIComponent(this.activeDid) + "&recipientDid=" + encodeURIComponent(contact.did); const headers = await getHeaders(activeDid); @@ -209,7 +207,7 @@ export default class ContactAmountssView extends Vue { "/api/v2/report/gives?agentDid=" + encodeURIComponent(contact.did) + "&recipientDid=" + - encodeURIComponent(identity.did); + encodeURIComponent(this.activeDid); const headers2 = await getHeaders(activeDid); const resp2 = await this.axios.get(url2, { headers: headers2 }); if (resp2.status === 200) { diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue index c14f350dd..0a7109c72 100644 --- a/src/views/ContactQRScanShowView.vue +++ b/src/views/ContactQRScanShowView.vue @@ -34,7 +34,11 @@

-
+
+ + Click here to copy your DID to your clipboard. + + + Then give it to them so they can paste it in their list of People. +
You have no identitifiers yet, so @@ -92,13 +106,14 @@ import { nextDerivationPath, } from "@/libs/crypto"; import { - CONTACT_URL_PREFIX, createEndorserJwtForDid, + CONTACT_URL_PREFIX, + createEndorserJwtForDid, ENDORSER_JWT_URL_LOCATION, isDid, register, setVisibilityUtil, } from "@/libs/endorserServer"; -import * as libsUtil from "@/libs/util"; +import { ETHR_DID_PREFIX } from "@/libs/crypto/vc"; @Component({ components: { @@ -117,6 +132,8 @@ export default class ContactQRScanShow extends Vue { isRegistered = false; qrValue = ""; + ETHR_DID_PREFIX = ETHR_DID_PREFIX; + async created() { await db.open(); const settings = await db.settings.get(MASTER_SETTINGS_KEY); @@ -131,20 +148,9 @@ export default class ContactQRScanShow extends Vue { const accounts = await accountsDB.accounts.toArray(); const account = R.find((acc) => acc.did === this.activeDid, accounts); if (account) { - const identity = await libsUtil.getIdentity(this.activeDid); - const publicKeyHex = identity.keys[0].publicKeyHex; + const publicKeyHex = account.publicKeyHex; const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64"); - const newDerivPath = nextDerivationPath(account.derivationPath as string); - const nextPublicHex = deriveAddress( - account.mnemonic as string, - newDerivPath, - )[2]; - const nextPublicEncKey = Buffer.from(nextPublicHex, "hex"); - const nextPublicEncKeyHash = sha256(nextPublicEncKey); - const nextPublicEncKeyHashBase64 = - Buffer.from(nextPublicEncKeyHash).toString("base64"); - const contactInfo = { iat: Date.now(), iss: this.activeDid, @@ -153,13 +159,28 @@ export default class ContactQRScanShow extends Vue { (settings?.firstName || "") + (settings?.lastName ? ` ${settings.lastName}` : ""), // deprecated, pre v 0.1.3 publicEncKey, - nextPublicEncKeyHash: nextPublicEncKeyHashBase64, profileImageUrl: settings?.profileImageUrl, registered: settings?.isRegistered, }, }; - const vcJwt: string = await createEndorserJwtForDid(identity.did, contactInfo); + if (account?.mnemonic && account?.derivationPath) { + const newDerivPath = nextDerivationPath( + account.derivationPath as string, + ); + const nextPublicHex = deriveAddress( + account.mnemonic as string, + newDerivPath, + )[2]; + const nextPublicEncKey = Buffer.from(nextPublicHex, "hex"); + const nextPublicEncKeyHash = sha256(nextPublicEncKey); + const nextPublicEncKeyHashBase64 = + Buffer.from(nextPublicEncKeyHash).toString("base64"); + contactInfo.own.nextPublicEncKeyHash = nextPublicEncKeyHashBase64; + } + + const vcJwt = await createEndorserJwtForDid(this.activeDid, contactInfo); + const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION; this.qrValue = viewPrefix + vcJwt; } @@ -409,7 +430,7 @@ export default class ContactQRScanShow extends Vue { ); } - onCopyToClipboard() { + onCopyUrlToClipboard() { //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing useClipboard() .copy(this.qrValue) @@ -426,5 +447,22 @@ export default class ContactQRScanShow extends Vue { ); }); } + + onCopyDidToClipboard() { + //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing + useClipboard() + .copy(this.activeDid) + .then(() => { + this.$notify( + { + group: "alert", + type: "info", + title: "Copied", + text: "Your DID was copied to the clipboard. Have them paste it on their 'People' screen to add you.", + }, + 10000, + ); + }); + } } diff --git a/src/views/GiftedDetails.vue b/src/views/GiftedDetails.vue index 1ac570c69..626a0c85c 100644 --- a/src/views/GiftedDetails.vue +++ b/src/views/GiftedDetails.vue @@ -313,12 +313,11 @@ export default class GiftedDetails extends Vue { if (this.projectId) { // console.log("Getting project name from cache", this.projectId); - const identity = await libsUtil.getIdentity(this.activeDid); const project = await getPlanFromCache( this.projectId, this.axios, this.apiServer, - identity.did, + this.activeDid, ); this.projectName = project?.name ? "the project: " + project.name diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue index b9ba94edd..0ebf62ac8 100644 --- a/src/views/NewEditProjectView.vue +++ b/src/views/NewEditProjectView.vue @@ -175,7 +175,6 @@ import "leaflet/dist/leaflet.css"; import { AxiosError } from "axios"; import { DateTime } from "luxon"; -import { IIdentifier } from "@veramo/core"; import { Component, Vue } from "vue-facing-decorator"; import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; @@ -189,7 +188,6 @@ import { createEndorserJwtVcFromClaim, PlanVerifiableCredential, } from "@/libs/endorserServer"; -import * as libsUtil from "@/libs/util"; import { useAppStore } from "@/store/app"; @Component({ @@ -229,8 +227,6 @@ export default class NewEditProjectView extends Vue { zoneName = DateTime.local().zoneName; zoom = 2; - libsUtil = libsUtil; - async mounted() { await accountsDB.open(); this.numAccounts = await accountsDB.accounts.count(); @@ -365,7 +361,7 @@ export default class NewEditProjectView extends Vue { } } - private async saveProject(identity: IIdentifier) { + private async saveProject(issuerDid: string) { // Make a claim const vcClaim: PlanVerifiableCredential = this.fullClaim; if (this.projectId) { @@ -416,13 +412,13 @@ export default class NewEditProjectView extends Vue { } else { delete vcClaim.startTime; } - const vcJwt = await createEndorserJwtVcFromClaim(identity.did, vcClaim); + const vcJwt = await createEndorserJwtVcFromClaim(issuerDid, vcClaim); // Make the xhr request payload const payload = JSON.stringify({ jwtEncoded: vcJwt }); const url = this.apiServer + "/api/v2/claim"; - const token = await accessToken(identity.did); + const token = await accessToken(issuerDid); const headers = { "Content-Type": "application/json", Authorization: "Bearer " + token, @@ -508,8 +504,7 @@ export default class NewEditProjectView extends Vue { if (this.numAccounts === 0) { console.error("Error: there is no account."); } else { - const identity = await libsUtil.getIdentity(this.activeDid); - this.saveProject(identity); + this.saveProject(this.activeDid); } } diff --git a/src/views/ProjectsView.vue b/src/views/ProjectsView.vue index 4ff0c4350..d8a1f9691 100644 --- a/src/views/ProjectsView.vue +++ b/src/views/ProjectsView.vue @@ -235,7 +235,6 @@ import { accountsDB, db } from "@/db/index"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { accessToken } from "@/libs/crypto"; import * as libsUtil from "@/libs/util"; -import { IIdentifier } from "@veramo/core"; import InfiniteScroll from "@/components/InfiniteScroll.vue"; import QuickNav from "@/components/QuickNav.vue"; import ProjectIcon from "@/components/ProjectIcon.vue"; @@ -255,9 +254,9 @@ export default class ProjectsView extends Vue { ); } + activeDid = ""; apiServer = ""; projects: PlanData[] = []; - currentIid: IIdentifier; isLoading = false; isRegistered = false; numAccounts = 0; @@ -271,7 +270,7 @@ export default class ProjectsView extends Vue { try { await db.open(); const settings = await db.settings.get(MASTER_SETTINGS_KEY); - const activeDid: string = (settings?.activeDid as string) || ""; + this.activeDid = (settings?.activeDid as string) || ""; this.apiServer = (settings?.apiServer as string) || ""; this.isRegistered = !!settings?.isRegistered; @@ -281,7 +280,6 @@ export default class ProjectsView extends Vue { console.error("No accounts found."); this.errNote("You need an identifier to load your projects."); } else { - this.currentIid = await libsUtil.getIdentity(activeDid); await this.loadOffers(); } } catch (err) { @@ -342,7 +340,7 @@ export default class ProjectsView extends Vue { if (this.projects.length > 0 && payload) { const latestProject = this.projects[this.projects.length - 1]; await this.loadProjects( - this.currentIid, + this.activeDid, `beforeId=${latestProject.rowid}`, ); } @@ -350,13 +348,12 @@ export default class ProjectsView extends Vue { /** * Load projects initially - * @param identifier of the user + * @param issuerDid of the user * @param urlExtra additional url parameters in a string **/ - async loadProjects(identifier?: IIdentifier, urlExtra: string = "") { - const identity = identifier || this.currentIid; + async loadProjects(activeDid?: string, urlExtra: string = "") { const url = `${this.apiServer}/api/v2/report/plansByIssuer?${urlExtra}`; - const token: string = await accessToken(identity.did); + const token: string = await accessToken(activeDid); await this.projectDataLoader(url, token); } @@ -446,19 +443,18 @@ export default class ProjectsView extends Vue { async loadMoreOfferData(payload: boolean) { if (this.offers.length > 0 && payload) { const latestOffer = this.offers[this.offers.length - 1]; - await this.loadOffers(this.currentIid, `&beforeId=${latestOffer.jwtId}`); + await this.loadOffers(this.activeDid, `&beforeId=${latestOffer.jwtId}`); } } /** * Load offers initially - * @param identifier of the user + * @param issuerDid of the user * @param urlExtra additional url parameters in a string **/ - async loadOffers(identifier?: IIdentifier, urlExtra: string = "") { - const identity = identifier || this.currentIid; - const url = `${this.apiServer}/api/v2/report/offers?offeredByDid=${identity.did}${urlExtra}`; - const token: string = await accessToken(identity.did); + async loadOffers(issuerDid?: string, urlExtra: string = "") { + const url = `${this.apiServer}/api/v2/report/offers?offeredByDid=${issuerDid}${urlExtra}`; + const token: string = await accessToken(issuerDid); await this.offerDataLoader(url, token); }