Browse Source

make a passkey-generator in start & home pages, and make that the default

master
Trent Larson 3 months ago
parent
commit
2dd6e9b07a
  1. 5
      src/libs/didPeer.ts
  2. 35
      src/libs/util.ts
  3. 7
      src/views/GiftedDetails.vue
  4. 206
      src/views/HomeView.vue
  5. 79
      src/views/StartView.vue
  6. 24
      src/views/TestView.vue

5
src/libs/didPeer.ts

@ -20,6 +20,7 @@ import {
PublicKeyCredentialRequestOptionsJSON, PublicKeyCredentialRequestOptionsJSON,
} from "@simplewebauthn/types"; } from "@simplewebauthn/types";
import { AppString } from "@/constants/app";
import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers"; import { getWebCrypto, unwrapEC2Signature } from "@/libs/crypto/passkeyHelpers";
const PEER_DID_PREFIX = "did:peer:"; const PEER_DID_PREFIX = "did:peer:";
@ -42,9 +43,9 @@ function arrayToBase64Url(anything: Uint8Array) {
export async function registerCredential(passkeyName?: string) { export async function registerCredential(passkeyName?: string) {
const options: PublicKeyCredentialCreationOptionsJSON = const options: PublicKeyCredentialCreationOptionsJSON =
await generateRegistrationOptions({ await generateRegistrationOptions({
rpName: "Time Safari", rpName: AppString.APP_NAME,
rpID: window.location.hostname, rpID: window.location.hostname,
userName: passkeyName || "Time Safari User", userName: passkeyName || AppString.APP_NAME + " User",
// Don't prompt users for additional information about the authenticator // Don't prompt users for additional information about the authenticator
// (Recommended for smoother UX) // (Recommended for smoother UX)
attestationType: "none", attestationType: "none",

35
src/libs/util.ts

@ -11,6 +11,9 @@ 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 { Buffer } from "buffer";
export const PRIVACY_MESSAGE = export const PRIVACY_MESSAGE =
"The data you send be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to those you allow."; "The data you send be visible to the world -- except: your IDs and the IDs of anyone you tag will stay private, only visible to those you allow.";
@ -239,6 +242,38 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
return newId.did; return newId.did;
}; };
export const registerAndSavePasskey = async (
keyName: string,
): Promise<Account> => {
const cred = await registerCredential(keyName);
const publicKeyBytes = cred.publicKeyBytes;
const did = createPeerDid(publicKeyBytes as Uint8Array);
const passkeyCredIdHex = cred.credIdHex as string;
const account = {
dateCreated: new Date().toISOString(),
did,
passkeyCredIdHex,
publicKeyHex: Buffer.from(publicKeyBytes).toString("hex"),
};
await accountsDB.open();
await accountsDB.accounts.add(account);
return account;
};
export const registerSaveAndActivatePasskey = async (
keyName: string,
): Promise<Account> => {
const account = await registerAndSavePasskey(keyName);
await db.open();
await db.settings.update(MASTER_SETTINGS_KEY, {
activeDid: account.did,
});
return account;
};
export const sendTestThroughPushServer = async ( export const sendTestThroughPushServer = async (
subscriptionJSON: PushSubscriptionJSON, subscriptionJSON: PushSubscriptionJSON,
skipFilter: boolean, skipFilter: boolean,

7
src/views/GiftedDetails.vue

@ -180,16 +180,17 @@ import ImageMethodDialog from "@/components/ImageMethodDialog.vue";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import TopMessage from "@/components/TopMessage.vue"; import TopMessage from "@/components/TopMessage.vue";
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app"; import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "@/constants/app";
import {accountsDB, db} from "@/db/index"; import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { import {
constructGive, constructGive,
createAndSubmitGive, didInfo, createAndSubmitGive,
didInfo,
getPlanFromCache, getPlanFromCache,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import * as libsUtil from "@/libs/util"; import * as libsUtil from "@/libs/util";
import { accessToken } from "@/libs/crypto"; import { accessToken } from "@/libs/crypto";
import {Contact} from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
@Component({ @Component({
components: { components: {

206
src/views/HomeView.vue

@ -5,7 +5,7 @@
<!-- CONTENT --> <!-- CONTENT -->
<section id="Content" class="p-2 pb-24 max-w-3xl mx-auto"> <section id="Content" class="p-2 pb-24 max-w-3xl mx-auto">
<h1 id="ViewHeading" class="text-4xl text-center font-light px-4 mb-8"> <h1 id="ViewHeading" class="text-4xl text-center font-light px-4 mb-8">
Time Safari {{ AppString.APP_NAME }}
</h1> </h1>
<!-- prompt to install notifications --> <!-- prompt to install notifications -->
@ -79,89 +79,100 @@
<!-- !isCreatingIdentifier --> <!-- !isCreatingIdentifier -->
<div <div
v-if="!activeDid" v-if="!activeDid"
class="bg-amber-200 rounded-md overflow-hidden 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"> <p class="text-lg mb-3">
Want to connect with your contacts, or share contributions or Want to see info from your contacts, or share contributions?
projects?
</p> </p>
<router-link <div class="flex justify-between">
:to="{ name: 'start' }" <button
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"
> @click="generateIdentifier()"
Create An Identifier >
</router-link> Let me start the easiest (with a passkey).
</div> </button>
<router-link
<div :to="{ name: 'start' }"
v-else-if="!isRegistered" 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="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4" >
> Give me all the options.
<!-- activeDid && !isRegistered --> </router-link>
Someone must register you before you can give kudos or make offers or </div>
create projects... basically before doing anything.
<router-link
: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"
>
Show Them Your Identifier Info
</router-link>
</div> </div>
<div v-else> <div v-else class="mb-4">
<!-- activeDid && isRegistered --> <!-- activeDid -->
<!-- show the actions for recognizing a give -->
<div class="mb-4">
<h2 class="text-xl font-bold">Record Something Given By:</h2>
</div>
<ul <div
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5" v-if="!isRegistered"
class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
> >
<li @click="openDialog()"> <!-- activeDid && !isRegistered -->
<img Someone must register you before you can give kudos or make offers
src="../assets/blank-square.svg" or create projects... basically before doing anything.
class="mx-auto border border-slate-300 rounded-md mb-1"
/>
<h3
class="text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
Unnamed/Unknown
</h3>
</li>
<li
v-for="contact in allContacts.slice(0, 7)"
:key="contact.did"
@click="openDialog(contact)"
>
<EntityIcon
:contact="contact"
:iconSize="64"
class="mx-auto border border-slate-300 rounded-md mb-1 cursor-pointer"
/>
<h3
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
{{ contact.name || contact.did }}
</h3>
</li>
</ul>
<div class="flex justify-between">
<router-link <router-link
v-if="allContacts.length >= 7" :to="{ name: 'contact-qr' }"
:to="{ name: 'contact-gift' }" 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-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
> >
Choose From All Contacts Show Them Your Identifier Info
</router-link> </router-link>
<button </div>
@click="openGiftedPrompts()"
class="block text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" <div v-else>
<!-- activeDid && isRegistered -->
<!-- show the actions for recognizing a give -->
<div class="mb-4">
<h2 class="text-xl font-bold">Record Something Given By:</h2>
</div>
<ul
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5"
> >
Ideas... <li @click="openDialog()">
</button> <img
src="../assets/blank-square.svg"
class="mx-auto border border-slate-300 rounded-md mb-1"
/>
<h3
class="text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
Unnamed/Unknown
</h3>
</li>
<li
v-for="contact in allContacts.slice(0, 7)"
:key="contact.did"
@click="openDialog(contact)"
>
<EntityIcon
:contact="contact"
:iconSize="64"
class="mx-auto border border-slate-300 rounded-md mb-1 cursor-pointer"
/>
<h3
class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
{{ contact.name || contact.did }}
</h3>
</li>
</ul>
<div class="flex justify-between">
<router-link
v-if="allContacts.length >= 7"
:to="{ name: 'contact-gift' }"
class="block text-center text-md font-bold bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
>
Choose From All Contacts
</router-link>
<button
@click="openGiftedPrompts()"
class="block text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
>
Ideas...
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -309,6 +320,7 @@ import { IIdentifier } from "@veramo/core";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router"; import { Router } from "vue-router";
import App from "../App.vue";
import EntityIcon from "@/components/EntityIcon.vue"; import EntityIcon from "@/components/EntityIcon.vue";
import GiftedDialog from "@/components/GiftedDialog.vue"; import GiftedDialog from "@/components/GiftedDialog.vue";
import GiftedPrompts from "@/components/GiftedPrompts.vue"; import GiftedPrompts from "@/components/GiftedPrompts.vue";
@ -316,7 +328,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 { NotificationIface } from "@/constants/app"; import { AppString, NotificationIface } from "@/constants/app";
import { db, accountsDB } from "@/db/index"; import { db, accountsDB } from "@/db/index";
import { Account } from "@/db/tables/accounts"; import { Account } from "@/db/tables/accounts";
import { Contact } from "@/db/tables/contacts"; import { Contact } from "@/db/tables/contacts";
@ -336,7 +348,7 @@ import {
GiverReceiverInputInfo, GiverReceiverInputInfo,
GiveSummaryRecord, GiveSummaryRecord,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import { generateSaveAndActivateIdentity } from "@/libs/util"; import { registerSaveAndActivatePasskey } from "@/libs/util";
interface GiveRecordWithContactInfo extends GiveSummaryRecord { interface GiveRecordWithContactInfo extends GiveSummaryRecord {
giver: { giver: {
@ -354,6 +366,11 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord {
} }
@Component({ @Component({
computed: {
App() {
return App;
},
},
components: { components: {
GiftedDialog, GiftedDialog,
GiftedPrompts, GiftedPrompts,
@ -367,6 +384,8 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord {
export default class HomeView extends Vue { export default class HomeView extends Vue {
$notify!: (notification: NotificationIface, timeout?: number) => void; $notify!: (notification: NotificationIface, timeout?: number) => void;
AppString = AppString;
activeDid = ""; activeDid = "";
allContacts: Array<Contact> = []; allContacts: Array<Contact> = [];
allMyDids: Array<string> = []; allMyDids: Array<string> = [];
@ -374,6 +393,7 @@ export default class HomeView extends Vue {
feedData: GiveRecordWithContactInfo[] = []; feedData: GiveRecordWithContactInfo[] = [];
feedPreviousOldestId?: string; feedPreviousOldestId?: string;
feedLastViewedClaimId?: string; feedLastViewedClaimId?: string;
givenName = "";
isAnyFeedFilterOn: boolean; isAnyFeedFilterOn: boolean;
isCreatingIdentifier = false; isCreatingIdentifier = false;
isFeedFilteredByVisible = false; isFeedFilteredByVisible = false;
@ -397,15 +417,6 @@ export default class HomeView extends Vue {
return identity; // may be null return identity; // may be null
} }
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() {
try { try {
await accountsDB.open(); await accountsDB.open();
@ -418,6 +429,7 @@ export default class HomeView extends Vue {
this.activeDid = settings?.activeDid || ""; this.activeDid = settings?.activeDid || "";
this.allContacts = await db.contacts.toArray(); this.allContacts = await db.contacts.toArray();
this.feedLastViewedClaimId = settings?.lastViewedClaimId; this.feedLastViewedClaimId = settings?.lastViewedClaimId;
this.givenName = settings?.firstName || "";
this.isFeedFilteredByVisible = !!settings?.filterFeedByVisible; this.isFeedFilteredByVisible = !!settings?.filterFeedByVisible;
this.isFeedFilteredByNearby = !!settings?.filterFeedByNearby; this.isFeedFilteredByNearby = !!settings?.filterFeedByNearby;
this.isRegistered = !!settings?.isRegistered; this.isRegistered = !!settings?.isRegistered;
@ -426,14 +438,7 @@ export default class HomeView extends Vue {
this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings); this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings);
if (this.allMyDids.length === 0) { // someone may have have registered after sharing contact info, so recheck
this.isCreatingIdentifier = true;
this.activeDid = await generateSaveAndActivateIdentity();
this.allMyDids = [this.activeDid];
this.isCreatingIdentifier = false;
}
// someone may have have registered after sharing contact info
if (!this.isRegistered && this.activeDid) { if (!this.isRegistered && this.activeDid) {
const identity = await this.getIdentity(this.activeDid); const identity = await this.getIdentity(this.activeDid);
try { try {
@ -475,6 +480,15 @@ export default class HomeView extends Vue {
} }
} }
async generateIdentifier() {
this.isCreatingIdentifier = true;
const account = await registerSaveAndActivatePasskey(
AppString.APP_NAME + (this.givenName ? " - " + this.givenName : ""),
);
this.activeDid = account.did;
this.allMyDids = this.allMyDids.concat(this.activeDid);
this.isCreatingIdentifier = false;
}
resultsAreFiltered() { resultsAreFiltered() {
return this.isFeedFilteredByVisible || this.isFeedFilteredByNearby; return this.isFeedFilteredByVisible || this.isFeedFilteredByNearby;
} }
@ -483,7 +497,7 @@ export default class HomeView extends Vue {
return "Notification" in window; return "Notification" in window;
} }
public async buildHeaders() { async buildHeaders() {
const headers: HeadersInit = { const headers: HeadersInit = {
"Content-Type": "application/json", "Content-Type": "application/json",
}; };
@ -520,7 +534,7 @@ export default class HomeView extends Vue {
* Data loader used by infinite scroller * Data loader used by infinite scroller
* @param payload is the flag from the InfiniteScroll indicating if it should load * @param payload is the flag from the InfiniteScroll indicating if it should load
**/ **/
public async loadMoreGives(payload: boolean) { async loadMoreGives(payload: boolean) {
// Since feed now loads projects along the way, it takes longer // Since feed now loads projects along the way, it takes longer
// and the InfiniteScroll component triggers a load before finished. // and the InfiniteScroll component triggers a load before finished.
// One alternative is to totally separate the project link loading. // One alternative is to totally separate the project link loading.
@ -542,7 +556,7 @@ export default class HomeView extends Vue {
} }
} }
public async updateAllFeed() { async updateAllFeed() {
this.isFeedLoading = true; this.isFeedLoading = true;
let endOfResults = true; let endOfResults = true;
await this.retrieveGives(this.apiServer, this.feedPreviousOldestId) await this.retrieveGives(this.apiServer, this.feedPreviousOldestId)
@ -650,7 +664,7 @@ export default class HomeView extends Vue {
* @param beforeId the earliest ID (of previous searches) to search earlier * @param beforeId the earliest ID (of previous searches) to search earlier
* @return claims in reverse chronological order * @return claims in reverse chronological order
*/ */
public async retrieveGives(endorserApiServer: string, beforeId?: string) { async retrieveGives(endorserApiServer: string, beforeId?: string) {
const beforeQuery = beforeId == null ? "" : "&beforeId=" + beforeId; const beforeQuery = beforeId == null ? "" : "&beforeId=" + beforeId;
const response = await fetch( const response = await fetch(
endorserApiServer + endorserApiServer +

79
src/views/StartView.vue

@ -17,7 +17,7 @@
<!-- Heading --> <!-- Heading -->
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
Start Here Generate an Identity
</h1> </h1>
</div> </div>
@ -25,33 +25,57 @@
<div id="start-question" class="mt-8"> <div id="start-question" class="mt-8">
<div class="max-w-3xl mx-auto"> <div class="max-w-3xl mx-auto">
<p class="text-center text-xl font-light"> <p class="text-center text-xl font-light">
Do you want a new identifier of your own? How do you want to create this identifier?
</p> </p>
<p class="text-center font-light"> <p class="text-center font-light mt-6">
If you haven't used this before, click "Yes" to generate a new A <strong>passkey</strong> is easy to manage, though it is less
identifier. interoperable with other systems for advanced uses.
<a
href="https://www.perplexity.ai/search/what-are-passkeys-v2SHV3yLQlyA2CYH6.Nvhg"
target="_blank"
>
<fa icon="info-circle" class="fa-fw text-blue-500" />
</a>
</p> </p>
<p class="text-center mb-4 font-light"> <p class="text-center font-light mt-4">
Only click "No" if you have a seed of 12 or 24 words generated A <strong>new seed</strong> allows you full control over the keys,
elsewhere. though you are responsible for backups.
<a
href="https://www.perplexity.ai/search/what-is-a-seed-phrase-OqiP9foVRXidr_2le5OFKA"
target="_blank"
>
<fa icon="info-circle" class="fa-fw text-blue-500" />
</a>
</p> </p>
<a <div class="grid grid-cols-1 sm:grid-cols-2 gap-2 mt-4">
@click="onClickYes()" <a
class="block w-full text-center text-lg uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2" @click="onClickNewPasskey()"
> class="block w-full text-center text-lg uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2 cursor-pointer"
Yes, generate one >
</a> Generate one with a passkey
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> </a>
<a
@click="onClickNewSeed()"
class="block w-full text-center text-lg uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md mb-2 cursor-pointer"
>
Generate one with a new seed
</a>
</div>
<p class="text-center font-light mt-4">
You can also import an existing seed or derive a new address from an
existing seed.
</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 mt-2">
<a <a
@click="onClickNo()" @click="onClickNo()"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" class="block w-full text-center text-md uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md cursor-pointer"
> >
No, I have a seed You have a seed
</a> </a>
<a <a
v-if="numAccounts > 0" v-if="numAccounts > 0"
@click="onClickDerive()" @click="onClickDerive()"
class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" class="block w-full text-center text-md uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md cursor-pointer"
> >
Derive new address from existing seed Derive new address from existing seed
</a> </a>
@ -64,23 +88,38 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { accountsDB } from "@/db/index"; import { AppString } from "@/constants/app";
import { accountsDB, db } from "@/db/index";
import { MASTER_SETTINGS_KEY, Settings } from "@/db/tables/settings";
import { registerSaveAndActivatePasskey } from "@/libs/util";
@Component({ @Component({
components: {}, components: {},
}) })
export default class StartView extends Vue { export default class StartView extends Vue {
givenName = "";
numAccounts = 0; numAccounts = 0;
async mounted() { async mounted() {
await db.open();
const settings = (await db.settings.get(MASTER_SETTINGS_KEY)) as Settings;
this.givenName = settings?.firstName || "";
await accountsDB.open(); await accountsDB.open();
this.numAccounts = await accountsDB.accounts.count(); this.numAccounts = await accountsDB.accounts.count();
} }
public onClickYes() { public onClickNewSeed() {
this.$router.push({ name: "new-identifier" }); this.$router.push({ name: "new-identifier" });
} }
public async onClickNewPasskey() {
const keyName =
AppString.APP_NAME + (this.givenName ? " - " + this.givenName : "");
await registerSaveAndActivatePasskey(keyName);
this.$router.push({ name: "account" });
}
public onClickNo() { public onClickNo() {
this.$router.push({ name: "import-account" }); this.$router.push({ name: "import-account" });
} }

24
src/views/TestView.vue

@ -244,7 +244,7 @@ import { ref } from "vue";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import QuickNav from "@/components/QuickNav.vue"; import QuickNav from "@/components/QuickNav.vue";
import { 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, createPeerDid,
@ -255,6 +255,7 @@ import {
verifyJwtWebCrypto, verifyJwtWebCrypto,
} from "@/libs/didPeer"; } from "@/libs/didPeer";
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import {registerAndSavePasskey} from "@/libs/util";
const inputFileNameRef = ref<Blob>(); const inputFileNameRef = ref<Blob>();
@ -333,14 +334,14 @@ export default class Help extends Vue {
} }
public async register() { public async register() {
const DEFAULT_USERNAME = "Time Safari Tester"; const DEFAULT_USERNAME = AppString.APP_NAME + " Tester";
if (!this.userName) { if (!this.userName) {
this.$notify( this.$notify(
{ {
group: "modal", group: "modal",
type: "confirm", type: "confirm",
title: "No Name", title: "No Name",
text: "You must have a name to attach to this passkey. Would you like to enter your own name first?", text: "You should have a name to attach to this passkey. Would you like to enter your own name first?",
onNo: async () => { onNo: async () => {
this.userName = DEFAULT_USERNAME; this.userName = DEFAULT_USERNAME;
}, },
@ -353,18 +354,11 @@ export default class Help extends Vue {
); );
return; return;
} }
const cred = await registerCredential("Time Safari - " + this.userName); const account = await registerAndSavePasskey(
const publicKeyBytes = cred.publicKeyBytes; AppString.APP_NAME + " - " + this.userName,
this.activeDid = createPeerDid(publicKeyBytes as Uint8Array); );
this.credIdHex = cred.credIdHex as string; this.activeDid = account.did;
this.credIdHex = account.passkeyCredIdHex;
await accountsDB.open();
await accountsDB.accounts.add({
dateCreated: new Date().toISOString(),
did: this.activeDid,
passkeyCredIdHex: this.credIdHex,
publicKeyHex: Buffer.from(publicKeyBytes).toString("hex"),
});
} }
public async createJwtSimplewebauthn() { public async createJwtSimplewebauthn() {

Loading…
Cancel
Save