Browse Source

add expiration inside JWANT & refactor getHeaders to move toward supporting did:peer

Trent Larson 5 months ago
parent
commit
c200cdbead
  1. 23
      src/libs/crypto/index.ts
  2. 4
      src/libs/crypto/passkeyHelpers.ts
  3. 29
      src/libs/didPeer.ts
  4. 74
      src/libs/endorserServer.ts
  5. 7
      src/libs/util.ts
  6. 39
      src/views/AccountViewView.vue
  7. 14
      src/views/ClaimAddRawView.vue
  8. 40
      src/views/ClaimView.vue
  9. 27
      src/views/ConfirmGiftView.vue
  10. 20
      src/views/ContactAmountsView.vue
  11. 31
      src/views/ContactGiftingView.vue
  12. 7
      src/views/ContactQRScanShowView.vue
  13. 36
      src/views/ContactsView.vue
  14. 2
      src/views/GiftedDetails.vue
  15. 40
      src/views/HomeView.vue
  16. 16
      src/views/NewEditProjectView.vue
  17. 2
      src/views/TestView.vue

23
src/libs/crypto/index.ts

@ -6,7 +6,8 @@ import { HDNode } from "@ethersproject/hdnode";
import * as didJwt from "did-jwt"; import * as didJwt from "did-jwt";
import * as u8a from "uint8arrays"; import * as u8a from "uint8arrays";
import { ENDORSER_JWT_URL_LOCATION } from "@/libs/endorserServer"; import { Account } from "@/db/tables/accounts";
import { createEndorserJwt, ENDORSER_JWT_URL_LOCATION } from "@/libs/endorserServer";
import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup"; import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup";
export const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'"; export const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'";
@ -88,9 +89,20 @@ export const generateSeed = (): string => {
* @param {IIdentifier} identifier * @param {IIdentifier} identifier
* @return {*} * @return {*}
*/ */
export const accessToken = async (identifier: IIdentifier) => { export const accessToken = async (
const did: string = identifier.did; identifier: IIdentifier | undefined,
const privateKeyHex: string = identifier.keys[0].privateKeyHex as string; did?: string,
) => {
if (did) {
const nowEpoch = Math.floor(Date.now() / 1000);
const endEpoch = nowEpoch + 60; // add one minute
const tokenPayload = { exp: endEpoch, iat: nowEpoch, iss: did };
return createEndorserJwt(did, tokenPayload);
} else {
// deprecated
// must have identifier
const did = identifier?.did;
const privateKeyHex: string = identifier?.keys[0].privateKeyHex as string;
const signer = SimpleSigner(privateKeyHex); const signer = SimpleSigner(privateKeyHex);
@ -101,10 +113,11 @@ export const accessToken = async (identifier: IIdentifier) => {
const alg = undefined; // defaults to 'ES256K', more standardized but harder to verify vs ES256K-R const alg = undefined; // defaults to 'ES256K', more standardized but harder to verify vs ES256K-R
const jwt: string = await didJwt.createJWT(tokenPayload, { const jwt: string = await didJwt.createJWT(tokenPayload, {
alg, alg,
issuer: did, issuer: did || "no DID set",
signer, signer,
}); });
return jwt; return jwt;
}
}; };
export const sign = async (privateKeyHex: string) => { export const sign = async (privateKeyHex: string) => {

4
src/libs/crypto/passkeyHelpers.ts

@ -88,7 +88,7 @@ export function getWebCrypto(): Promise<{ subtle: SubtleCrypto }> {
); );
return toResolve; return toResolve;
} }
export class MissingWebCrypto extends Error { class MissingWebCrypto extends Error {
constructor() { constructor() {
const message = "An instance of the Crypto API could not be located"; const message = "An instance of the Crypto API could not be located";
super(message); super(message);
@ -96,7 +96,7 @@ export class MissingWebCrypto extends Error {
} }
} }
// Make it possible to stub return values during testing // Make it possible to stub return values during testing
export const _getWebCryptoInternals = { const _getWebCryptoInternals = {
stubThisGlobalThisCrypto: () => globalThis.crypto, stubThisGlobalThisCrypto: () => globalThis.crypto,
// Make it possible to reset the `webCrypto` at the top of the file // Make it possible to reset the `webCrypto` at the top of the file
setCachedCrypto: (newCrypto: { subtle: SubtleCrypto }) => { setCachedCrypto: (newCrypto: { subtle: SubtleCrypto }) => {

29
src/libs/didPeer.ts

@ -117,13 +117,17 @@ export class PeerSetup {
issuerDid: string, issuerDid: string,
payload: object, payload: object,
credIdHex: string, credIdHex: string,
expMinutes: number = 1,
) { ) {
const credentialId = arrayBufferToBase64URLString( const credentialId = arrayBufferToBase64URLString(
Buffer.from(credIdHex, "hex").buffer, Buffer.from(credIdHex, "hex").buffer,
); );
const issuedAt = Math.floor(Date.now() / 1000);
const expiryTime = Math.floor(Date.now() / 1000) + expMinutes * 60; // some minutes from now
const fullPayload = { const fullPayload = {
...payload, ...payload,
iat: Math.floor(Date.now() / 1000), exp: expiryTime,
iat: issuedAt,
iss: issuerDid, iss: issuerDid,
}; };
this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload))); this.challenge = new Uint8Array(Buffer.from(JSON.stringify(fullPayload)));
@ -159,7 +163,8 @@ export class PeerSetup {
const dataInJwt = { const dataInJwt = {
AuthenticationDataB64URL: authenticatorDataBase64Url, AuthenticationDataB64URL: authenticatorDataBase64Url,
ClientDataJSONB64URL: this.clientDataJsonBase64Url, ClientDataJSONB64URL: this.clientDataJsonBase64Url,
iat: Math.floor(Date.now() / 1000), exp: expiryTime,
iat: issuedAt,
iss: issuerDid, iss: issuerDid,
}; };
const dataInJwtString = JSON.stringify(dataInJwt); const dataInJwtString = JSON.stringify(dataInJwt);
@ -178,10 +183,14 @@ export class PeerSetup {
issuerDid: string, issuerDid: string,
payload: object, payload: object,
credIdHex: string, credIdHex: string,
expMinutes: number = 1,
) { ) {
const issuedAt = Math.floor(Date.now() / 1000);
const expiryTime = Math.floor(Date.now() / 1000) + expMinutes * 60; // some minutes from now
const fullPayload = { const fullPayload = {
...payload, ...payload,
iat: Math.floor(Date.now() / 1000), exp: expiryTime,
iat: issuedAt,
iss: issuerDid, iss: issuerDid,
}; };
const dataToSignString = JSON.stringify(fullPayload); const dataToSignString = JSON.stringify(fullPayload);
@ -227,7 +236,8 @@ export class PeerSetup {
const dataInJwt = { const dataInJwt = {
AuthenticationDataB64URL: authenticatorDataBase64Url, AuthenticationDataB64URL: authenticatorDataBase64Url,
ClientDataJSONB64URL: this.clientDataJsonBase64Url, ClientDataJSONB64URL: this.clientDataJsonBase64Url,
iat: Math.floor(Date.now() / 1000), exp: expiryTime,
iat: issuedAt,
iss: issuerDid, iss: issuerDid,
}; };
const dataInJwtString = JSON.stringify(dataInJwt); const dataInJwtString = JSON.stringify(dataInJwt);
@ -308,6 +318,16 @@ export class PeerSetup {
// } // }
} }
export async function createDidPeerJwt(
did: string,
credIdHex: string,
payload: object,
): Promise<string> {
const peerSetup = new PeerSetup();
const jwt = await peerSetup.createJwtNavigator(did, payload, credIdHex);
return jwt;
}
// I'd love to use this but it doesn't verify. // I'd love to use this but it doesn't verify.
// Requires: // Requires:
// npm install @noble/curves // npm install @noble/curves
@ -380,6 +400,7 @@ export async function verifyJwtSimplewebauthn(
return verification.verified; return verification.verified;
} }
// similar code is in endorser-ch util-crypto.ts verifyPeerSignature
export async function verifyJwtWebCrypto( export async function verifyJwtWebCrypto(
credId: Base64URLString, credId: Base64URLString,
issuerDid: string, issuerDid: string,

74
src/libs/endorserServer.ts

@ -2,7 +2,6 @@ import {
Axios, Axios,
AxiosRequestConfig, AxiosRequestConfig,
AxiosResponse, AxiosResponse,
RawAxiosRequestHeaders,
} from "axios"; } from "axios";
import * as didJwt from "did-jwt"; import * as didJwt from "did-jwt";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
@ -13,7 +12,8 @@ import { DEFAULT_IMAGE_API_SERVER } from "@/constants/app";
import { Contact } from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
import { accessToken, SimpleSigner } from "@/libs/crypto"; import { accessToken, SimpleSigner } from "@/libs/crypto";
import { NonsensitiveDexie } from "@/db/index"; import { NonsensitiveDexie } from "@/db/index";
import { getIdentity } from "@/libs/util"; import { createDidPeerJwt } from "@/libs/didPeer";
import { getAccount, getIdentity } from "@/libs/util";
export const SCHEMA_ORG_CONTEXT = "https://schema.org"; export const SCHEMA_ORG_CONTEXT = "https://schema.org";
// the object in RegisterAction claims // the object in RegisterAction claims
@ -160,7 +160,7 @@ export interface OfferVerifiableCredential {
// Note that previous VCs may have additional fields. // Note that previous VCs may have additional fields.
// https://endorser.ch/doc/html/transactions.html#id7 // https://endorser.ch/doc/html/transactions.html#id7
export interface PlanVerifiableCredential { export interface PlanVerifiableCredential {
"@context": SCHEMA_ORG_CONTEXT; "@context": "https://schema.org";
"@type": "PlanAction"; "@type": "PlanAction";
name: string; name: string;
agent?: { identifier: string }; agent?: { identifier: string };
@ -453,28 +453,30 @@ export function didInfo(
return didInfoForContact(did, activeDid, contact, allMyDids).displayName; return didInfoForContact(did, activeDid, contact, allMyDids).displayName;
} }
async function getHeaders(identity: IIdentifier | null) { export async function getHeaders(did?: string) {
const headers: RawAxiosRequestHeaders = { const headers: { "Content-Type": string; Authorization?: string } = {
"Content-Type": "application/json", "Content-Type": "application/json",
}; };
if (identity) { if (did) {
const token = await accessToken(identity); const token = await accessToken(undefined, did);
headers["Authorization"] = "Bearer " + token; headers["Authorization"] = "Bearer " + token;
} else {
// it's often OK to request without auth; we assume necessary checks are done earlier
} }
return headers; return headers;
} }
/** /**
* @param handleId nullable, in which case "undefined" will be returned * @param handleId nullable, in which case "undefined" will be returned
* @param identity nullable, in which case no private info will be returned * @param requesterDid optional, in which case no private info will be returned
* @param axios * @param axios
* @param apiServer * @param apiServer
*/ */
export async function getPlanFromCache( export async function getPlanFromCache(
handleId: string | null, handleId: string | null,
identity: IIdentifier | null,
axios: Axios, axios: Axios,
apiServer: string, apiServer: string,
requesterDid?: string,
): Promise<PlanSummaryRecord | undefined> { ): Promise<PlanSummaryRecord | undefined> {
if (!handleId) { if (!handleId) {
return undefined; return undefined;
@ -485,7 +487,7 @@ export async function getPlanFromCache(
apiServer + apiServer +
"/api/v2/report/plans?handleId=" + "/api/v2/report/plans?handleId=" +
encodeURIComponent(handleId); encodeURIComponent(handleId);
const headers = await getHeaders(identity); const headers = await getHeaders(requesterDid);
try { try {
const resp = await axios.get(url, { headers }); const resp = await axios.get(url, { headers });
if (resp.status === 200 && resp.data?.data?.length > 0) { if (resp.status === 200 && resp.data?.data?.length > 0) {
@ -944,18 +946,34 @@ export const bvcMeetingJoinClaim = (did: string, startTime: string) => {
}; };
}; };
export async function createEndorserJwt(did: string, payload: object) {
const account = await getAccount(did);
if (account.identity) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const identity = JSON.parse(account.identity!);
const privateKeyHex = identity.keys[0].privateKeyHex;
const signer = await SimpleSigner(privateKeyHex);
return didJwt.createJWT(payload, {
issuer: did,
signer: signer,
});
} else if (account.passkeyCredIdHex) {
return createDidPeerJwt(did, account.passkeyCredIdHex, payload);
} else {
throw new Error("No identity data found to sign for DID " + did);
}
}
export async function register( export async function register(
activeDid: string, activeDid: string,
apiServer: string, apiServer: string,
axios: Axios, axios: Axios,
contact: Contact, contact: Contact,
) { ) {
const identity = await getIdentity(activeDid);
const vcClaim: RegisterVerifiableCredential = { const vcClaim: RegisterVerifiableCredential = {
"@context": SCHEMA_ORG_CONTEXT, "@context": SCHEMA_ORG_CONTEXT,
"@type": "RegisterAction", "@type": "RegisterAction",
agent: { identifier: identity.did }, agent: { identifier: activeDid },
object: SERVICE_ID, object: SERVICE_ID,
participant: { identifier: contact.did }, participant: { identifier: contact.did },
}; };
@ -968,26 +986,10 @@ export async function register(
}, },
}; };
// Create a signature using private key of identity // Create a signature using private key of identity
if (identity.keys[0].privateKeyHex == null) { const vcJwt = await createEndorserJwt(activeDid, vcPayload);
return { error: "Private key not found." };
}
// 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
const payload = JSON.stringify({ jwtEncoded: vcJwt });
const url = apiServer + "/api/v2/claim"; const url = apiServer + "/api/v2/claim";
const headers = await getHeaders(identity); const resp = await axios.post(url, { jwtEncoded: vcJwt });
const resp = await axios.post(url, payload, { headers });
if (resp.data?.success?.handleId) { if (resp.data?.success?.handleId) {
return { success: true }; return { success: true };
} else if (resp.data?.success?.embeddedRecordError) { } else if (resp.data?.success?.embeddedRecordError) {
@ -1017,7 +1019,7 @@ export async function setVisibilityUtil(
const url = const url =
apiServer + "/api/report/" + (visibility ? "canSeeMe" : "cannotSeeMe"); apiServer + "/api/report/" + (visibility ? "canSeeMe" : "cannotSeeMe");
const identity = await getIdentity(activeDid); const identity = await getIdentity(activeDid);
const headers = await getHeaders(identity); const headers = await getHeaders(identity.did);
const payload = JSON.stringify({ did: contact.did }); const payload = JSON.stringify({ did: contact.did });
try { try {
@ -1052,10 +1054,10 @@ export async function setVisibilityUtil(
export async function fetchEndorserRateLimits( export async function fetchEndorserRateLimits(
apiServer: string, apiServer: string,
axios: Axios, axios: Axios,
identity: IIdentifier, did: string,
) { ) {
const url = `${apiServer}/api/report/rateLimits`; const url = `${apiServer}/api/report/rateLimits`;
const headers = await getHeaders(identity); const headers = await getHeaders(did);
return await axios.get(url, { headers } as AxiosRequestConfig); return await axios.get(url, { headers } as AxiosRequestConfig);
} }
@ -1070,9 +1072,9 @@ export async function fetchEndorserRateLimits(
export async function fetchImageRateLimits( export async function fetchImageRateLimits(
apiServer: string, apiServer: string,
axios: Axios, axios: Axios,
identity: IIdentifier, did: string,
) { ) {
const url = DEFAULT_IMAGE_API_SERVER + "/image-limits"; const url = DEFAULT_IMAGE_API_SERVER + "/image-limits";
const headers = await getHeaders(identity); const headers = await getHeaders(did);
return await axios.get(url, { headers } as AxiosRequestConfig); return await axios.get(url, { headers } as AxiosRequestConfig);
} }

7
src/libs/util.ts

@ -196,12 +196,17 @@ export function findAllVisibleToDids(
* *
**/ **/
export const getIdentity = async (activeDid: string): Promise<IIdentifier> => { export const getAccount = async (activeDid: string): Promise<Account> => {
await accountsDB.open(); await accountsDB.open();
const account = (await accountsDB.accounts const account = (await accountsDB.accounts
.where("did") .where("did")
.equals(activeDid) .equals(activeDid)
.first()) as Account; .first()) as Account;
return account;
};
export const getIdentity = async (activeDid: string): Promise<IIdentifier> => {
const account = await getAccount(activeDid);
const identity = JSON.parse(account?.identity || "null"); const identity = JSON.parse(account?.identity || "null");
if (!identity) { if (!identity) {

39
src/views/AccountViewView.vue

@ -807,32 +807,6 @@ export default class AccountViewView extends Vue {
} }
} }
/**
* Asynchronously retrieves headers for HTTP requests.
*
* @param {IIdentifier} identity - The identity object for which to generate the headers.
* @returns {Promise<Record<string,string>>} A Promise that resolves to an object containing the headers.
*
* @throws Will throw an error if unable to generate an access token.
*/
public async getHeaders(
identity: IIdentifier,
): Promise<Record<string, string>> {
try {
const token = await accessToken(identity);
const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
};
return headers;
} catch (error) {
console.error("Failed to get headers:", error);
return Promise.reject(error);
}
}
// 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();
@ -884,7 +858,7 @@ export default class AccountViewView extends Vue {
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(identity); this.checkLimitsFor(this.activeDid);
} else { } else {
// Handle the case where any of these are null or undefined // Handle the case where any of these are null or undefined
} }
@ -1238,9 +1212,8 @@ export default class AccountViewView extends Vue {
} }
async checkLimits() { async checkLimits() {
const identity = await this.getIdentity(this.activeDid); if (this.activeDid) {
if (identity) { this.checkLimitsFor(this.activeDid);
this.checkLimitsFor(identity);
} else { } else {
this.limitsMessage = this.limitsMessage =
"You have no identifier, or your data has been corrupted."; "You have no identifier, or your data has been corrupted.";
@ -1252,7 +1225,7 @@ export default class AccountViewView extends Vue {
* *
* Updates component state variables `limits`, `limitsMessage`, and `loadingLimits`. * Updates component state variables `limits`, `limitsMessage`, and `loadingLimits`.
*/ */
public async checkLimitsFor(identity: IIdentifier) { public async checkLimitsFor(did: string) {
this.loadingLimits = true; this.loadingLimits = true;
this.limitsMessage = ""; this.limitsMessage = "";
@ -1260,7 +1233,7 @@ export default class AccountViewView extends Vue {
const resp = await fetchEndorserRateLimits( const resp = await fetchEndorserRateLimits(
this.apiServer, this.apiServer,
this.axios, this.axios,
identity, did,
); );
if (resp.status === 200) { if (resp.status === 200) {
this.endorserLimits = resp.data; this.endorserLimits = resp.data;
@ -1288,7 +1261,7 @@ export default class AccountViewView extends Vue {
const imageResp = await fetchImageRateLimits( const imageResp = await fetchImageRateLimits(
this.apiServer, this.apiServer,
this.axios, this.axios,
identity, did,
); );
if (imageResp.status === 200) { if (imageResp.status === 200) {
this.imageLimits = imageResp.data; this.imageLimits = imageResp.data;

14
src/views/ClaimAddRawView.vue

@ -29,7 +29,6 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { RawAxiosRequestHeaders } from "axios";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
@ -37,7 +36,6 @@ import GiftedDialog from "@/components/GiftedDialog.vue";
import { NotificationIface } from "@/constants/app"; import { NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index"; import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";
import * as serverUtil from "@/libs/endorserServer"; import * as serverUtil from "@/libs/endorserServer";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import { Account } from "@/db/tables/accounts"; import { Account } from "@/db/tables/accounts";
@ -82,18 +80,6 @@ export default class ClaimAddRawView extends Vue {
return identity; return identity;
} }
public async getHeaders(identity: IIdentifier) {
const headers: RawAxiosRequestHeaders = {
"Content-Type": "application/json",
};
if (identity) {
const token = await accessToken(identity);
headers["Authorization"] = "Bearer " + token;
}
return headers;
}
// similar code is found in ProjectViewView
async submitClaim() { async submitClaim() {
const fullClaim = JSON.parse(this.claimStr); const fullClaim = JSON.parse(this.claimStr);
const result = await serverUtil.createAndSubmitClaim( const result = await serverUtil.createAndSubmitClaim(

40
src/views/ClaimView.vue

@ -407,7 +407,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { AxiosError, RawAxiosRequestHeaders } from "axios"; import { AxiosError } from "axios";
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import * as R from "ramda"; import * as R from "ramda";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";
@ -419,7 +419,6 @@ 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, Settings } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";
import * as serverUtil from "@/libs/endorserServer"; import * as serverUtil from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util"; import * as libsUtil from "@/libs/util";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
@ -432,7 +431,6 @@ import { GiverReceiverInputInfo } from "@/libs/endorserServer";
export default class ClaimView extends Vue { export default class ClaimView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
accountIdentityStr: string = "null";
activeDid = ""; activeDid = "";
allMyDids: Array<string> = []; allMyDids: Array<string> = [];
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
@ -485,15 +483,12 @@ export default class ClaimView extends Vue {
const accounts = accountsDB.accounts; const accounts = accountsDB.accounts;
const accountsArr: Array<Account> = await accounts?.toArray(); const accountsArr: Array<Account> = await accounts?.toArray();
this.allMyDids = accountsArr.map((acc) => acc.did); this.allMyDids = accountsArr.map((acc) => acc.did);
const account = accountsArr.find((acc) => acc.did === this.activeDid);
this.accountIdentityStr = (account?.identity as string) || "null";
const identity = JSON.parse(this.accountIdentityStr);
const pathParam = window.location.pathname.substring("/claim/".length); const pathParam = window.location.pathname.substring("/claim/".length);
let claimId; let claimId;
if (pathParam) { if (pathParam) {
claimId = decodeURIComponent(pathParam); claimId = decodeURIComponent(pathParam);
await this.loadClaim(claimId, identity); await this.loadClaim(claimId, this.activeDid);
} else { } else {
this.$notify( this.$notify(
{ {
@ -543,17 +538,6 @@ export default class ClaimView extends Vue {
return identity; return identity;
} }
public async getHeaders(identity: IIdentifier) {
const headers: RawAxiosRequestHeaders = {
"Content-Type": "application/json",
};
if (identity) {
const token = await accessToken(identity);
headers["Authorization"] = "Bearer " + token;
}
return headers;
}
// Isn't there a better way to make this available to the template? // Isn't there a better way to make this available to the template?
didInfo(did: string) { didInfo(did: string) {
return serverUtil.didInfo( return serverUtil.didInfo(
@ -564,12 +548,12 @@ export default class ClaimView extends Vue {
); );
} }
async loadClaim(claimId: string, identity: IIdentifier) { async loadClaim(claimId: string, userDid: string) {
const urlPath = libsUtil.isGlobalUri(claimId) const urlPath = libsUtil.isGlobalUri(claimId)
? "/api/claim/byHandle/" ? "/api/claim/byHandle/"
: "/api/claim/"; : "/api/claim/";
const url = this.apiServer + urlPath + encodeURIComponent(claimId); const url = this.apiServer + urlPath + encodeURIComponent(claimId);
const headers = await this.getHeaders(identity); const headers = await serverUtil.getHeaders(userDid);
try { try {
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
@ -601,7 +585,7 @@ export default class ClaimView extends Vue {
this.apiServer + this.apiServer +
"/api/v2/report/gives?handleId=" + "/api/v2/report/gives?handleId=" +
encodeURIComponent(this.veriClaim.handleId as string); encodeURIComponent(this.veriClaim.handleId as string);
const giveHeaders = await this.getHeaders(identity); const giveHeaders = await serverUtil.getHeaders(userDid);
const giveResp = await this.axios.get(giveUrl, { const giveResp = await this.axios.get(giveUrl, {
headers: giveHeaders, headers: giveHeaders,
}); });
@ -615,7 +599,7 @@ export default class ClaimView extends Vue {
this.apiServer + this.apiServer +
"/api/v2/report/offers?handleId=" + "/api/v2/report/offers?handleId=" +
encodeURIComponent(this.veriClaim.handleId as string); encodeURIComponent(this.veriClaim.handleId as string);
const offerHeaders = await this.getHeaders(identity); const offerHeaders = await serverUtil.getHeaders(userDid);
const offerResp = await this.axios.get(offerUrl, { const offerResp = await this.axios.get(offerUrl, {
headers: offerHeaders, headers: offerHeaders,
}); });
@ -631,7 +615,7 @@ export default class ClaimView extends Vue {
this.apiServer + this.apiServer +
"/api/report/issuersWhoClaimedOrConfirmed?claimId=" + "/api/report/issuersWhoClaimedOrConfirmed?claimId=" +
encodeURIComponent(serverUtil.stripEndorserPrefix(claimId)); encodeURIComponent(serverUtil.stripEndorserPrefix(claimId));
const confirmHeaders = await this.getHeaders(identity); const confirmHeaders = await serverUtil.getHeaders(userDid);
const response = await this.axios.get(confirmUrl, { const response = await this.axios.get(confirmUrl, {
headers: confirmHeaders, headers: confirmHeaders,
}); });
@ -673,15 +657,9 @@ export default class ClaimView extends Vue {
} }
async showFullClaim(claimId: string) { async showFullClaim(claimId: string) {
await accountsDB.open();
const accounts = accountsDB.accounts;
const accountsArr: Account[] = await accounts?.toArray();
const account = accountsArr.find((acc) => acc.did === this.activeDid);
const identity = JSON.parse((account?.identity as string) || "null");
const url = const url =
this.apiServer + "/api/claim/full/" + encodeURIComponent(claimId); this.apiServer + "/api/claim/full/" + encodeURIComponent(claimId);
const headers = await this.getHeaders(identity); const headers = await serverUtil.getHeaders(this.activeDid);
try { try {
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
@ -794,7 +772,7 @@ export default class ClaimView extends Vue {
}; };
this.$router.push(route).then(async () => { this.$router.push(route).then(async () => {
this.resetThisValues(); this.resetThisValues();
await this.loadClaim(claimId, JSON.parse(this.accountIdentityStr)); await this.loadClaim(claimId, this.activeDid);
}); });
} }

27
src/views/ConfirmGiftView.vue

@ -420,7 +420,6 @@ import { isGiveAction } from "@/libs/util";
export default class ClaimView extends Vue { export default class ClaimView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
accountIdentityStr: string = "null";
activeDid = ""; activeDid = "";
allMyDids: Array<string> = []; allMyDids: Array<string> = [];
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
@ -471,9 +470,6 @@ export default class ClaimView extends Vue {
const accounts = accountsDB.accounts; const accounts = accountsDB.accounts;
const accountsArr: Array<Account> = await accounts?.toArray(); const accountsArr: Array<Account> = await accounts?.toArray();
this.allMyDids = accountsArr.map((acc) => acc.did); this.allMyDids = accountsArr.map((acc) => acc.did);
const account = accountsArr.find((acc) => acc.did === this.activeDid);
this.accountIdentityStr = (account?.identity as string) || "null";
const identity = JSON.parse(this.accountIdentityStr);
const pathParam = window.location.pathname.substring( const pathParam = window.location.pathname.substring(
"/confirm-gift/".length, "/confirm-gift/".length,
@ -481,7 +477,7 @@ export default class ClaimView extends Vue {
let claimId; let claimId;
if (pathParam) { if (pathParam) {
claimId = decodeURIComponent(pathParam); claimId = decodeURIComponent(pathParam);
await this.loadClaim(claimId, identity); await this.loadClaim(claimId, this.activeDid);
} else { } else {
this.$notify( this.$notify(
{ {
@ -546,17 +542,6 @@ export default class ClaimView extends Vue {
return identity; return identity;
} }
public async getHeaders(identity: IIdentifier) {
const headers: RawAxiosRequestHeaders = {
"Content-Type": "application/json",
};
if (identity) {
const token = await accessToken(identity);
headers["Authorization"] = "Bearer " + token;
}
return headers;
}
// Isn't there a better way to make this available to the template? // Isn't there a better way to make this available to the template?
didInfo(did: string | undefined) { didInfo(did: string | undefined) {
return serverUtil.didInfo( return serverUtil.didInfo(
@ -567,14 +552,14 @@ export default class ClaimView extends Vue {
); );
} }
async loadClaim(claimId: string, identity: IIdentifier) { async loadClaim(claimId: string, userDid: string) {
const urlPath = libsUtil.isGlobalUri(claimId) const urlPath = libsUtil.isGlobalUri(claimId)
? "/api/claim/byHandle/" ? "/api/claim/byHandle/"
: "/api/claim/"; : "/api/claim/";
const url = this.apiServer + urlPath + encodeURIComponent(claimId); const url = this.apiServer + urlPath + encodeURIComponent(claimId);
try { try {
const headers = await this.getHeaders(identity); const headers = await serverUtil.getHeaders(userDid);
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
// resp.data is: // resp.data is:
// - a Jwt from https://api.endorser.ch/api-docs/ // - a Jwt from https://api.endorser.ch/api-docs/
@ -614,7 +599,7 @@ export default class ClaimView extends Vue {
this.apiServer + this.apiServer +
"/api/v2/report/gives?handleId=" + "/api/v2/report/gives?handleId=" +
encodeURIComponent(this.veriClaim.handleId as string); encodeURIComponent(this.veriClaim.handleId as string);
const giveHeaders = await this.getHeaders(identity); const giveHeaders = await serverUtil.getHeaders(userDid);
const giveResp = await this.axios.get(giveUrl, { const giveResp = await this.axios.get(giveUrl, {
headers: giveHeaders, headers: giveHeaders,
}); });
@ -685,7 +670,7 @@ export default class ClaimView extends Vue {
this.apiServer + this.apiServer +
"/api/report/issuersWhoClaimedOrConfirmed?claimId=" + "/api/report/issuersWhoClaimedOrConfirmed?claimId=" +
encodeURIComponent(serverUtil.stripEndorserPrefix(claimId)); encodeURIComponent(serverUtil.stripEndorserPrefix(claimId));
const confirmHeaders = await this.getHeaders(identity); const confirmHeaders = await serverUtil.getHeaders(userDid);
const response = await this.axios.get(confirmUrl, { const response = await this.axios.get(confirmUrl, {
headers: confirmHeaders, headers: confirmHeaders,
}); });
@ -794,7 +779,7 @@ export default class ClaimView extends Vue {
}; };
this.$router.push(route).then(async () => { this.$router.push(route).then(async () => {
this.resetThisValues(); this.resetThisValues();
await this.loadClaim(claimId, JSON.parse(this.accountIdentityStr)); await this.loadClaim(claimId, this.activeDid);
}); });
} }

20
src/views/ContactAmountsView.vue

@ -120,6 +120,7 @@ import { accessToken, SimpleSigner } from "@/libs/crypto";
import { import {
AgreeVerifiableCredential, AgreeVerifiableCredential,
displayAmount, displayAmount,
getHeaders,
GiveSummaryRecord, GiveSummaryRecord,
GiveVerifiableCredential, GiveVerifiableCredential,
SCHEMA_ORG_CONTEXT, SCHEMA_ORG_CONTEXT,
@ -148,7 +149,7 @@ export default class ContactAmountssView extends Vue {
.where("did") .where("did")
.equals(activeDid) .equals(activeDid)
.first(); .first();
const identity = JSON.parse(account?.identity || "null"); const identity = JSON.parse((account?.identity as string) || "null");
if (!identity) { if (!identity) {
throw new Error( throw new Error(
@ -158,15 +159,6 @@ export default class ContactAmountssView extends Vue {
return identity; return identity;
} }
public async getHeaders(identity: IIdentifier) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
async created() { async created() {
try { try {
await db.open(); await db.open();
@ -174,8 +166,8 @@ export default class ContactAmountssView extends Vue {
this.contact = (await db.contacts.get(contactDid)) || null; this.contact = (await db.contacts.get(contactDid)) || null;
const settings = await db.settings.get(MASTER_SETTINGS_KEY); const settings = await db.settings.get(MASTER_SETTINGS_KEY);
this.activeDid = settings?.activeDid || ""; this.activeDid = (settings?.activeDid as string) || "";
this.apiServer = settings?.apiServer || ""; this.apiServer = (settings?.apiServer as string) || "";
if (this.activeDid && this.contact) { if (this.activeDid && this.contact) {
this.loadGives(this.activeDid, this.contact); this.loadGives(this.activeDid, this.contact);
@ -207,7 +199,7 @@ export default class ContactAmountssView extends Vue {
encodeURIComponent(identity.did) + encodeURIComponent(identity.did) +
"&recipientDid=" + "&recipientDid=" +
encodeURIComponent(contact.did); encodeURIComponent(contact.did);
const headers = await this.getHeaders(identity); const headers = await getHeaders(activeDid);
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });
if (resp.status === 200) { if (resp.status === 200) {
result = resp.data.data; result = resp.data.data;
@ -234,7 +226,7 @@ export default class ContactAmountssView extends Vue {
encodeURIComponent(contact.did) + encodeURIComponent(contact.did) +
"&recipientDid=" + "&recipientDid=" +
encodeURIComponent(identity.did); encodeURIComponent(identity.did);
const headers2 = await this.getHeaders(identity); const headers2 = await getHeaders(activeDid);
const resp2 = await this.axios.get(url2, { headers: headers2 }); const resp2 = await this.axios.get(url2, { headers: headers2 });
if (resp2.status === 200) { if (resp2.status === 200) {
result = R.concat(result, resp2.data.data); result = R.concat(result, resp2.data.data);

31
src/views/ContactGiftingView.vue

@ -72,17 +72,15 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { IIdentifier } from "@veramo/core";
import GiftedDialog from "@/components/GiftedDialog.vue"; import GiftedDialog from "@/components/GiftedDialog.vue";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import EntityIcon from "@/components/EntityIcon.vue"; import EntityIcon from "@/components/EntityIcon.vue";
import { NotificationIface } from "@/constants/app"; import { NotificationIface } from "@/constants/app";
import { db, accountsDB } from "@/db/index"; import { db, accountsDB } from "@/db/index";
import { Account, AccountsSchema } from "@/db/tables/accounts"; import { AccountsSchema } from "@/db/tables/accounts";
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 { accessToken } from "@/libs/crypto";
import { GiverReceiverInputInfo } from "@/libs/endorserServer"; import { GiverReceiverInputInfo } from "@/libs/endorserServer";
@Component({ @Component({
@ -134,32 +132,7 @@ export default class ContactGiftingView extends Vue {
} }
} }
public async getIdentity(activeDid: string) { openDialog(giver?: GiverReceiverInputInfo) {
await accountsDB.open();
const account = (await accountsDB.accounts
.where("did")
.equals(activeDid)
.first()) 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;
}
public async getHeaders(identity: IIdentifier) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
openDialog(giver: GiverReceiverInputInfo) {
const recipient = this.projectId const recipient = this.projectId
? undefined ? undefined
: { did: this.activeDid, name: "you" }; : { did: this.activeDid, name: "you" };

7
src/views/ContactQRScanShowView.vue

@ -137,8 +137,11 @@ export default class ContactQRScanShow extends Vue {
const publicKeyHex = identity.keys[0].publicKeyHex; const publicKeyHex = identity.keys[0].publicKeyHex;
const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64"); const publicEncKey = Buffer.from(publicKeyHex, "hex").toString("base64");
const newDerivPath = nextDerivationPath(account.derivationPath); const newDerivPath = nextDerivationPath(account.derivationPath as string);
const nextPublicHex = deriveAddress(account.mnemonic, newDerivPath)[2]; const nextPublicHex = deriveAddress(
account.mnemonic as string,
newDerivPath,
)[2];
const nextPublicEncKey = Buffer.from(nextPublicHex, "hex"); const nextPublicEncKey = Buffer.from(nextPublicHex, "hex");
const nextPublicEncKeyHash = sha256(nextPublicEncKey); const nextPublicEncKeyHash = sha256(nextPublicEncKey);
const nextPublicEncKeyHashBase64 = const nextPublicEncKeyHashBase64 =

36
src/views/ContactsView.vue

@ -311,12 +311,13 @@ import { AppString, 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, Settings } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { accessToken, getContactPayloadFromJwtUrl } from "@/libs/crypto"; import { getContactPayloadFromJwtUrl } from "@/libs/crypto";
import { import {
CONTACT_CSV_HEADER, CONTACT_CSV_HEADER,
CONTACT_URL_PREFIX, CONTACT_URL_PREFIX,
GiverReceiverInputInfo, GiverReceiverInputInfo,
GiveSummaryRecord, GiveSummaryRecord,
getHeaders,
isDid, isDid,
register, register,
setVisibilityUtil, setVisibilityUtil,
@ -414,22 +415,6 @@ export default class ContactsView extends Vue {
return identity; return identity;
} }
public async getHeaders(identity: IIdentifier) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
public async getHeadersAndIdentity(activeDid: string) {
const identity = await this.getIdentity(activeDid);
const headers = await this.getHeaders(identity);
return { headers, identity };
}
async loadGives() { async loadGives() {
if (!this.activeDid) { if (!this.activeDid) {
return; return;
@ -481,7 +466,7 @@ export default class ContactsView extends Vue {
}; };
try { try {
const { headers } = await this.getHeadersAndIdentity(this.activeDid); const headers = await getHeaders(this.activeDid);
const givenByUrl = const givenByUrl =
this.apiServer + this.apiServer +
"/api/v2/report/gives?agentDid=" + "/api/v2/report/gives?agentDid=" +
@ -954,8 +939,19 @@ export default class ContactsView extends Vue {
this.apiServer + this.apiServer +
"/api/report/canDidExplicitlySeeMe?did=" + "/api/report/canDidExplicitlySeeMe?did=" +
encodeURIComponent(contact.did); encodeURIComponent(contact.did);
const identity = await this.getIdentity(this.activeDid); const headers = await getHeaders(this.activeDid);
const headers = await this.getHeaders(identity); if (!headers["Authorization"]) {
this.$notify(
{
group: "alert",
type: "danger",
title: "No Identity",
text: "There is no identity to use to check visibility.",
},
3000,
);
return;
}
try { try {
const resp = await this.axios.get(url, { headers }); const resp = await this.axios.get(url, { headers });

2
src/views/GiftedDetails.vue

@ -316,9 +316,9 @@ export default class GiftedDetails extends Vue {
const identity = await libsUtil.getIdentity(this.activeDid); const identity = await libsUtil.getIdentity(this.activeDid);
const project = await getPlanFromCache( const project = await getPlanFromCache(
this.projectId, this.projectId,
identity,
this.axios, this.axios,
this.apiServer, this.apiServer,
identity.did,
); );
this.projectName = project?.name this.projectName = project?.name
? "the project: " + project.name ? "the project: " + project.name

40
src/views/HomeView.vue

@ -338,12 +338,12 @@ import {
MASTER_SETTINGS_KEY, MASTER_SETTINGS_KEY,
Settings, Settings,
} from "@/db/tables/settings"; } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto";
import { import {
contactForDid, contactForDid,
containsNonHiddenDid, containsNonHiddenDid,
didInfoForContact, didInfoForContact,
fetchEndorserRateLimits, fetchEndorserRateLimits,
getHeaders,
getPlanFromCache, getPlanFromCache,
GiverReceiverInputInfo, GiverReceiverInputInfo,
GiveSummaryRecord, GiveSummaryRecord,
@ -407,16 +407,6 @@ export default class HomeView extends Vue {
showShortcutBvc = false; showShortcutBvc = false;
userAgentInfo = new UAParser(); // see https://docs.uaparser.js.org/v2/api/ua-parser-js/get-os.html userAgentInfo = new UAParser(); // see https://docs.uaparser.js.org/v2/api/ua-parser-js/get-os.html
public async getIdentity(activeDid: string): Promise<IIdentifier | null> {
await accountsDB.open();
const account = (await accountsDB.accounts
.where("did")
.equals(activeDid)
.first()) as Account;
const identity = JSON.parse(account?.identity || "null");
return identity; // may be null
}
async mounted() { async mounted() {
try { try {
await accountsDB.open(); await accountsDB.open();
@ -440,12 +430,11 @@ export default class HomeView extends Vue {
// someone may have have registered after sharing contact info, so recheck // someone may have have registered after sharing contact info, so recheck
if (!this.isRegistered && this.activeDid) { if (!this.isRegistered && this.activeDid) {
const identity = await this.getIdentity(this.activeDid);
try { try {
const resp = await fetchEndorserRateLimits( const resp = await fetchEndorserRateLimits(
this.apiServer, this.apiServer,
this.axios, this.axios,
identity as IIdentifier, this.activeDid,
); );
if (resp.status === 200) { if (resp.status === 200) {
// we just needed to know that they're registered // we just needed to know that they're registered
@ -497,26 +486,6 @@ export default class HomeView extends Vue {
return "Notification" in window; return "Notification" in window;
} }
async buildHeaders() {
const headers: HeadersInit = {
"Content-Type": "application/json",
};
const identity = await this.getIdentity(this.activeDid);
if (this.activeDid) {
if (identity) {
headers["Authorization"] = "Bearer " + (await accessToken(identity));
} else {
throw new Error(
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service. Switch your ID.",
);
}
} else {
// it's OK without auth... we just won't get any identifiers
}
return headers;
}
// only called when a setting was changed // only called when a setting was changed
async reloadFeedOnChange() { async reloadFeedOnChange() {
await db.open(); await db.open();
@ -564,7 +533,6 @@ export default class HomeView extends Vue {
if (results.data.length > 0) { if (results.data.length > 0) {
endOfResults = false; endOfResults = false;
// include the descriptions of the giver and receiver // include the descriptions of the giver and receiver
const identity = await this.getIdentity(this.activeDid);
for (const record: GiveSummaryRecord of results.data) { for (const record: GiveSummaryRecord of results.data) {
// similar code is in endorser-mobile utility.ts // similar code is in endorser-mobile utility.ts
// claim.claim happen for some claims wrapped in a Verifiable Credential // claim.claim happen for some claims wrapped in a Verifiable Credential
@ -581,9 +549,9 @@ export default class HomeView extends Vue {
// We should display it immediately and then get the plan later. // We should display it immediately and then get the plan later.
const plan = await getPlanFromCache( const plan = await getPlanFromCache(
record.fulfillsPlanHandleId, record.fulfillsPlanHandleId,
identity,
this.axios, this.axios,
this.apiServer, this.apiServer,
this.activeDid,
); );
// check if the record should be filtered out // check if the record should be filtered out
@ -672,7 +640,7 @@ export default class HomeView extends Vue {
beforeQuery, beforeQuery,
{ {
method: "GET", method: "GET",
headers: await this.buildHeaders(), headers: await getHeaders(this.activeDid),
}, },
); );

16
src/views/NewEditProjectView.vue

@ -245,15 +245,6 @@ export default class NewEditProjectView extends Vue {
return identity; return identity;
} }
public async getHeaders(identity: IIdentifier) {
const token = await accessToken(identity);
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
};
return headers;
}
async mounted() { async mounted() {
await accountsDB.open(); await accountsDB.open();
this.numAccounts = await accountsDB.accounts.count(); this.numAccounts = await accountsDB.accounts.count();
@ -460,11 +451,14 @@ export default class NewEditProjectView extends Vue {
const signer = await SimpleSigner(privateKeyHex); const signer = await SimpleSigner(privateKeyHex);
const alg = undefined; const alg = undefined;
// create a JWT for the request // create a JWT for the request
const vcJwt: string = await didJwt.createJWT(vcPayload, { const vcJwt: string = await didJwt.createJWT(
vcPayload as Partial<didJwt.JWTPayload>,
{
alg: alg, alg: alg,
issuer: identity.did, issuer: identity.did,
signer: signer, signer: signer,
}); },
);
// Make the xhr request payload // Make the xhr request payload

2
src/views/TestView.vue

@ -247,9 +247,7 @@ import QuickNav from "@/components/QuickNav.vue";
import { AppString, NotificationIface } from "@/constants/app"; import { AppString, NotificationIface } from "@/constants/app";
import { accountsDB, db } from "@/db/index"; import { accountsDB, db } from "@/db/index";
import { import {
createPeerDid,
PeerSetup, PeerSetup,
registerCredential,
verifyJwtP256, verifyJwtP256,
verifyJwtSimplewebauthn, verifyJwtSimplewebauthn,
verifyJwtWebCrypto, verifyJwtWebCrypto,

Loading…
Cancel
Save