forked from jsnbuchanan/crowd-funder-for-time-pwa
catch more errors if something catastrophic happens to encrypted data
This commit is contained in:
@@ -4,10 +4,10 @@ import { sha256 } from "ethereum-cryptography/sha256";
|
||||
import { LRUCache } from "lru-cache";
|
||||
import * as R from "ramda";
|
||||
|
||||
import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app";
|
||||
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
import { accessToken, deriveAddress, nextDerivationPath } from "@/libs/crypto";
|
||||
import { NonsensitiveDexie } from "@/db/index";
|
||||
import { logConsoleAndDb, NonsensitiveDexie } from "@/db/index";
|
||||
import {
|
||||
retrieveAccountMetadata,
|
||||
retrieveFullyDecryptedAccount,
|
||||
@@ -501,35 +501,72 @@ export function tokenExpiryTimeDescription() {
|
||||
/**
|
||||
* Get the headers for a request, potentially including Authorization
|
||||
*/
|
||||
export async function getHeaders(did?: string) {
|
||||
export async function getHeaders(
|
||||
did?: string,
|
||||
$notify?: (notification: NotificationIface, timeout?: number) => void,
|
||||
failureMessage?: string,
|
||||
) {
|
||||
const headers: { "Content-Type": string; Authorization?: string } = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (did) {
|
||||
let token;
|
||||
const account = await retrieveAccountMetadata(did);
|
||||
if (account?.passkeyCredIdHex) {
|
||||
if (
|
||||
passkeyAccessToken &&
|
||||
passkeyTokenExpirationEpochSeconds > Date.now() / 1000
|
||||
) {
|
||||
// there's an active current passkey token
|
||||
token = passkeyAccessToken;
|
||||
} else {
|
||||
// there's no current passkey token or it's expired
|
||||
token = await accessToken(did);
|
||||
try {
|
||||
let token;
|
||||
const account = await retrieveAccountMetadata(did);
|
||||
if (account?.passkeyCredIdHex) {
|
||||
if (
|
||||
passkeyAccessToken &&
|
||||
passkeyTokenExpirationEpochSeconds > Date.now() / 1000
|
||||
) {
|
||||
// there's an active current passkey token
|
||||
token = passkeyAccessToken;
|
||||
} else {
|
||||
// there's no current passkey token or it's expired
|
||||
token = await accessToken(did);
|
||||
|
||||
passkeyAccessToken = token;
|
||||
const passkeyExpirationSeconds = await getPasskeyExpirationSeconds();
|
||||
passkeyTokenExpirationEpochSeconds =
|
||||
Date.now() / 1000 + passkeyExpirationSeconds;
|
||||
passkeyAccessToken = token;
|
||||
const passkeyExpirationSeconds = await getPasskeyExpirationSeconds();
|
||||
passkeyTokenExpirationEpochSeconds =
|
||||
Date.now() / 1000 + passkeyExpirationSeconds;
|
||||
}
|
||||
} else {
|
||||
token = await accessToken(did);
|
||||
}
|
||||
headers["Authorization"] = "Bearer " + token;
|
||||
} catch (error) {
|
||||
// This rarely happens: we've seen it when they have account info but the
|
||||
// encryption secret got lost. But in most cases we want users to at
|
||||
// least see their feed -- and anything else that returns results for
|
||||
// anonymous users.
|
||||
|
||||
// We'll continue with an anonymous request... still want to show feed and other things, but ideally let them know.
|
||||
logConsoleAndDb(
|
||||
"Something failed in getHeaders call (will proceed anonymously" +
|
||||
($notify ? " and notify user" : "") +
|
||||
"): " +
|
||||
// IntelliJ type system complains about getCircularReplacer() with: Argument of type '(obj: any, key: string, value: any) => any' is not assignable to parameter of type '(this: any, key: string, value: any) => any'.
|
||||
//JSON.stringify(error, getCircularReplacer()), // JSON.stringify(error) on a Dexie error throws another error about: Converting circular structure to JSON
|
||||
error,
|
||||
true,
|
||||
);
|
||||
if ($notify) {
|
||||
// remember: only want to do this if they supplied a DID, expecting personal results
|
||||
const notifyMessage =
|
||||
failureMessage ||
|
||||
"Showing anonymous data. See the Help page for help with personal data.";
|
||||
$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Personal Data Error",
|
||||
text: notifyMessage,
|
||||
},
|
||||
3000,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
token = await accessToken(did);
|
||||
}
|
||||
headers["Authorization"] = "Bearer " + token;
|
||||
} else {
|
||||
// it's often OK to request without auth; we assume necessary checks are done earlier
|
||||
// it's usually OK to request without auth; we assume we're only here when allowed
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
@@ -611,6 +648,7 @@ export async function getNewOffersToUser(
|
||||
url += "&beforeId=" + beforeOfferJwtId;
|
||||
}
|
||||
const headers = await getHeaders(activeDid);
|
||||
console.log("Using headers: ", headers);
|
||||
const response = await axios.get(url, { headers });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -147,6 +147,23 @@ export interface ConfirmerData {
|
||||
numConfsNotVisible: number;
|
||||
}
|
||||
|
||||
// // This is meant to be a second argument to JSON.stringify to avoid circular references.
|
||||
// // Usage: JSON.stringify(error, getCircularReplacer())
|
||||
// // Beware: we've seen this return "undefined" when there is actually a message, eg: DatabaseClosedError: Error DEXIE ENCRYPT ADDON: Encryption key has changed
|
||||
// function getCircularReplacer() {
|
||||
// const seen = new WeakSet();
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// return (obj: any, key: string, value: any): any => {
|
||||
// if (typeof value === "object" && value !== null) {
|
||||
// if (seen.has(value)) {
|
||||
// return "[circular ref]";
|
||||
// }
|
||||
// seen.add(value);
|
||||
// }
|
||||
// return value;
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* @return only confirmers, excluding the issuer and hidden DIDs
|
||||
*/
|
||||
@@ -423,11 +440,13 @@ export function findAllVisibleToDids(
|
||||
export interface AccountKeyInfo extends Account, KeyMeta {}
|
||||
|
||||
export const retrieveAccountCount = async (): Promise<number> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
return await accountsDB.accounts.count();
|
||||
};
|
||||
|
||||
export const retrieveAccountDids = async (): Promise<string[]> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const allAccounts = await accountsDB.accounts.toArray();
|
||||
const allDids = allAccounts.map((acc) => acc.did);
|
||||
@@ -439,6 +458,7 @@ export const retrieveAccountDids = async (): Promise<string[]> => {
|
||||
export const retrieveAccountMetadata = async (
|
||||
activeDid: string,
|
||||
): Promise<AccountKeyInfo | undefined> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const account = (await accountsDB.accounts
|
||||
.where("did")
|
||||
@@ -454,6 +474,7 @@ export const retrieveAccountMetadata = async (
|
||||
};
|
||||
|
||||
export const retrieveAllAccountsMetadata = async (): Promise<Account[]> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const array = await accountsDB.accounts.toArray();
|
||||
return array.map((account) => {
|
||||
@@ -466,6 +487,7 @@ export const retrieveAllAccountsMetadata = async (): Promise<Account[]> => {
|
||||
export const retrieveFullyDecryptedAccount = async (
|
||||
activeDid: string,
|
||||
): Promise<AccountKeyInfo | undefined> => {
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
const account = (await accountsDB.accounts
|
||||
.where("did")
|
||||
@@ -496,6 +518,7 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
|
||||
const newId = newIdentifier(address, publicHex, privateHex, derivationPath);
|
||||
const identity = JSON.stringify(newId);
|
||||
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
await accountsDB.accounts.add({
|
||||
dateCreated: new Date().toISOString(),
|
||||
@@ -527,6 +550,7 @@ export const registerAndSavePasskey = async (
|
||||
passkeyCredIdHex,
|
||||
publicKeyHex: Buffer.from(publicKeyBytes).toString("hex"),
|
||||
};
|
||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||
const accountsDB = await accountsDBPromise;
|
||||
await accountsDB.accounts.add(account);
|
||||
return account;
|
||||
|
||||
Reference in New Issue
Block a user