WIP: BROKEN FOR ELECTRON: Fixes in progress

This commit is contained in:
Matthew Raymer
2025-05-28 13:09:51 +00:00
parent 25b269bbfc
commit 473a115841
20 changed files with 671 additions and 615 deletions

View File

@@ -194,7 +194,9 @@ export class PeerSetup {
},
};
const credential = await navigator.credentials.get(options) as PublicKeyCredential;
const credential = (await navigator.credentials.get(
options,
)) as PublicKeyCredential;
// console.log("nav credential get", credential);
const response = credential?.response as AuthenticatorAssertionResponse;
@@ -229,9 +231,7 @@ export class PeerSetup {
.replace(/\//g, "_")
.replace(/=+$/, "");
const origSignature = Buffer.from(response?.signature).toString(
"base64",
);
const origSignature = Buffer.from(response?.signature).toString("base64");
this.signature = origSignature
.replace(/\+/g, "-")
.replace(/\//g, "_")
@@ -327,10 +327,7 @@ export async function verifyJwtP256(
const publicKeyBytes = peerDidToPublicKeyBytes(issuerDid);
// Use challenge in preimage construction
const preimage = Buffer.concat([
authDataFromBase,
Buffer.from(challenge),
]);
const preimage = Buffer.concat([authDataFromBase, Buffer.from(challenge)]);
const isValid = p256.verify(
finalSigBuffer,
@@ -391,10 +388,7 @@ export async function verifyJwtWebCrypto(
const finalSigBuffer = unwrapEC2Signature(sigBuffer);
// Use challenge in preimage construction
const preimage = Buffer.concat([
authDataFromBase,
Buffer.from(challenge),
]);
const preimage = Buffer.concat([authDataFromBase, Buffer.from(challenge)]);
return verifyPeerSignature(preimage, issuerDid, finalSigBuffer);
}

View File

@@ -53,7 +53,7 @@ import {
ClaimObject,
VerifiableCredentialClaim,
Agent,
QuantitativeValue
QuantitativeValue,
} from "../interfaces/common";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
@@ -106,11 +106,12 @@ export const CONTACT_CONFIRM_URL_PATH_TIME_SAFARI = "/contact/confirm/";
*/
export const ENDORSER_CH_HANDLE_PREFIX = "https://endorser.ch/entity/";
export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper<GenericVerifiableCredential> = {
claim: {
"@context": SCHEMA_ORG_CONTEXT,
"@type": ""
},
export const BLANK_GENERIC_SERVER_RECORD: GenericCredWrapper<GenericVerifiableCredential> =
{
claim: {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "",
},
handleId: "",
id: "",
issuedAt: "",
@@ -200,10 +201,10 @@ const testRecursivelyOnStrings = (
return input.some((item) => testRecursivelyOnStrings(item, test));
} else if (input && typeof input === "object") {
return Object.values(input as Record<string, unknown>).some((value) =>
testRecursivelyOnStrings(value, test)
testRecursivelyOnStrings(value, test),
);
}
return false;
}
return false;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -545,7 +546,7 @@ export async function setPlanInCache(
* @returns {string|undefined} User-friendly message or undefined if none found
*/
export function serverMessageForUser(error: unknown): string | undefined {
if (error && typeof error === 'object' && 'response' in error) {
if (error && typeof error === "object" && "response" in error) {
const err = error as AxiosErrorResponse;
return err.response?.data?.error?.message;
}
@@ -571,21 +572,26 @@ export function errorStringForLog(error: unknown) {
// --- property '_value' closes the circle
}
let fullError = "" + error + " - JSON: " + stringifiedError;
if (error && typeof error === 'object' && 'response' in error) {
if (error && typeof error === "object" && "response" in error) {
const err = error as AxiosErrorResponse;
const errorResponseText = JSON.stringify(err.response);
// for some reason, error.response is not included in stringify result (eg. for 400 errors on invite redemptions)
if (!R.empty(errorResponseText) && !fullError.includes(errorResponseText)) {
// add error.response stuff
if (err.response?.config && err.config && R.equals(err.config, err.response.config)) {
// but exclude "config" because it's already in there
const newErrorResponseText = JSON.stringify(
// for some reason, error.response is not included in stringify result (eg. for 400 errors on invite redemptions)
if (!R.empty(errorResponseText) && !fullError.includes(errorResponseText)) {
// add error.response stuff
if (
err.response?.config &&
err.config &&
R.equals(err.config, err.response.config)
) {
// but exclude "config" because it's already in there
const newErrorResponseText = JSON.stringify(
R.omit(["config"] as never[], err.response),
);
fullError += " - .response w/o same config JSON: " + newErrorResponseText;
} else {
fullError += " - .response JSON: " + errorResponseText;
);
fullError +=
" - .response w/o same config JSON: " + newErrorResponseText;
} else {
fullError += " - .response JSON: " + errorResponseText;
}
}
}
@@ -664,7 +670,7 @@ export function hydrateGive(
"@type": "GiveAction",
object: undefined,
agent: undefined,
fulfills: []
fulfills: [],
};
if (lastClaimId) {
@@ -679,13 +685,13 @@ export function hydrateGive(
vcClaim.recipient = { identifier: toDid };
}
vcClaim.description = description || undefined;
if (amount && !isNaN(amount)) {
const quantitativeValue: QuantitativeValue = {
"@context": SCHEMA_ORG_CONTEXT,
"@type": "QuantitativeValue",
amountOfThisGood: amount,
unitCode: unitCode || "HUR"
unitCode: unitCode || "HUR",
};
vcClaim.object = quantitativeValue;
}
@@ -697,34 +703,34 @@ export function hydrateGive(
// Filter and add fulfills elements
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem: { '@type': string }) => elem["@type"] !== "PlanAction"
(elem: { "@type": string }) => elem["@type"] !== "PlanAction",
);
if (fulfillsProjectHandleId) {
vcClaim.fulfills.push({
"@type": "PlanAction",
identifier: fulfillsProjectHandleId
identifier: fulfillsProjectHandleId,
});
}
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem: { '@type': string }) => elem["@type"] !== "Offer"
(elem: { "@type": string }) => elem["@type"] !== "Offer",
);
if (fulfillsOfferHandleId) {
vcClaim.fulfills.push({
"@type": "Offer",
identifier: fulfillsOfferHandleId
identifier: fulfillsOfferHandleId,
});
}
vcClaim.fulfills = vcClaim.fulfills.filter(
(elem: { '@type': string }) =>
elem["@type"] !== "DonateAction" && elem["@type"] !== "TradeAction"
(elem: { "@type": string }) =>
elem["@type"] !== "DonateAction" && elem["@type"] !== "TradeAction",
);
vcClaim.fulfills.push({
"@type": isTrade ? "TradeAction" : "DonateAction"
vcClaim.fulfills.push({
"@type": isTrade ? "TradeAction" : "DonateAction",
});
vcClaim.image = imageUrl || undefined;
@@ -732,7 +738,7 @@ export function hydrateGive(
if (providerPlanHandleId) {
vcClaim.provider = {
"@type": "PlanAction",
identifier: providerPlanHandleId
identifier: providerPlanHandleId,
};
}
@@ -854,7 +860,7 @@ export function hydrateOffer(
"@type": "OfferAction",
object: undefined,
agent: undefined,
itemOffered: {}
itemOffered: {},
};
if (lastClaimId) {
@@ -875,7 +881,7 @@ export function hydrateOffer(
"@context": SCHEMA_ORG_CONTEXT,
"@type": "QuantitativeValue",
amountOfThisGood: amount,
unitCode: unitCode || "HUR"
unitCode: unitCode || "HUR",
};
vcClaim.object = quantitativeValue;
}
@@ -886,7 +892,7 @@ export function hydrateOffer(
if (fulfillsProjectHandleId) {
vcClaim.itemOffered.isPartOf = {
"@type": "PlanAction",
identifier: fulfillsProjectHandleId
identifier: fulfillsProjectHandleId,
};
}
}
@@ -1024,12 +1030,14 @@ export async function createAndSubmitClaim(
logger.error("Error submitting claim:", error);
const errorMessage: string =
serverMessageForUser(error) ||
(error && typeof error === 'object' && 'message' in error ? String(error.message) : undefined) ||
(error && typeof error === "object" && "message" in error
? String(error.message)
: undefined) ||
"Got some error submitting the claim. Check your permissions, network, and error logs.";
return {
success: false,
error: errorMessage
error: errorMessage,
};
}
}
@@ -1061,10 +1069,7 @@ export async function generateEndorserJwtUrlForAccount(
// Add the next key -- not recommended for the QR code for such a high resolution
if (isContact) {
const newDerivPath = nextDerivationPath(account.derivationPath);
const nextPublicHex = deriveAddress(
account.mnemonic,
newDerivPath,
)[2];
const nextPublicHex = deriveAddress(account.mnemonic, newDerivPath)[2];
const nextPublicEncKey = Buffer.from(nextPublicHex, "hex");
const nextPublicEncKeyHash = sha256(nextPublicEncKey);
const nextPublicEncKeyHashBase64 =
@@ -1133,13 +1138,15 @@ export const capitalizeAndInsertSpacesBeforeCaps = (text: string) => {
similar code is also contained in endorser-mobile
**/
const claimSummary = (
claim: GenericVerifiableCredential | GenericCredWrapper<GenericVerifiableCredential>,
claim:
| GenericVerifiableCredential
| GenericCredWrapper<GenericVerifiableCredential>,
) => {
if (!claim) {
return "something";
}
let specificClaim: GenericVerifiableCredential;
if ('claim' in claim) {
if ("claim" in claim) {
// It's a GenericCredWrapper
specificClaim = claim.claim as GenericVerifiableCredential;
} else {
@@ -1180,7 +1187,7 @@ export const claimSpecialDescription = (
contacts: Array<Contact>,
) => {
let claim = record.claim;
if ('claim' in claim) {
if ("claim" in claim) {
claim = claim.claim as GenericVerifiableCredential;
}
@@ -1189,12 +1196,23 @@ export const claimSpecialDescription = (
const type = claimObj["@type"] || "UnknownType";
if (type === "AgreeAction") {
return issuer + " agreed with " + claimSummary(claimObj.object as GenericVerifiableCredential);
return (
issuer +
" agreed with " +
claimSummary(claimObj.object as GenericVerifiableCredential)
);
} else if (isAccept(claim)) {
return issuer + " accepted " + claimSummary(claimObj.object as GenericVerifiableCredential);
return (
issuer +
" accepted " +
claimSummary(claimObj.object as GenericVerifiableCredential)
);
} else if (type === "GiveAction") {
const giveClaim = claim as GiveVerifiableCredential;
const agent: Agent = giveClaim.agent || { identifier: undefined, did: undefined };
const agent: Agent = giveClaim.agent || {
identifier: undefined,
did: undefined,
};
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
@@ -1209,7 +1227,10 @@ export const claimSpecialDescription = (
return contactInfo + " gave" + offering + recipientInfo;
} else if (type === "JoinAction") {
const joinClaim = claim as ClaimObject;
const agent: Agent = joinClaim.agent || { identifier: undefined, did: undefined };
const agent: Agent = joinClaim.agent || {
identifier: undefined,
did: undefined,
};
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
@@ -1219,7 +1240,10 @@ export const claimSpecialDescription = (
return contactInfo + " joined" + objectInfo;
} else if (isOffer(claim)) {
const offerClaim = claim as OfferVerifiableCredential;
const agent: Agent = offerClaim.agent || { identifier: undefined, did: undefined };
const agent: Agent = offerClaim.agent || {
identifier: undefined,
did: undefined,
};
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
@@ -1234,7 +1258,10 @@ export const claimSpecialDescription = (
return contactInfo + " offered" + offering + offerRecipientInfo;
} else if (type === "PlanAction") {
const planClaim = claim as ClaimObject;
const agent: Agent = planClaim.agent || { identifier: undefined, did: undefined };
const agent: Agent = planClaim.agent || {
identifier: undefined,
did: undefined,
};
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
@@ -1244,7 +1271,10 @@ export const claimSpecialDescription = (
return contactInfo + " planned" + objectInfo;
} else if (type === "Tenure") {
const tenureClaim = claim as ClaimObject;
const agent: Agent = tenureClaim.agent || { identifier: undefined, did: undefined };
const agent: Agent = tenureClaim.agent || {
identifier: undefined,
did: undefined,
};
const agentDid = agent.did || agent.identifier;
const contactInfo = agentDid
? didInfo(agentDid, activeDid, identifiers, contacts)
@@ -1253,11 +1283,7 @@ export const claimSpecialDescription = (
const objectInfo = object ? " " + claimSummary(object) : "";
return contactInfo + " has tenure" + objectInfo;
} else {
return (
issuer +
" declared " +
claimSummary(claim)
);
return issuer + " declared " + claimSummary(claim);
}
};
@@ -1333,35 +1359,39 @@ export async function register(
contact: Contact,
): Promise<{ success?: boolean; error?: string }> {
try {
const vcJwt = await createInviteJwt(activeDid, contact);
const url = apiServer + "/api/v2/claim";
const resp = await axios.post<{
success?: {
handleId?: string;
const vcJwt = await createInviteJwt(activeDid, contact);
const url = apiServer + "/api/v2/claim";
const resp = await axios.post<{
success?: {
handleId?: string;
embeddedRecordError?: string;
};
error?: string;
message?: string;
}>(url, { jwtEncoded: vcJwt });
if (resp.data?.success?.handleId) {
return { success: true };
} else if (resp.data?.success?.embeddedRecordError) {
let message = "There was some problem with the registration and so it may not be complete.";
if (resp.data?.success?.handleId) {
return { success: true };
} else if (resp.data?.success?.embeddedRecordError) {
let message =
"There was some problem with the registration and so it may not be complete.";
if (typeof resp.data.success.embeddedRecordError === "string") {
message += " " + resp.data.success.embeddedRecordError;
}
return { error: message };
} else {
message += " " + resp.data.success.embeddedRecordError;
}
return { error: message };
} else {
logger.error("Registration error:", JSON.stringify(resp.data));
return { error: "Got a server error when registering." };
}
} catch (error: unknown) {
if (error && typeof error === 'object') {
if (error && typeof error === "object") {
const err = error as AxiosErrorResponse;
const errorMessage = err.message ||
(err.response?.data && typeof err.response.data === 'object' && 'message' in err.response.data
? (err.response.data as { message: string }).message
const errorMessage =
err.message ||
(err.response?.data &&
typeof err.response.data === "object" &&
"message" in err.response.data
? (err.response.data as { message: string }).message
: undefined);
logger.error("Registration error:", errorMessage || JSON.stringify(err));
return { error: errorMessage || "Got a server error when registering." };

View File

@@ -30,19 +30,13 @@ import {
simpleEncrypt,
} from "../libs/crypto";
import * as serverUtil from "../libs/endorserServer";
import {
containsHiddenDid,
} from "../libs/endorserServer";
import { containsHiddenDid } from "../libs/endorserServer";
import {
GenericCredWrapper,
GenericVerifiableCredential,
} from "../interfaces/common";
import {
GiveSummaryRecord,
} from "../interfaces/records";
import {
OfferVerifiableCredential,
} from "../interfaces/claims";
import { GiveSummaryRecord } from "../interfaces/records";
import { OfferVerifiableCredential } from "../interfaces/claims";
import { KeyMeta } from "../interfaces/common";
import { createPeerDid } from "../libs/crypto/vc/didPeer";
import { registerCredential } from "../libs/crypto/vc/passkeyDidPeer";
@@ -406,10 +400,7 @@ export function offerGiverDid(
export const canFulfillOffer = (
veriClaim: GenericCredWrapper<GenericVerifiableCredential>,
) => {
return (
veriClaim.claimType === "Offer" &&
!!offerGiverDid(veriClaim)
);
return veriClaim.claimType === "Offer" && !!offerGiverDid(veriClaim);
};
// return object with paths and arrays of DIDs for any keys ending in "VisibleToDid"
@@ -478,8 +469,10 @@ export function findAllVisibleToDids(
*
**/
export interface AccountKeyInfo extends Omit<Account, 'derivationPath'>, Omit<KeyMeta, 'derivationPath'> {
derivationPath?: string; // Make it optional to match Account type
export interface AccountKeyInfo
extends Omit<Account, "derivationPath">,
Omit<KeyMeta, "derivationPath"> {
derivationPath?: string; // Make it optional to match Account type
}
export const retrieveAccountCount = async (): Promise<number> => {
@@ -489,7 +482,7 @@ export const retrieveAccountCount = async (): Promise<number> => {
`SELECT COUNT(*) FROM accounts`,
);
if (dbResult?.values?.[0]?.[0]) {
result = dbResult.values[0][0] as number;
result = dbResult.values[0][0] as number;
}
if (USE_DEXIE_DB) {
@@ -638,7 +631,7 @@ export const retrieveAllFullyDecryptedAccounts = async (): Promise<
) as unknown as AccountEncrypted[];
if (USE_DEXIE_DB) {
const accountsDB = await accountsDBPromise;
allAccounts = await accountsDB.accounts.toArray() as AccountEncrypted[];
allAccounts = (await accountsDB.accounts.toArray()) as AccountEncrypted[];
}
return allAccounts;
};