Browse Source

replace remaining didJwt.createJwt calls with one that checks for did:peer

master
Trent Larson 5 months ago
parent
commit
674ca1d63c
  1. 3
      src/components/GiftedDialog.vue
  2. 3
      src/components/OfferDialog.vue
  3. 5
      src/libs/crypto/index.ts
  4. 82
      src/libs/endorserServer.ts
  5. 2
      src/libs/util.ts
  6. 3
      src/test/index.ts
  7. 89
      src/views/AccountViewView.vue
  8. 2
      src/views/ClaimAddRawView.vue
  9. 2
      src/views/ClaimView.vue
  10. 2
      src/views/ConfirmGiftView.vue
  11. 97
      src/views/ContactAmountsView.vue
  12. 12
      src/views/ContactQRScanShowView.vue
  13. 18
      src/views/ContactsView.vue
  14. 3
      src/views/GiftedDetails.vue
  15. 157
      src/views/NewEditProjectView.vue
  16. 2
      src/views/ProjectViewView.vue
  17. 5
      src/views/QuickActionBvcBeginView.vue
  18. 8
      src/views/QuickActionBvcEndView.vue

3
src/components/GiftedDialog.vue

@ -287,11 +287,10 @@ export default class GiftedDialog extends Vue {
unitCode: string = "HUR", unitCode: string = "HUR",
) { ) {
try { try {
const identity = await libsUtil.getIdentity(this.activeDid);
const result = await createAndSubmitGive( const result = await createAndSubmitGive(
this.axios, this.axios,
this.apiServer, this.apiServer,
identity, this.activeDid,
giverDid, giverDid,
this.receiver?.did as string, this.receiver?.did as string,
description, description,

3
src/components/OfferDialog.vue

@ -223,11 +223,10 @@ export default class OfferDialog extends Vue {
} }
try { try {
const identity = await libsUtil.getIdentity(this.activeDid);
const result = await createAndSubmitOffer( const result = await createAndSubmitOffer(
this.axios, this.axios,
this.apiServer, this.apiServer,
identity, this.activeDid,
description, description,
amount, amount,
unitCode, unitCode,

5
src/libs/crypto/index.ts

@ -101,11 +101,6 @@ export const accessToken = async (did?: string) => {
} }
}; };
export const sign = async (privateKeyHex: string) => {
const signer = SimpleSigner(privateKeyHex);
return signer;
};
/** /**
* Copied out of did-jwt since it's deprecated in that library. * Copied out of did-jwt since it's deprecated in that library.
* *

82
src/libs/endorserServer.ts

@ -2,7 +2,6 @@ import { Axios, AxiosRequestConfig, AxiosResponse } from "axios";
import * as didJwt from "did-jwt"; import * as didJwt from "did-jwt";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
import * as R from "ramda"; import * as R from "ramda";
import { IIdentifier } from "@veramo/core";
import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app"; import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app";
import { Contact } from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
@ -516,6 +515,9 @@ export async function setPlanInCache(
planCache.set(handleId, planSummary); planCache.set(handleId, planSummary);
} }
/**
* Construct GiveAction VC for submission to server
*/
export function constructGive( export function constructGive(
fromDid?: string | null, fromDid?: string | null,
toDid?: string, toDid?: string,
@ -570,7 +572,7 @@ export function constructGive(
export async function createAndSubmitGive( export async function createAndSubmitGive(
axios: Axios, axios: Axios,
apiServer: string, apiServer: string,
identity: IIdentifier, issuerDid: string,
fromDid?: string | null, fromDid?: string | null,
toDid?: string, toDid?: string,
description?: string, description?: string,
@ -594,7 +596,7 @@ export async function createAndSubmitGive(
); );
return createAndSubmitClaim( return createAndSubmitClaim(
vcClaim as GenericCredWrapper, vcClaim as GenericCredWrapper,
identity, issuerDid,
apiServer, apiServer,
axios, axios,
); );
@ -612,7 +614,7 @@ export async function createAndSubmitGive(
export async function createAndSubmitOffer( export async function createAndSubmitOffer(
axios: Axios, axios: Axios,
apiServer: string, apiServer: string,
identity: IIdentifier, issuerDid: string,
description?: string, description?: string,
amount?: number, amount?: number,
unitCode?: string, unitCode?: string,
@ -623,7 +625,7 @@ export async function createAndSubmitOffer(
const vcClaim: OfferVerifiableCredential = { const vcClaim: OfferVerifiableCredential = {
"@context": SCHEMA_ORG_CONTEXT, "@context": SCHEMA_ORG_CONTEXT,
"@type": "Offer", "@type": "Offer",
offeredBy: { identifier: identity.did }, offeredBy: { identifier: issuerDid },
validThrough: expirationDate || undefined, validThrough: expirationDate || undefined,
}; };
if (amount) { if (amount) {
@ -647,7 +649,7 @@ export async function createAndSubmitOffer(
} }
return createAndSubmitClaim( return createAndSubmitClaim(
vcClaim as GenericCredWrapper, vcClaim as GenericCredWrapper,
identity, issuerDid,
apiServer, apiServer,
axios, axios,
); );
@ -655,7 +657,7 @@ export async function createAndSubmitOffer(
// similar logic is found in endorser-mobile // similar logic is found in endorser-mobile
export const createAndSubmitConfirmation = async ( export const createAndSubmitConfirmation = async (
identifier: IIdentifier, issuerDid: string,
claim: GenericVerifiableCredential, claim: GenericVerifiableCredential,
lastClaimId: string, // used to set the lastClaimId lastClaimId: string, // used to set the lastClaimId
handleId: string | undefined, handleId: string | undefined,
@ -672,12 +674,12 @@ export const createAndSubmitConfirmation = async (
"@type": "AgreeAction", "@type": "AgreeAction",
object: goodClaim, object: goodClaim,
}; };
return createAndSubmitClaim(confirmationClaim, identifier, apiServer, axios); return createAndSubmitClaim(confirmationClaim, issuerDid, apiServer, axios);
}; };
export async function createAndSubmitClaim( export async function createAndSubmitClaim(
vcClaim: GenericVerifiableCredential, vcClaim: GenericVerifiableCredential,
identity: IIdentifier, issuerDid: string,
apiServer: string, apiServer: string,
axios: Axios, axios: Axios,
): Promise<CreateAndSubmitClaimResult> { ): Promise<CreateAndSubmitClaimResult> {
@ -690,34 +692,15 @@ export async function createAndSubmitClaim(
}, },
}; };
// Create a signature using private key of identity const vcJwt: string = await createEndorserJwt(issuerDid, vcPayload);
const firstKey = identity.keys[0];
const privateKeyHex = firstKey?.privateKeyHex;
if (!privateKeyHex) {
throw {
error: "No private key",
message: `Your identifier ${identity.did} is not configured correctly. Use a different identifier.`,
};
}
const signer = await SimpleSigner(privateKeyHex);
// Create a JWT for the request
const vcJwt: string = await didJwt.createJWT(vcPayload, {
issuer: identity.did,
signer,
});
// Make the xhr request payload // Make the xhr request payload
const payload = JSON.stringify({ jwtEncoded: vcJwt }); const payload = JSON.stringify({ jwtEncoded: vcJwt });
const url = `${apiServer}/api/v2/claim`; const url = `${apiServer}/api/v2/claim`;
const token = await accessToken(identity.did);
const response = await axios.post(url, payload, { const response = await axios.post(url, payload, {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${token}`,
}, },
}); });
@ -942,21 +925,36 @@ export const bvcMeetingJoinClaim = (did: string, startTime: string) => {
}; };
}; };
export async function createEndorserJwt(did: string, payload: object) { export async function createEndorserJwtVcFromClaim(
const account = await getAccount(did); issuerDid: string,
claim: object,
) {
// Make a payload for the claim
const vcPayload = {
vc: {
"@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiableCredential"],
credentialSubject: claim,
},
};
return createEndorserJwt(issuerDid, vcPayload);
}
export async function createEndorserJwt(issuerDid: string, payload: object) {
const account = await getAccount(issuerDid);
if (account?.identity) { if (account?.identity) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const identity = JSON.parse(account.identity!); const identity = JSON.parse(account.identity!);
const privateKeyHex = identity.keys[0].privateKeyHex; const privateKeyHex = identity.keys[0].privateKeyHex;
const signer = await SimpleSigner(privateKeyHex); const signer = await SimpleSigner(privateKeyHex);
return didJwt.createJWT(payload, { return didJwt.createJWT(payload, {
issuer: did, issuer: issuerDid,
signer: signer, signer: signer,
}); });
} else if (account?.passkeyCredIdHex) { } else if (account?.passkeyCredIdHex) {
return createDidPeerJwt(did, account.passkeyCredIdHex, payload); return createDidPeerJwt(issuerDid, account.passkeyCredIdHex, payload);
} else { } else {
throw new Error("No identity data found to sign for DID " + did); throw new Error("No identity data found to sign for DID " + issuerDid);
} }
} }
@ -1044,16 +1042,16 @@ export async function setVisibilityUtil(
* *
* @param apiServer endorser server URL string * @param apiServer endorser server URL string
* @param axios Axios instance * @param axios Axios instance
* @param {IIdentifier} identity - The identity object to check rate limits for. * @param {string} issuerDid - The DID for which to check rate limits.
* @returns {Promise<AxiosResponse>} The Axios response object. * @returns {Promise<AxiosResponse>} The Axios response object.
*/ */
export async function fetchEndorserRateLimits( export async function fetchEndorserRateLimits(
apiServer: string, apiServer: string,
axios: Axios, axios: Axios,
did: string, issuerDid: string,
) { ) {
const url = `${apiServer}/api/report/rateLimits`; const url = `${apiServer}/api/report/rateLimits`;
const headers = await getHeaders(did); const headers = await getHeaders(issuerDid);
return await axios.get(url, { headers } as AxiosRequestConfig); return await axios.get(url, { headers } as AxiosRequestConfig);
} }
@ -1062,15 +1060,11 @@ export async function fetchEndorserRateLimits(
* *
* @param apiServer image server URL string * @param apiServer image server URL string
* @param axios Axios instance * @param axios Axios instance
* @param {IIdentifier} identity - The identity object to check rate limits for. * @param {string} issuerDid - The DID for which to check rate limits.
* @returns {Promise<AxiosResponse>} The Axios response object. * @returns {Promise<AxiosResponse>} The Axios response object.
*/ */
export async function fetchImageRateLimits( export async function fetchImageRateLimits(axios: Axios, issuerDid: string) {
apiServer: string,
axios: Axios,
did: string,
) {
const url = DEFAULT_IMAGE_API_SERVER + "/image-limits"; const url = DEFAULT_IMAGE_API_SERVER + "/image-limits";
const headers = await getHeaders(did); const headers = await getHeaders(issuerDid);
return await axios.get(url, { headers } as AxiosRequestConfig); return await axios.get(url, { headers } as AxiosRequestConfig);
} }

2
src/libs/util.ts

@ -16,7 +16,7 @@ import { createPeerDid, registerCredential } from "@/libs/didPeer";
import { Buffer } from "buffer"; import { Buffer } from "buffer";
export const PRIVACY_MESSAGE = export const PRIVACY_MESSAGE =
"The data you send be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to those you allow."; "The data you send will be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to them and others you explicitly allow.";
/* eslint-disable prettier/prettier */ /* eslint-disable prettier/prettier */
export const UNIT_SHORT: Record<string, string> = { export const UNIT_SHORT: Record<string, string> = {

3
src/test/index.ts

@ -6,6 +6,9 @@ import { SERVICE_ID } from "../libs/endorserServer";
import { deriveAddress, newIdentifier } from "../libs/crypto"; import { deriveAddress, newIdentifier } from "../libs/crypto";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
/**
* Get User #0 to sign & submit a RegisterAction for the user's activeDid.
*/
export async function testServerRegisterUser() { export async function testServerRegisterUser() {
const testUser0Mnem = const testUser0Mnem =
"seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control"; "seminar accuse mystery assist delay law thing deal image undo guard initial shallow wrestle list fragile borrow velvet tomorrow awake explain test offer control";

89
src/views/AccountViewView.vue

@ -359,6 +359,7 @@
<div class="text-slate-500 text-sm font-bold">Derivation Path</div> <div class="text-slate-500 text-sm font-bold">Derivation Path</div>
<div <div
v-if="derivationPath"
class="text-sm text-slate-500 flex justify-start items-center mb-1" class="text-sm text-slate-500 flex justify-start items-center mb-1"
> >
<code class="truncate">{{ derivationPath }}</code> <code class="truncate">{{ derivationPath }}</code>
@ -375,6 +376,12 @@
</button> </button>
<span v-show="showDerCopy">Copied</span> <span v-show="showDerCopy">Copied</span>
</div> </div>
<div
v-else
class="text-sm text-slate-500 flex justify-start items-center mb-1"
>
(none)
</div>
</div> </div>
<!-- id used by puppeteer test script --> <!-- id used by puppeteer test script -->
@ -646,13 +653,16 @@
<script lang="ts"> <script lang="ts">
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import { Buffer } from "buffer/";
import Dexie from "dexie"; import Dexie from "dexie";
import "dexie-export-import"; import "dexie-export-import";
import { ImportProgress } from "dexie-export-import/dist/import"; import { ImportProgress } from "dexie-export-import/dist/import";
import { IIdentifier } from "@veramo/core";
import { ref } from "vue"; import { ref } from "vue";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { useClipboard } from "@vueuse/core"; import { useClipboard } from "@vueuse/core";
import EntityIcon from "@/components/EntityIcon.vue";
import ImageMethodDialog from "@/components/ImageMethodDialog.vue"; import ImageMethodDialog from "@/components/ImageMethodDialog.vue";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import TopMessage from "@/components/TopMessage.vue"; import TopMessage from "@/components/TopMessage.vue";
@ -664,9 +674,9 @@ import {
NotificationIface, NotificationIface,
} from "@/constants/app"; } from "@/constants/app";
import { db, accountsDB } from "@/db/index"; import { db, accountsDB } from "@/db/index";
import { Account } from "@/db/tables/accounts";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto"; import { accessToken } from "@/libs/crypto";
import { IIdentifier } from "@veramo/core";
import { import {
ErrorResponse, ErrorResponse,
EndorserRateLimits, EndorserRateLimits,
@ -674,15 +684,7 @@ import {
fetchEndorserRateLimits, fetchEndorserRateLimits,
fetchImageRateLimits, fetchImageRateLimits,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import { Buffer } from "buffer/"; import { getAccount } from "@/libs/util";
import EntityIcon from "@/components/EntityIcon.vue";
interface IAccount {
did: string;
publicKeyHex: string;
privateHex?: string;
derivationPath: string;
}
const inputImportFileNameRef = ref<Blob>(); const inputImportFileNameRef = ref<Blob>();
@ -705,6 +707,7 @@ export default class AccountViewView extends Vue {
givenName = ""; givenName = "";
hideRegisterPromptOnNewContact = false; hideRegisterPromptOnNewContact = false;
imageLimits: ImageRateLimits | null = null; imageLimits: ImageRateLimits | null = null;
imageServer = "";
isRegistered = false; isRegistered = false;
isSubscribed = false; isSubscribed = false;
limitsMessage = ""; limitsMessage = "";
@ -738,18 +741,9 @@ export default class AccountViewView extends Vue {
*/ */
async mounted() { async mounted() {
try { try {
await db.open();
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
// Initialize component state with values from the database or defaults // Initialize component state with values from the database or defaults
this.initializeState(settings); await this.initializeState();
await this.processIdentity();
// Get and process the identity
const identity = await this.getIdentity(this.activeDid);
if (identity) {
this.processIdentity(identity);
}
const registration = await navigator.serviceWorker.ready; const registration = await navigator.serviceWorker.ready;
this.subscription = await registration.pushManager.getSubscription(); this.subscription = await registration.pushManager.getSubscription();
@ -768,9 +762,12 @@ export default class AccountViewView extends Vue {
/** /**
* Initializes component state with values from the database or defaults. * Initializes component state with values from the database or defaults.
* @param {SettingsType} settings - Object containing settings from the database.
*/ */
initializeState(settings: Settings | undefined) { async initializeState() {
await db.open();
const settings: Settings | undefined =
await db.settings.get(MASTER_SETTINGS_KEY);
this.activeDid = (settings?.activeDid as string) || ""; this.activeDid = (settings?.activeDid as string) || "";
this.apiServer = (settings?.apiServer as string) || ""; this.apiServer = (settings?.apiServer as string) || "";
this.apiServerInput = (settings?.apiServer as string) || ""; this.apiServerInput = (settings?.apiServer as string) || "";
@ -778,6 +775,7 @@ export default class AccountViewView extends Vue {
(settings?.firstName || "") + (settings?.firstName || "") +
(settings?.lastName ? ` ${settings.lastName}` : ""); // pre v 0.1.3 (settings?.lastName ? ` ${settings.lastName}` : ""); // pre v 0.1.3
this.isRegistered = !!settings?.isRegistered; this.isRegistered = !!settings?.isRegistered;
this.imageServer = (settings?.imageServer as string) || "";
this.profileImageUrl = settings?.profileImageUrl as string; this.profileImageUrl = settings?.profileImageUrl as string;
this.showContactGives = !!settings?.showContactGivesInline; this.showContactGives = !!settings?.showContactGivesInline;
this.hideRegisterPromptOnNewContact = this.hideRegisterPromptOnNewContact =
@ -790,23 +788,6 @@ export default class AccountViewView extends Vue {
this.webPushServerInput = (settings?.webPushServer as string) || ""; this.webPushServerInput = (settings?.webPushServer as string) || "";
} }
public async getIdentity(activeDid: string): Promise<IIdentifier | null> {
try {
// Open the accounts database
await accountsDB.open();
// Search for the account with the matching DID (decentralized identifier)
const account: { identity?: string } | undefined =
await accountsDB.accounts.where("did").equals(activeDid).first();
// Return parsed identity or null if not found
return JSON.parse((account?.identity as string) || "null");
} catch (error) {
console.error("Failed to find account:", error);
return null;
}
}
// call fn, copy text to the clipboard, then redo fn after 2 seconds // call fn, copy text to the clipboard, then redo fn after 2 seconds
doCopyTwoSecRedo(text: string, fn: () => void) { doCopyTwoSecRedo(text: string, fn: () => void) {
fn(); fn();
@ -846,21 +827,19 @@ export default class AccountViewView extends Vue {
/** /**
* Processes the identity and updates the component's state. * Processes the identity and updates the component's state.
* @param {IdentityType} identity - Object containing identity information.
*/ */
processIdentity(identity: IIdentifier) { async processIdentity() {
if ( const account: Account | undefined = await getAccount(this.activeDid);
identity && if (account?.identity) {
identity.keys && const identity = JSON.parse(account.identity as string) as IIdentifier;
identity.keys.length > 0 &&
identity.keys[0].meta
) {
this.publicHex = identity.keys[0].publicKeyHex; this.publicHex = identity.keys[0].publicKeyHex;
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
this.derivationPath = identity.keys[0].meta?.derivationPath as string; this.derivationPath = identity.keys[0].meta?.derivationPath as string;
this.checkLimitsFor(this.activeDid); this.checkLimitsFor(this.activeDid);
} else { } else if (account?.publicKeyHex) {
// Handle the case where any of these are null or undefined this.publicHex = account.publicKeyHex as string;
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
this.checkLimitsFor(this.activeDid);
} }
} }
@ -1258,11 +1237,7 @@ export default class AccountViewView extends Vue {
); );
} }
} }
const imageResp = await fetchImageRateLimits( const imageResp = await fetchImageRateLimits(this.axios, did);
this.apiServer,
this.axios,
did,
);
if (imageResp.status === 200) { if (imageResp.status === 200) {
this.imageLimits = imageResp.data; this.imageLimits = imageResp.data;
} }
@ -1359,9 +1334,9 @@ export default class AccountViewView extends Vue {
* *
* @param {AccountType} account - The account object. * @param {AccountType} account - The account object.
*/ */
private updateActiveAccountProperties(account: IAccount) { private updateActiveAccountProperties(account: Account) {
this.activeDid = account.did; this.activeDid = account.did;
this.derivationPath = account.derivationPath; this.derivationPath = account.derivationPath || "";
this.publicHex = account.publicKeyHex; this.publicHex = account.publicKeyHex;
this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64"); this.publicBase64 = Buffer.from(this.publicHex, "hex").toString("base64");
} }

2
src/views/ClaimAddRawView.vue

@ -84,7 +84,7 @@ export default class ClaimAddRawView extends Vue {
const fullClaim = JSON.parse(this.claimStr); const fullClaim = JSON.parse(this.claimStr);
const result = await serverUtil.createAndSubmitClaim( const result = await serverUtil.createAndSubmitClaim(
fullClaim, fullClaim,
await this.getIdentity(this.activeDid), this.activeDid,
this.apiServer, this.apiServer,
this.axios, this.axios,
); );

2
src/views/ClaimView.vue

@ -738,7 +738,7 @@ export default class ClaimView extends Vue {
}; };
const result = await serverUtil.createAndSubmitClaim( const result = await serverUtil.createAndSubmitClaim(
confirmationClaim, confirmationClaim,
await this.getIdentity(this.activeDid), this.activeDid,
this.apiServer, this.apiServer,
this.axios, this.axios,
); );

2
src/views/ConfirmGiftView.vue

@ -744,7 +744,7 @@ export default class ClaimView extends Vue {
}; };
const result = await serverUtil.createAndSubmitClaim( const result = await serverUtil.createAndSubmitClaim(
confirmationClaim, confirmationClaim,
await this.getIdentity(this.activeDid), this.activeDid,
this.apiServer, this.apiServer,
this.axios, this.axios,
); );

97
src/views/ContactAmountsView.vue

@ -106,7 +106,6 @@
<script lang="ts"> <script lang="ts">
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import * as didJwt from "did-jwt";
import * as R from "ramda"; import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
@ -115,9 +114,10 @@ import { NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index"; import { accountsDB, db } from "@/db/index";
import { Contact } from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken, SimpleSigner } from "@/libs/crypto"; import { accessToken } from "@/libs/crypto";
import { import {
AgreeVerifiableCredential, AgreeVerifiableCredential,
createEndorserJwtVcFromClaim,
displayAmount, displayAmount,
getHeaders, getHeaders,
GiveSummaryRecord, GiveSummaryRecord,
@ -280,67 +280,48 @@ export default class ContactAmountssView extends Vue {
object: origClaim, object: origClaim,
}; };
// Make a payload for the claim const vcJwt: string = await createEndorserJwtVcFromClaim(
const vcPayload: didJwt.JWTPayload = { this.activeDid,
vc: { vcClaim,
"@context": ["https://www.w3.org/2018/credentials/v1"], );
type: ["VerifiableCredential"],
credentialSubject: vcClaim,
},
};
// Create a signature using private key of identity
const identity = await this.getIdentity(this.activeDid);
if (identity.keys[0].privateKeyHex !== null) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const privateKeyHex: string = identity.keys[0].privateKeyHex!;
const signer = await SimpleSigner(privateKeyHex);
const alg = undefined;
// Create a JWT for the request
const vcJwt: string = await didJwt.createJWT(vcPayload, {
alg: alg,
issuer: identity.did,
signer: signer,
});
// Make the xhr request payload // Make the xhr request payload
const payload = JSON.stringify({ jwtEncoded: vcJwt }); const payload = JSON.stringify({ jwtEncoded: vcJwt });
const url = this.apiServer + "/api/v2/claim"; const url = this.apiServer + "/api/v2/claim";
const token = await accessToken(this.activeDid); const token = await accessToken(this.activeDid);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: "Bearer " + token, Authorization: "Bearer " + token,
}; };
try { try {
const resp = await this.axios.post(url, payload, { headers }); const resp = await this.axios.post(url, payload, { headers });
if (resp.data?.success) { if (resp.data?.success) {
record.amountConfirmed = record.amountConfirmed =
(origClaim.object?.amountOfThisGood as number) || 1; (origClaim.object?.amountOfThisGood as number) || 1;
} }
} catch (error) { } catch (error) {
let userMessage = "There was an error. See logs for more info."; let userMessage = "There was an error. See logs for more info.";
const serverError = error as AxiosError; const serverError = error as AxiosError;
if (serverError) { if (serverError) {
if (serverError.message) { if (serverError.message) {
userMessage = serverError.message; // Info for the user userMessage = serverError.message; // Info for the user
} else {
userMessage = JSON.stringify(serverError.toJSON());
}
} else { } else {
userMessage = error as string; userMessage = JSON.stringify(serverError.toJSON());
} }
// Now set that error for the user to see. } else {
this.$notify( userMessage = error as string;
{
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
// Now set that error for the user to see.
this.$notify(
{
group: "alert",
type: "danger",
title: "Error With Server",
text: userMessage,
},
-1,
);
} }
} }

12
src/views/ContactQRScanShowView.vue

@ -93,7 +93,7 @@ import {
SimpleSigner, SimpleSigner,
} from "@/libs/crypto"; } from "@/libs/crypto";
import { import {
CONTACT_URL_PREFIX, CONTACT_URL_PREFIX, createEndorserJwt,
ENDORSER_JWT_URL_LOCATION, ENDORSER_JWT_URL_LOCATION,
isDid, isDid,
register, register,
@ -161,15 +161,7 @@ export default class ContactQRScanShow extends Vue {
}, },
}; };
const alg = undefined; const vcJwt: string = await createEndorserJwt(identity.did, contactInfo);
const privateKeyHex: string = identity.keys[0].privateKeyHex;
const signer = await SimpleSigner(privateKeyHex);
// create a JWT for the request
const vcJwt: string = await didJwt.createJWT(contactInfo, {
alg: alg,
issuer: identity.did,
signer: signer,
});
const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION; const viewPrefix = CONTACT_URL_PREFIX + ENDORSER_JWT_URL_LOCATION;
this.qrValue = viewPrefix + vcJwt; this.qrValue = viewPrefix + vcJwt;
} }

18
src/views/ContactsView.vue

@ -303,12 +303,11 @@
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import { IndexableType } from "dexie"; import { IndexableType } from "dexie";
import * as R from "ramda"; import * as R from "ramda";
import { IIdentifier } from "@veramo/core";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router"; import { Router } from "vue-router";
import { AppString, NotificationIface } from "@/constants/app"; import { AppString, NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index"; import { db } from "@/db/index";
import { Contact } from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { getContactPayloadFromJwtUrl } from "@/libs/crypto"; import { getContactPayloadFromJwtUrl } from "@/libs/crypto";
@ -327,7 +326,6 @@ import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue"; import EntityIcon from "@/components/EntityIcon.vue";
import GiftedDialog from "@/components/GiftedDialog.vue"; import GiftedDialog from "@/components/GiftedDialog.vue";
import OfferDialog from "@/components/OfferDialog.vue"; import OfferDialog from "@/components/OfferDialog.vue";
import { Account } from "@/db/tables/accounts";
import { Buffer } from "buffer/"; import { Buffer } from "buffer/";
@ -401,20 +399,6 @@ export default class ContactsView extends Vue {
); );
} }
public async getIdentity(activeDid: string): Promise<IIdentifier> {
await accountsDB.open();
const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts) as Account;
const identity = JSON.parse(account?.identity || "null");
if (!identity) {
throw new Error(
"Attempted to load Give records with no identifier available.",
);
}
return identity;
}
async loadGives() { async loadGives() {
if (!this.activeDid) { if (!this.activeDid) {
return; return;

3
src/views/GiftedDetails.vue

@ -549,7 +549,6 @@ export default class GiftedDetails extends Vue {
*/ */
public async recordGive() { public async recordGive() {
try { try {
const identity = await libsUtil.getIdentity(this.activeDid);
const recipientDid = this.givenToRecipient const recipientDid = this.givenToRecipient
? this.recipientDid ? this.recipientDid
: undefined; : undefined;
@ -557,7 +556,7 @@ export default class GiftedDetails extends Vue {
const result = await createAndSubmitGive( const result = await createAndSubmitGive(
this.axios, this.axios,
this.apiServer, this.apiServer,
identity, this.activeDid,
this.giverDid, this.giverDid,
recipientDid, recipientDid,
this.description, this.description,

157
src/views/NewEditProjectView.vue

@ -174,7 +174,6 @@
<script lang="ts"> <script lang="ts">
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import * as didJwt from "did-jwt";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
@ -184,10 +183,13 @@ import QuickNav from "@/components/QuickNav.vue";
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app"; import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index"; import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken, SimpleSigner } from "@/libs/crypto"; import { accessToken } from "@/libs/crypto";
import * as libsUtil from "@/libs/util"; import * as libsUtil from "@/libs/util";
import { useAppStore } from "@/store/app"; import { useAppStore } from "@/store/app";
import { PlanVerifiableCredential } from "@/libs/endorserServer"; import {
createEndorserJwtVcFromClaim,
PlanVerifiableCredential,
} from "@/libs/endorserServer";
import ImageMethodDialog from "@/components/ImageMethodDialog.vue"; import ImageMethodDialog from "@/components/ImageMethodDialog.vue";
@Component({ @Component({
@ -430,113 +432,88 @@ export default class NewEditProjectView extends Vue {
} else { } else {
delete vcClaim.startTime; delete vcClaim.startTime;
} }
// Make a payload for the claim const vcJwt = await createEndorserJwtVcFromClaim(identity.did, vcClaim);
const vcPayload = {
vc: {
"@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiableCredential"],
credentialSubject: vcClaim,
},
};
// create a signature using private key of identity
if (identity.keys[0].privateKeyHex != null) {
const privateKeyHex: string = identity.keys[0].privateKeyHex;
const signer = await SimpleSigner(privateKeyHex);
const alg = undefined;
// create a JWT for the request
const vcJwt: string = await didJwt.createJWT(
vcPayload as Partial<didJwt.JWTPayload>,
{
alg: alg,
issuer: identity.did,
signer: signer,
},
);
// Make the xhr request payload // Make the xhr request payload
const payload = JSON.stringify({ jwtEncoded: vcJwt }); const payload = JSON.stringify({ jwtEncoded: vcJwt });
const url = this.apiServer + "/api/v2/claim"; const url = this.apiServer + "/api/v2/claim";
const token = await accessToken(identity.did); const token = await accessToken(identity.did);
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: "Bearer " + token, Authorization: "Bearer " + token,
}; };
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.errorMessage = ""; this.errorMessage = "";
useAppStore() useAppStore()
.setProjectId(resp.data.success.handleId) .setProjectId(resp.data.success.handleId)
.then(() => { .then(() => {
this.$router.push({ name: "project" }); this.$router.push({ name: "project" });
}); });
} else { } else {
console.error( console.error(
"Got unexpected 'data' inside response from server", "Got unexpected 'data' inside response from server",
resp, resp,
); );
this.$notify(
{
group: "alert",
type: "danger",
title: "Error Saving Idea",
text: "Server did not save the idea. Try again.",
},
-1,
);
}
} catch (error) {
let userMessage = "There was an error saving the project.";
const serverError = error as AxiosError<{
error?: { message?: string };
}>;
if (serverError) {
console.error("Got error from server", serverError);
if (Object.prototype.hasOwnProperty.call(serverError, "message")) {
userMessage =
(serverError.response?.data?.error?.message as string) ||
userMessage;
this.$notify( this.$notify(
{ {
group: "alert", group: "alert",
type: "danger", type: "danger",
title: "Error Saving Idea", title: "User Message",
text: "Server did not save the idea. Try again.", text: userMessage,
}, },
-1, -1,
); );
}
} catch (error) {
let userMessage = "There was an error saving the project.";
const serverError = error as AxiosError<{
error?: { message?: string };
}>;
if (serverError) {
console.error("Got error from server", serverError);
if (Object.prototype.hasOwnProperty.call(serverError, "message")) {
userMessage =
(serverError.response?.data?.error?.message as string) ||
userMessage;
this.$notify(
{
group: "alert",
type: "danger",
title: "User Message",
text: userMessage,
},
-1,
);
} else {
this.$notify(
{
group: "alert",
type: "danger",
title: "Server Message",
text: JSON.stringify(serverError.toJSON()),
},
-1,
);
}
} else { } else {
console.error(
"Here's the full error trying to save the claim:",
error,
);
this.$notify( this.$notify(
{ {
group: "alert", group: "alert",
type: "danger", type: "danger",
title: "Claim Error", title: "Server Message",
text: error as string, text: JSON.stringify(serverError.toJSON()),
}, },
-1, -1,
); );
} }
// Now set that error for the user to see. } else {
this.errorMessage = userMessage; console.error("Here's the full error trying to save the claim:", error);
this.$notify(
{
group: "alert",
type: "danger",
title: "Claim Error",
text: error as string,
},
-1,
);
} }
// Now set that error for the user to see.
this.errorMessage = userMessage;
} }
} }

2
src/views/ProjectViewView.vue

@ -981,7 +981,7 @@ export default class ProjectViewView extends Vue {
}; };
const result = await serverUtil.createAndSubmitClaim( const result = await serverUtil.createAndSubmitClaim(
confirmationClaim, confirmationClaim,
await this.getIdentity(this.activeDid), this.activeDid,
this.apiServer, this.apiServer,
this.axios, this.axios,
); );

5
src/views/QuickActionBvcBeginView.vue

@ -124,7 +124,6 @@ export default class QuickActionBvcBeginView extends Vue {
try { try {
const hoursNum = libsUtil.numberOrZero(this.hoursStr); const hoursNum = libsUtil.numberOrZero(this.hoursStr);
const identity = await libsUtil.getIdentity(activeDid);
this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000); this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000);
@ -134,7 +133,7 @@ export default class QuickActionBvcBeginView extends Vue {
const timeResult = await createAndSubmitGive( const timeResult = await createAndSubmitGive(
axios, axios,
apiServer, apiServer,
identity, activeDid,
activeDid, activeDid,
undefined, undefined,
undefined, undefined,
@ -165,7 +164,7 @@ export default class QuickActionBvcBeginView extends Vue {
if (this.attended) { if (this.attended) {
const attendResult = await createAndSubmitClaim( const attendResult = await createAndSubmitClaim(
bvcMeetingJoinClaim(activeDid, this.todayOrPreviousStartDate), bvcMeetingJoinClaim(activeDid, this.todayOrPreviousStartDate),
identity, activeDid,
apiServer, apiServer,
axios, axios,
); );

8
src/views/QuickActionBvcEndView.vue

@ -157,7 +157,6 @@ import {
getHeaders, getHeaders,
ErrorResult, ErrorResult,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util";
@Component({ @Component({
methods: { claimSpecialDescription }, methods: { claimSpecialDescription },
@ -264,8 +263,6 @@ export default class QuickActionBvcBeginView extends Vue {
async record() { async record() {
try { try {
const identity = await libsUtil.getIdentity(this.activeDid);
this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000); this.$notify({ group: "alert", type: "toast", title: "Sent..." }, 1000);
// in parallel, make a confirmation for each selected claim and send them all to the server // in parallel, make a confirmation for each selected claim and send them all to the server
@ -277,9 +274,8 @@ export default class QuickActionBvcBeginView extends Vue {
if (!record) { if (!record) {
return { type: "error", error: "Record not found." }; return { type: "error", error: "Record not found." };
} }
const identity = await libsUtil.getIdentity(this.activeDid);
return createAndSubmitConfirmation( return createAndSubmitConfirmation(
identity, this.activeDid,
record.claim as GenericVerifiableCredential, record.claim as GenericVerifiableCredential,
record.id, record.id,
record.handleId, record.handleId,
@ -313,7 +309,7 @@ export default class QuickActionBvcBeginView extends Vue {
const giveResult = await createAndSubmitGive( const giveResult = await createAndSubmitGive(
axios, axios,
this.apiServer, this.apiServer,
identity, this.activeDid,
undefined, undefined,
this.activeDid, this.activeDid,
this.description, this.description,

Loading…
Cancel
Save