move low-level DID-related create & decode into separate folder
This commit is contained in:
@@ -47,7 +47,7 @@ npm run lint
|
|||||||
```
|
```
|
||||||
# (Let's replace this with a .env.development or .env.staging file.)
|
# (Let's replace this with a .env.development or .env.staging file.)
|
||||||
# The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.
|
# The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.
|
||||||
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HNTZYJJXTGT0EZS3VEJGX7AK VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app npm run build
|
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HNTZYJJXTGT0EZS3VEJGX7AK VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app PASSKEYS_ENABLED=yep npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
* Production
|
* Production
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ export const DEFAULT_PUSH_SERVER =
|
|||||||
|
|
||||||
export const IMAGE_TYPE_PROFILE = "profile";
|
export const IMAGE_TYPE_PROFILE = "profile";
|
||||||
|
|
||||||
|
export const PASSKEYS_ENABLED =
|
||||||
|
!!import.meta.env.VITE_PASSKEYS_ENABLED || false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The possible values for "group" and "type" are in App.vue.
|
* The possible values for "group" and "type" are in App.vue.
|
||||||
* From the notiwind package
|
* From the notiwind package
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import { getRandomBytesSync } from "ethereum-cryptography/random";
|
|||||||
import { entropyToMnemonic } from "ethereum-cryptography/bip39";
|
import { entropyToMnemonic } from "ethereum-cryptography/bip39";
|
||||||
import { wordlist } from "ethereum-cryptography/bip39/wordlists/english";
|
import { wordlist } from "ethereum-cryptography/bip39/wordlists/english";
|
||||||
import { HDNode } from "@ethersproject/hdnode";
|
import { HDNode } from "@ethersproject/hdnode";
|
||||||
import * as didJwt from "did-jwt";
|
|
||||||
import * as u8a from "uint8arrays";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createEndorserJwt,
|
createEndorserJwtForDid,
|
||||||
ENDORSER_JWT_URL_LOCATION,
|
ENDORSER_JWT_URL_LOCATION,
|
||||||
} from "@/libs/endorserServer";
|
} from "@/libs/endorserServer";
|
||||||
import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup";
|
import { DEFAULT_DID_PROVIDER_NAME } from "../veramo/setup";
|
||||||
|
import { decodeEndorserJwt } from "@/libs/crypto/vc";
|
||||||
|
|
||||||
export const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'";
|
export const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'";
|
||||||
|
|
||||||
@@ -95,58 +94,12 @@ export const accessToken = async (did?: string) => {
|
|||||||
const nowEpoch = Math.floor(Date.now() / 1000);
|
const nowEpoch = Math.floor(Date.now() / 1000);
|
||||||
const endEpoch = nowEpoch + 60; // add one minute
|
const endEpoch = nowEpoch + 60; // add one minute
|
||||||
const tokenPayload = { exp: endEpoch, iat: nowEpoch, iss: did };
|
const tokenPayload = { exp: endEpoch, iat: nowEpoch, iss: did };
|
||||||
return createEndorserJwt(did, tokenPayload);
|
return createEndorserJwtForDid(did, tokenPayload);
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Copied out of did-jwt since it's deprecated in that library.
|
|
||||||
*
|
|
||||||
* The SimpleSigner returns a configured function for signing data.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const signer = SimpleSigner(import.meta.env.PRIVATE_KEY)
|
|
||||||
* signer(data, (err, signature) => {
|
|
||||||
* ...
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* @param {String} hexPrivateKey a hex encoded private key
|
|
||||||
* @return {Function} a configured signer function
|
|
||||||
*/
|
|
||||||
export function SimpleSigner(hexPrivateKey: string): didJwt.Signer {
|
|
||||||
const signer = didJwt.ES256KSigner(didJwt.hexToBytes(hexPrivateKey), true);
|
|
||||||
return async (data) => {
|
|
||||||
const signature = (await signer(data)) as string;
|
|
||||||
return fromJose(signature);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// from did-jwt/util; see SimpleSigner above
|
|
||||||
export function fromJose(signature: string): {
|
|
||||||
r: string;
|
|
||||||
s: string;
|
|
||||||
recoveryParam?: number;
|
|
||||||
} {
|
|
||||||
const signatureBytes: Uint8Array = didJwt.base64ToBytes(signature);
|
|
||||||
if (signatureBytes.length < 64 || signatureBytes.length > 65) {
|
|
||||||
throw new TypeError(
|
|
||||||
`Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const r = bytesToHex(signatureBytes.slice(0, 32));
|
|
||||||
const s = bytesToHex(signatureBytes.slice(32, 64));
|
|
||||||
const recoveryParam =
|
|
||||||
signatureBytes.length === 65 ? signatureBytes[64] : undefined;
|
|
||||||
return { r, s, recoveryParam };
|
|
||||||
}
|
|
||||||
|
|
||||||
// from did-jwt/util; see SimpleSigner above
|
|
||||||
export function bytesToHex(b: Uint8Array): string {
|
|
||||||
return u8a.toString(b, "base16");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@return results of uportJwtPayload:
|
@return results of uportJwtPayload:
|
||||||
{ iat: number, iss: string (DID), own: { name, publicEncKey (base64-encoded key) } }
|
{ iat: number, iss: string (DID), own: { name, publicEncKey (base64-encoded key) } }
|
||||||
@@ -163,7 +116,7 @@ export const getContactPayloadFromJwtUrl = (jwtUrlText: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JWT format: { header, payload, signature, data }
|
// JWT format: { header, payload, signature, data }
|
||||||
const jwt = didJwt.decodeJWT(jwtText);
|
const jwt = decodeEndorserJwt(jwtText);
|
||||||
|
|
||||||
return jwt.payload;
|
return jwt.payload;
|
||||||
};
|
};
|
||||||
|
|||||||
106
src/libs/crypto/vc/index.ts
Normal file
106
src/libs/crypto/vc/index.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* Verifiable Credential & DID functions, specifically for EndorserSearch.org tools
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as didJwt from "did-jwt";
|
||||||
|
import { IIdentifier } from "@veramo/core";
|
||||||
|
import * as u8a from "uint8arrays";
|
||||||
|
|
||||||
|
import { createDidPeerJwt } from "@/libs/crypto/vc/passkeyDidPeer";
|
||||||
|
import {JWTDecoded} from "did-jwt/lib/JWT";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta info about a key
|
||||||
|
*/
|
||||||
|
export interface KeyMeta {
|
||||||
|
/**
|
||||||
|
* Decentralized ID for the key
|
||||||
|
*/
|
||||||
|
did: string;
|
||||||
|
/**
|
||||||
|
* Stringified IIDentifier object from Veramo
|
||||||
|
*/
|
||||||
|
identity?: string;
|
||||||
|
/**
|
||||||
|
* The Webauthn credential ID in hex, if this is from a passkey
|
||||||
|
*/
|
||||||
|
passkeyCredIdHex?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell whether a key is from a passkey
|
||||||
|
* @param keyMeta contains info about the key, whose passkeyCredIdHex determines if the key is from a passkey
|
||||||
|
*/
|
||||||
|
export function isFromPasskey(keyMeta?: KeyMeta): boolean {
|
||||||
|
return !!keyMeta?.passkeyCredIdHex;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEndorserJwtForKey(
|
||||||
|
account: KeyMeta,
|
||||||
|
payload: object,
|
||||||
|
) {
|
||||||
|
if (account?.identity) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const identity: IIdentifier = JSON.parse(account.identity!);
|
||||||
|
const privateKeyHex = identity.keys[0].privateKeyHex;
|
||||||
|
const signer = await SimpleSigner(privateKeyHex as string);
|
||||||
|
return didJwt.createJWT(payload, {
|
||||||
|
issuer: account.did,
|
||||||
|
signer: signer,
|
||||||
|
});
|
||||||
|
} else if (account?.passkeyCredIdHex) {
|
||||||
|
return createDidPeerJwt(account.did, account.passkeyCredIdHex, payload);
|
||||||
|
} else {
|
||||||
|
throw new Error("No identity data found to sign for DID " + account.did);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied out of did-jwt since it's deprecated in that library.
|
||||||
|
*
|
||||||
|
* The SimpleSigner returns a configured function for signing data.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const signer = SimpleSigner(import.meta.env.PRIVATE_KEY)
|
||||||
|
* signer(data, (err, signature) => {
|
||||||
|
* ...
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* @param {String} hexPrivateKey a hex encoded private key
|
||||||
|
* @return {Function} a configured signer function
|
||||||
|
*/
|
||||||
|
function SimpleSigner(hexPrivateKey: string): didJwt.Signer {
|
||||||
|
const signer = didJwt.ES256KSigner(didJwt.hexToBytes(hexPrivateKey), true);
|
||||||
|
return async (data) => {
|
||||||
|
const signature = (await signer(data)) as string;
|
||||||
|
return fromJose(signature);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// from did-jwt/util; see SimpleSigner above
|
||||||
|
function fromJose(signature: string): {
|
||||||
|
r: string;
|
||||||
|
s: string;
|
||||||
|
recoveryParam?: number;
|
||||||
|
} {
|
||||||
|
const signatureBytes: Uint8Array = didJwt.base64ToBytes(signature);
|
||||||
|
if (signatureBytes.length < 64 || signatureBytes.length > 65) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const r = bytesToHex(signatureBytes.slice(0, 32));
|
||||||
|
const s = bytesToHex(signatureBytes.slice(32, 64));
|
||||||
|
const recoveryParam =
|
||||||
|
signatureBytes.length === 65 ? signatureBytes[64] : undefined;
|
||||||
|
return {r, s, recoveryParam};
|
||||||
|
}
|
||||||
|
|
||||||
|
// from did-jwt/util; see SimpleSigner above
|
||||||
|
function bytesToHex(b: Uint8Array): string {
|
||||||
|
return u8a.toString(b, "base16");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeEndorserJwt(jwt: string): JWTDecoded {
|
||||||
|
return didJwt.decodeJWT(jwt);
|
||||||
|
}
|
||||||
@@ -21,7 +21,10 @@ import {
|
|||||||
} from "@simplewebauthn/types";
|
} from "@simplewebauthn/types";
|
||||||
|
|
||||||
import { AppString } from "@/constants/app";
|
import { AppString } from "@/constants/app";
|
||||||
import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers";
|
import {
|
||||||
|
getWebCrypto,
|
||||||
|
unwrapEC2Signature,
|
||||||
|
} from "@/libs/crypto/vc/passkeyHelpers";
|
||||||
|
|
||||||
const PEER_DID_PREFIX = "did:peer:";
|
const PEER_DID_PREFIX = "did:peer:";
|
||||||
const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0";
|
const PEER_DID_MULTIBASE_PREFIX = PEER_DID_PREFIX + "0";
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
import { Axios, AxiosRequestConfig, AxiosResponse } from "axios";
|
import { Axios, AxiosRequestConfig, AxiosResponse } from "axios";
|
||||||
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 { 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";
|
||||||
import { accessToken, SimpleSigner } from "@/libs/crypto";
|
import { accessToken } from "@/libs/crypto";
|
||||||
import { NonsensitiveDexie } from "@/db/index";
|
import { NonsensitiveDexie } from "@/db/index";
|
||||||
import { createDidPeerJwt } from "@/libs/didPeer";
|
|
||||||
import { getAccount, getIdentity } from "@/libs/util";
|
import { getAccount, getIdentity } from "@/libs/util";
|
||||||
|
import { createEndorserJwtForKey, KeyMeta } from "@/libs/crypto/vc";
|
||||||
|
|
||||||
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
|
||||||
@@ -692,7 +691,7 @@ export async function createAndSubmitClaim(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const vcJwt: string = await createEndorserJwt(issuerDid, vcPayload);
|
const vcJwt: string = await createEndorserJwtForDid(issuerDid, vcPayload);
|
||||||
|
|
||||||
// Make the xhr request payload
|
// Make the xhr request payload
|
||||||
const payload = JSON.stringify({ jwtEncoded: vcJwt });
|
const payload = JSON.stringify({ jwtEncoded: vcJwt });
|
||||||
@@ -722,6 +721,14 @@ export async function createAndSubmitClaim(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createEndorserJwtForDid(
|
||||||
|
issuerDid: string,
|
||||||
|
payload: object,
|
||||||
|
) {
|
||||||
|
const account = await getAccount(issuerDid);
|
||||||
|
return createEndorserJwtForKey(account as KeyMeta, payload);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An AcceptAction is when someone accepts some contract or pledge.
|
* An AcceptAction is when someone accepts some contract or pledge.
|
||||||
*
|
*
|
||||||
@@ -937,25 +944,7 @@ export async function createEndorserJwtVcFromClaim(
|
|||||||
credentialSubject: claim,
|
credentialSubject: claim,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return createEndorserJwt(issuerDid, vcPayload);
|
return createEndorserJwtForDid(issuerDid, vcPayload);
|
||||||
}
|
|
||||||
|
|
||||||
export async function createEndorserJwt(issuerDid: string, payload: object) {
|
|
||||||
const account = await getAccount(issuerDid);
|
|
||||||
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: issuerDid,
|
|
||||||
signer: signer,
|
|
||||||
});
|
|
||||||
} else if (account?.passkeyCredIdHex) {
|
|
||||||
return createDidPeerJwt(issuerDid, account.passkeyCredIdHex, payload);
|
|
||||||
} else {
|
|
||||||
throw new Error("No identity data found to sign for DID " + issuerDid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function register(
|
export async function register(
|
||||||
@@ -980,7 +969,7 @@ export async function register(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
// Create a signature using private key of identity
|
// Create a signature using private key of identity
|
||||||
const vcJwt = await createEndorserJwt(activeDid, vcPayload);
|
const vcJwt = await createEndorserJwtForDid(activeDid, vcPayload);
|
||||||
|
|
||||||
const url = apiServer + "/api/v2/claim";
|
const url = apiServer + "/api/v2/claim";
|
||||||
const resp = await axios.post(url, { jwtEncoded: vcJwt });
|
const resp = await axios.post(url, { jwtEncoded: vcJwt });
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
|||||||
import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
|
import { deriveAddress, generateSeed, newIdentifier } from "@/libs/crypto";
|
||||||
import { GenericCredWrapper, containsHiddenDid } from "@/libs/endorserServer";
|
import { GenericCredWrapper, containsHiddenDid } from "@/libs/endorserServer";
|
||||||
import * as serverUtil from "@/libs/endorserServer";
|
import * as serverUtil from "@/libs/endorserServer";
|
||||||
import { createPeerDid, registerCredential } from "@/libs/didPeer";
|
import { createPeerDid, registerCredential } from "@/libs/crypto/vc/passkeyDidPeer";
|
||||||
|
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from "buffer";
|
||||||
|
import {KeyMeta} from "@/libs/crypto/vc";
|
||||||
|
|
||||||
export const PRIVACY_MESSAGE =
|
export const PRIVACY_MESSAGE =
|
||||||
"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.";
|
"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.";
|
||||||
@@ -196,9 +197,11 @@ export function findAllVisibleToDids(
|
|||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
export interface AccountKeyInfo extends Account, KeyMeta {}
|
||||||
|
|
||||||
export const getAccount = async (
|
export const getAccount = async (
|
||||||
activeDid: string,
|
activeDid: string,
|
||||||
): Promise<Account | undefined> => {
|
): Promise<AccountKeyInfo | undefined> => {
|
||||||
await accountsDB.open();
|
await accountsDB.open();
|
||||||
const account = (await accountsDB.accounts
|
const account = (await accountsDB.accounts
|
||||||
.where("did")
|
.where("did")
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ import { useClipboard } from "@vueuse/core";
|
|||||||
import QuickNav from "@/components/QuickNav.vue";
|
import QuickNav from "@/components/QuickNav.vue";
|
||||||
import { NotificationIface } from "@/constants/app";
|
import { NotificationIface } from "@/constants/app";
|
||||||
import { accountsDB, db } from "@/db/index";
|
import { accountsDB, db } from "@/db/index";
|
||||||
import { Account } from "@/db/tables/accounts";
|
|
||||||
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 {
|
import {
|
||||||
@@ -93,8 +92,7 @@ import {
|
|||||||
nextDerivationPath,
|
nextDerivationPath,
|
||||||
} from "@/libs/crypto";
|
} from "@/libs/crypto";
|
||||||
import {
|
import {
|
||||||
CONTACT_URL_PREFIX,
|
CONTACT_URL_PREFIX, createEndorserJwtForDid,
|
||||||
createEndorserJwt,
|
|
||||||
ENDORSER_JWT_URL_LOCATION,
|
ENDORSER_JWT_URL_LOCATION,
|
||||||
isDid,
|
isDid,
|
||||||
register,
|
register,
|
||||||
@@ -161,7 +159,7 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const vcJwt: string = await createEndorserJwt(identity.did, contactInfo);
|
const vcJwt: string = await createEndorserJwtForDid(identity.did, contactInfo);
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,16 +81,29 @@
|
|||||||
v-if="!activeDid"
|
v-if="!activeDid"
|
||||||
class="bg-amber-200 rounded-md text-center px-4 py-3 mb-4"
|
class="bg-amber-200 rounded-md text-center px-4 py-3 mb-4"
|
||||||
>
|
>
|
||||||
<p class="text-lg mb-3">
|
<div v-if="PASSKEYS_ENABLED">
|
||||||
To recognize giving, have someone register you:
|
<p class="text-lg mb-3">
|
||||||
</p>
|
Choose how to see info from your contacts or share contributions:
|
||||||
<div class="flex justify-center">
|
</p>
|
||||||
<!-- <button-->
|
<div class="flex justify-between">
|
||||||
<!-- class="block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"-->
|
<button
|
||||||
<!-- @click="generateIdentifier()"-->
|
class="block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
|
||||||
<!-- >-->
|
@click="generateIdentifier()"
|
||||||
<!-- Let me start the easiest (with a passkey).-->
|
>
|
||||||
<!-- </button>-->
|
Let me start the easiest (with a passkey).
|
||||||
|
</button>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'start' }"
|
||||||
|
class="block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
|
||||||
|
>
|
||||||
|
Give me all the options.
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p class="text-lg mb-3">
|
||||||
|
To recognize giving or collaborate, have someone register you:
|
||||||
|
</p>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: 'contact-qr' }"
|
:to="{ name: 'contact-qr' }"
|
||||||
class="block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
|
class="block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
|
||||||
@@ -327,7 +340,7 @@ import FeedFilters from "@/components/FeedFilters.vue";
|
|||||||
import InfiniteScroll from "@/components/InfiniteScroll.vue";
|
import InfiniteScroll from "@/components/InfiniteScroll.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";
|
||||||
import { AppString, NotificationIface } from "@/constants/app";
|
import { AppString, NotificationIface, PASSKEYS_ENABLED } from "@/constants/app";
|
||||||
import { db, accountsDB } from "@/db/index";
|
import { db, accountsDB } from "@/db/index";
|
||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import {
|
import {
|
||||||
@@ -383,6 +396,7 @@ export default class HomeView extends Vue {
|
|||||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
AppString = AppString;
|
AppString = AppString;
|
||||||
|
PASSKEYS_ENABLED = PASSKEYS_ENABLED;
|
||||||
|
|
||||||
activeDid = "";
|
activeDid = "";
|
||||||
allContacts: Array<Contact> = [];
|
allContacts: Array<Contact> = [];
|
||||||
|
|||||||
@@ -246,14 +246,15 @@ import { Component, Vue } from "vue-facing-decorator";
|
|||||||
import QuickNav from "@/components/QuickNav.vue";
|
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 { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
|
import * as vcLib from "@/libs/crypto/vc";
|
||||||
import {
|
import {
|
||||||
PeerSetup,
|
PeerSetup,
|
||||||
verifyJwtP256,
|
verifyJwtP256,
|
||||||
verifyJwtSimplewebauthn,
|
verifyJwtSimplewebauthn,
|
||||||
verifyJwtWebCrypto,
|
verifyJwtWebCrypto,
|
||||||
} from "@/libs/didPeer";
|
} from "@/libs/crypto/vc/passkeyDidPeer";
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
import {AccountKeyInfo, getAccount, registerAndSavePasskey} from "@/libs/util";
|
||||||
import { registerAndSavePasskey } from "@/libs/util";
|
|
||||||
|
|
||||||
const inputFileNameRef = ref<Blob>();
|
const inputFileNameRef = ref<Blob>();
|
||||||
|
|
||||||
@@ -360,6 +361,13 @@ export default class Help extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createJwtSimplewebauthn() {
|
public async createJwtSimplewebauthn() {
|
||||||
|
const account: AccountKeyInfo | undefined = await getAccount(
|
||||||
|
this.activeDid || "",
|
||||||
|
);
|
||||||
|
if (!vcLib.isFromPasskey(account)) {
|
||||||
|
alert(`The DID ${this.activeDid} is not passkey-enabled.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.peerSetup = new PeerSetup();
|
this.peerSetup = new PeerSetup();
|
||||||
this.jwt = await this.peerSetup.createJwtSimplewebauthn(
|
this.jwt = await this.peerSetup.createJwtSimplewebauthn(
|
||||||
this.activeDid as string,
|
this.activeDid as string,
|
||||||
@@ -370,6 +378,13 @@ export default class Help extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createJwtNavigator() {
|
public async createJwtNavigator() {
|
||||||
|
const account: AccountKeyInfo | undefined = await getAccount(
|
||||||
|
this.activeDid || "",
|
||||||
|
);
|
||||||
|
if (!vcLib.isFromPasskey(account)) {
|
||||||
|
alert(`The DID ${this.activeDid} is not passkey-enabled.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.peerSetup = new PeerSetup();
|
this.peerSetup = new PeerSetup();
|
||||||
this.jwt = await this.peerSetup.createJwtNavigator(
|
this.jwt = await this.peerSetup.createJwtNavigator(
|
||||||
this.activeDid as string,
|
this.activeDid as string,
|
||||||
|
|||||||
Reference in New Issue
Block a user