diff --git a/package-lock.json b/package-lock.json index cbbedcc..793ed30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "class-transformer": "^0.5.1", "core-js": "^3.26.1", "dexie": "^3.2.2", + "did-jwt": "^6.9.0", "ethereum-cryptography": "^1.1.2", "ethereumjs-util": "^7.1.5", "ethr-did-resolver": "^8.0.0", @@ -12186,9 +12187,9 @@ } }, "node_modules/did-jwt": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-6.10.1.tgz", - "integrity": "sha512-YJOvkuPKKX364ooAFNxZPcz/KBLRwLhRABQVQlVEqOjygsCkplNFB3UL97UqZ7Y3cAG6Jh5jKoAC4xFSm+h0qw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/did-jwt/-/did-jwt-6.9.0.tgz", + "integrity": "sha512-kZ8pakovM2VkG0pia6x0SA9/1rl9dOUti4i2FL3xg7arJDWW7dACJxX+6gQK7iR/DvXrfFo8F784ejHVbw9ryA==", "dependencies": { "@stablelib/ed25519": "^1.0.2", "@stablelib/random": "^1.0.1", diff --git a/package.json b/package.json index 178ca01..e6a9fb7 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "class-transformer": "^0.5.1", "core-js": "^3.26.1", "dexie": "^3.2.2", + "did-jwt": "^6.9.0", "ethereum-cryptography": "^1.1.2", "ethereumjs-util": "^7.1.5", "ethr-did-resolver": "^8.0.0", diff --git a/src/libs/crypto/index.ts b/src/libs/crypto/index.ts index a71cec1..a760295 100644 --- a/src/libs/crypto/index.ts +++ b/src/libs/crypto/index.ts @@ -5,44 +5,8 @@ import { entropyToMnemonic } from "ethereum-cryptography/bip39"; import { wordlist } from "ethereum-cryptography/bip39/wordlists/english"; import { HDNode } from "@ethersproject/hdnode"; import * as didJwt from "did-jwt"; -import { Signer } from "did-jwt"; import * as u8a from "uint8arrays"; -export function hexToBytes(s: string): Uint8Array { - const input = s.startsWith("0x") ? s.substring(2) : s; - return u8a.fromString(input.toLowerCase(), "base16"); -} - -export function fromJose(signature: string): { - r: string; - s: string; - recoveryParam?: number; -} { - const signatureBytes: Uint8Array = 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 }; -} - -export function bytesToHex(b: Uint8Array): string { - return u8a.toString(b, "base16"); -} - -export function base64ToBytes(s: string): Uint8Array { - const inputBase64Url = s - .replace(/\+/g, "-") - .replace(/\//g, "_") - .replace(/=/g, ""); - return u8a.fromString(inputBase64Url, "base64url"); -} - /** * * @@ -117,12 +81,8 @@ export const createIdentifier = (): string => { export const accessToken = async (identifier: IIdentifier) => { const did: string = identifier.did; const privateKeyHex: string = identifier.keys[0].privateKeyHex as string; - const input = privateKeyHex.startsWith("0x") - ? privateKeyHex.substring(2) - : privateKeyHex; - const privateKeyBytes = u8a.fromString(input.toLowerCase(), "base16"); - const signer = didJwt.SimpleSigner(privateKeyHex); + const signer = SimpleSigner(privateKeyHex); const nowEpoch = Math.floor(Date.now() / 1000); const endEpoch = nowEpoch + 60; // add one minute @@ -138,17 +98,14 @@ export const accessToken = async (identifier: IIdentifier) => { }; export const sign = async (privateKeyHex: string) => { - const input = privateKeyHex.startsWith("0x") - ? privateKeyHex.substring(2) - : privateKeyHex; - const privateKeyBytes = u8a.fromString(input.toLowerCase(), "base16"); - - const signer = didJwt.SimpleSigner(privateKeyHex); + const signer = SimpleSigner(privateKeyHex); return signer; }; /** + * Copied out of did-jwt since it's deprecated in that library. + * * The SimpleSigner returns a configured function for signing data. * * @example @@ -160,10 +117,34 @@ export const sign = async (privateKeyHex: string) => { * @param {String} hexPrivateKey a hex encoded private key * @return {Function} a configured signer function */ -export const SimpleSigner = async (hexPrivateKey: string): Promise => { - const signer = didJwt.ES256KSigner(hexToBytes(hexPrivateKey), true); +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"); +} diff --git a/src/main.ts b/src/main.ts index a8fa5f4..c5fca66 100644 --- a/src/main.ts +++ b/src/main.ts @@ -21,6 +21,7 @@ import { faQrcode, faUser, faPen, + faPlus, faTrashCan, faCalendar, faEllipsisVertical, @@ -40,6 +41,7 @@ library.add( faQrcode, faUser, faPen, + faPlus, faTrashCan, faCalendar, faEllipsisVertical, diff --git a/src/router/index.ts b/src/router/index.ts index 9697d48..38611c9 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -7,13 +7,19 @@ const routes: Array = [ name: "home", component: () => import(/* webpackChunkName: "start" */ "../views/DiscoverView.vue"), + beforeEnter: (to, from, next) => { + const appStore = useAppStore(); + const isAuthenticated = appStore.condition === "registered"; + if (isAuthenticated) { + next(); + } else { + next({ name: "start" }); + } + }, }, { path: "/about", name: "about", - // route level code-splitting - // this generates a separate chunk (about.[hash].js) for this route - // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ "../views/AboutView.vue"), }, @@ -75,6 +81,12 @@ const routes: Array = [ /* webpackChunkName: "new-edit-commitment" */ "../views/NewEditCommitmentView.vue" ), }, + { + path: "/project", + name: "project", + component: () => + import(/* webpackChunkName: "project" */ "../views/ProjectViewView.vue"), + }, { path: "/new-edit-project", name: "new-edit-project", @@ -83,12 +95,6 @@ const routes: Array = [ /* webpackChunkName: "new-edit-project" */ "../views/NewEditProjectView.vue" ), }, - { - path: "/project", - name: "project", - component: () => - import(/* webpackChunkName: "project" */ "../views/ProjectViewView.vue"), - }, { path: "/projects", name: "projects", @@ -111,27 +117,4 @@ const router = createRouter({ routes, }); -router.beforeEach(async (to, from, next) => { - const publicPages = ["/start", "/account", "/import-account"]; - const isPublic = publicPages.includes(to.path); - const appStore = useAppStore(); - console.log(to); - if (to.path === "/" && appStore.condition === "registered") { - next({ path: "/account" }); - } else if (isPublic) { - switch (appStore.condition) { - case "registered": - next(); - break; - default: - next(); - break; - } - } else if (appStore.condition === "uninitialized") { - next({ path: "/start" }); - } else { - next(); - } -}); - export default router; diff --git a/src/store/app.ts b/src/store/app.ts index 3ab7b8c..f3cc0c4 100644 --- a/src/store/app.ts +++ b/src/store/app.ts @@ -12,9 +12,14 @@ export const useAppStore = defineStore({ typeof localStorage["lastView"] == "undefined" ? "/start" : localStorage["lastView"], + _projectId: + typeof localStorage.getItem("projectId") === "undefined" + ? "" + : localStorage.getItem("projectId"), }), getters: { condition: (state) => state._condition, + projectId: (state): string => state._projectId as string, }, actions: { reset() { @@ -23,5 +28,8 @@ export const useAppStore = defineStore({ setCondition(newCondition: string) { localStorage.setItem("condition", newCondition); }, + setProjectId(newProjectId: string) { + localStorage.setItem("projectId", newProjectId); + }, }, }); diff --git a/src/views/DiscoverView.vue b/src/views/DiscoverView.vue index 091bd84..c351481 100644 --- a/src/views/DiscoverView.vue +++ b/src/views/DiscoverView.vue @@ -19,7 +19,7 @@
  • diff --git a/src/views/NewEditProjectView.vue b/src/views/NewEditProjectView.vue index bff74d3..1fffbcd 100644 --- a/src/views/NewEditProjectView.vue +++ b/src/views/NewEditProjectView.vue @@ -10,7 +10,6 @@ class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" > - [New/Edit] Project @@ -39,7 +38,7 @@ type="text" placeholder="Project Name" class="block w-full rounded border border-slate-400 mb-4 px-3 py-2" - v-modal="projectName" + v-model="projectName" />