diff --git a/iac/Backend.ts b/iac/Backend.ts index b82041d4..eed020f9 100644 --- a/iac/Backend.ts +++ b/iac/Backend.ts @@ -70,6 +70,7 @@ function elysiaBackend( postgres, sessionEncryptionKey, jwtSecret, + jwtSdkSecret, vapidPrivateKey, vapidPublicKey, coinGeckoApiKey, @@ -108,6 +109,7 @@ function elysiaBackend( // some secrets sessionEncryptionKey, jwtSecret, + jwtSdkSecret, masterSecretId, // mongo mongoExampleUri, diff --git a/knip.ts b/knip.ts index 61ed1aa9..29fece2e 100644 --- a/knip.ts +++ b/knip.ts @@ -19,6 +19,10 @@ const config: KnipConfig = { "packages/shared": { entry: "**/*.{ts,tsx}", }, + "packages/wallet": { + entry: ["src/**/*.tsx", "src/app/service-worker.ts"], + project: ["src/**/*.{ts,tsx}"], + }, "packages/backend-elysia": { entry: "src/index.ts", project: "src/**/*.ts", diff --git a/packages/backend-elysia/package.json b/packages/backend-elysia/package.json index 0b8f7915..3e765026 100644 --- a/packages/backend-elysia/package.json +++ b/packages/backend-elysia/package.json @@ -2,7 +2,9 @@ "name": "@frak-labs/backend-elysia", "description": "Elysia Backend for the frak ecosystem", "version": "0.0.2", + "private": true, "type": "module", + "license": "GNU GPL 3.0", "scripts": { "lint": "biome lint .", "format:check": "biome check .", diff --git a/packages/wallet/src/context/wallet/action/registerOptions.ts b/packages/wallet/src/context/wallet/action/registerOptions.ts index a497a73b..e138bd36 100644 --- a/packages/wallet/src/context/wallet/action/registerOptions.ts +++ b/packages/wallet/src/context/wallet/action/registerOptions.ts @@ -1,10 +1,5 @@ "use client"; - -import { - defaultUsername, - rpId, - rpName, -} from "@/context/wallet/smartWallet/webAuthN"; +import { WebAuthN } from "@frak-labs/app-essentials"; import { generateRegistrationOptions } from "@simplewebauthn/server"; import type { AuthenticatorTransportFuture, @@ -34,7 +29,7 @@ export async function getRegisterOptions({ const year = date.getFullYear().toString(); // Get the username - const username = `${defaultUsername}-${day}-${month}-${year}`; + const username = `${WebAuthN.defaultUsername}-${day}-${month}-${year}`; // Get the user id const randomBytes = window.crypto.getRandomValues(new Uint8Array(16)); @@ -42,8 +37,8 @@ export async function getRegisterOptions({ // Generate the registration options return await generateRegistrationOptions({ - rpName, - rpID: rpId, + rpName: WebAuthN.rpName, + rpID: WebAuthN.rpId, userID: fromHex(userId, "bytes"), userName: username, userDisplayName: username, diff --git a/packages/wallet/src/context/wallet/action/sign.ts b/packages/wallet/src/context/wallet/action/sign.ts index c9087afb..91981ec5 100644 --- a/packages/wallet/src/context/wallet/action/sign.ts +++ b/packages/wallet/src/context/wallet/action/sign.ts @@ -1,7 +1,7 @@ "use server"; import { getAuthenticatorRepository } from "@/context/wallet/repository/AuthenticatorRepository"; -import { rpId } from "@/context/wallet/smartWallet/webAuthN"; +import { WebAuthN } from "@frak-labs/app-essentials"; import { generateAuthenticationOptions } from "@simplewebauthn/server"; import type { Hex } from "viem"; @@ -25,7 +25,7 @@ export async function getSignOptions({ // Build the options return await generateAuthenticationOptions({ - rpID: rpId, + rpID: WebAuthN.rpId, allowCredentials: [ { id: authenticator._id, diff --git a/packages/wallet/src/context/wallet/dto/AuthenticatorDocument.ts b/packages/wallet/src/context/wallet/dto/AuthenticatorDocument.ts index a3a544ae..24d02e97 100644 --- a/packages/wallet/src/context/wallet/dto/AuthenticatorDocument.ts +++ b/packages/wallet/src/context/wallet/dto/AuthenticatorDocument.ts @@ -3,6 +3,7 @@ import type { AuthenticatorTransportFuture, CredentialDeviceType, } from "@simplewebauthn/types"; +import type { Binary } from "mongodb"; import type { Address } from "viem"; /** @@ -18,7 +19,7 @@ export type AuthenticatorDocument = Readonly<{ // The extracted pub key publicKey: P256PubKey; // The authenticator stuff - credentialPublicKey: string; + credentialPublicKey: Binary; counter: number; credentialDeviceType: CredentialDeviceType; credentialBackedUp: boolean; diff --git a/packages/wallet/src/context/wallet/repository/AuthenticatorRepository.ts b/packages/wallet/src/context/wallet/repository/AuthenticatorRepository.ts index c2f2958f..a0e5f421 100644 --- a/packages/wallet/src/context/wallet/repository/AuthenticatorRepository.ts +++ b/packages/wallet/src/context/wallet/repository/AuthenticatorRepository.ts @@ -2,7 +2,6 @@ import { getMongoDb } from "@/context/common/mongoDb"; import type { AuthenticatorDocument } from "@/context/wallet/dto/AuthenticatorDocument"; import type { Collection } from "mongodb"; import { memo } from "radash"; -import type { Address } from "viem"; /** * Repository used to access the authenticator collection @@ -21,55 +20,6 @@ class AuthenticatorRepository { ): Promise { return this.collection.findOne({ _id: credentialId }); } - - /** - * Create a new authenticator for the given user - * @param authenticator - */ - public async createAuthenticator( - authenticator: AuthenticatorDocument - ): Promise { - // Ensure no other credential exist with the same id - const existing = await this.getByCredentialId(authenticator._id); - if (existing) { - throw new Error("Credential already exists"); - } - - await this.collection.insertOne(authenticator); - } - - /** - * Update the counter for the given authenticator - * @param authenticatorId - * @param counter - */ - public async updateCounter({ - credentialId, - counter, - }: { credentialId: string; counter: number }): Promise { - await this.collection.updateOne( - { _id: credentialId }, - { $set: { counter } } - ); - } - - /** - * Set the smart wallet address for the given credential - * @param credentialId - * @param smartWalletAddress - */ - public async updateSmartWalletAddress({ - credentialId, - smartWalletAddress, - }: { - credentialId: string; - smartWalletAddress: Address; - }): Promise { - await this.collection.updateOne( - { _id: credentialId }, - { $set: { smartWalletAddress } } - ); - } } export const getAuthenticatorRepository = memo( diff --git a/packages/wallet/src/context/wallet/smartWallet/webAuthN.ts b/packages/wallet/src/context/wallet/smartWallet/webAuthN.ts index 1c790714..7ae9a3bd 100644 --- a/packages/wallet/src/context/wallet/smartWallet/webAuthN.ts +++ b/packages/wallet/src/context/wallet/smartWallet/webAuthN.ts @@ -1,77 +1,11 @@ -import { appUrl } from "@/context/common/env"; import type { P256Signature, WebAuthNSignature } from "@/types/WebAuthN"; -import { isRunningLocally } from "@frak-labs/app-essentials"; import { ECDSASigValue } from "@peculiar/asn1-ecc"; import { AsnParser } from "@peculiar/asn1-schema"; import { base64URLStringToBuffer } from "@simplewebauthn/browser"; -import { decodeCredentialPublicKey } from "@simplewebauthn/server/helpers"; import type { AuthenticationResponseJSON } from "@simplewebauthn/types"; import { toHex } from "viem"; import { polygon, polygonMumbai } from "viem/chains"; -/** - * The RP ID for the webauthn - */ -export const rpName = "Nexus by Frak"; -export const rpId = isRunningLocally ? "localhost" : "frak.id"; -export const rpOrigin = appUrl; - -/** - * The default user name - */ -export const defaultUsername = "Frak Wallet"; - -/** - * Decode the public key from the attestation object - * @param credentialPubKey - */ -export function decodePublicKey({ - credentialPubKey, -}: { credentialPubKey: Uint8Array }) { - const publicKey = decodeCredentialPublicKey( - credentialPubKey - ) as unknown as { - get(key: DecodedPubKeyIndexes.kty): number | undefined; - get(key: DecodedPubKeyIndexes.alg): number | undefined; - get(key: DecodedPubKeyIndexes.crv): DecodedPubKeyCrv | undefined; - get(key: DecodedPubKeyIndexes.x): Uint8Array | undefined; - get(key: DecodedPubKeyIndexes.y): Uint8Array | undefined; - }; - - const x = toHex( - publicKey.get(DecodedPubKeyIndexes.x) ?? Uint8Array.from([]) - ); - const y = toHex( - publicKey.get(DecodedPubKeyIndexes.y) ?? Uint8Array.from([]) - ); - - return { x, y }; -} - -/** - * The indexes zhere zhen can find each value in the decoded public key (matching the COSE curve) - */ -enum DecodedPubKeyIndexes { - kty = 1, - alg = 3, - crv = -1, - x = -2, - y = -3, - n = -1, - e = -2, -} - -/** - * The different type of curves we knoz about the public key - */ -enum DecodedPubKeyCrv { - P256 = 1, - P384 = 2, - P521 = 3, - ED25519 = 6, - SECP256K1 = 8, -} - /** * Verify a webauthn signature internally, and format it for blockchain transaction */ diff --git a/packages/wallet/src/module/authentication/hook/useLogin.ts b/packages/wallet/src/module/authentication/hook/useLogin.ts index c2a02bfa..659ca348 100644 --- a/packages/wallet/src/module/authentication/hook/useLogin.ts +++ b/packages/wallet/src/module/authentication/hook/useLogin.ts @@ -1,8 +1,8 @@ import type { PreviousAuthenticatorModel } from "@/context/common/dexie/PreviousAuthenticatorModel"; -import { rpId } from "@/context/wallet/smartWallet/webAuthN"; import { addLastAuthenticationAtom } from "@/module/authentication/atoms/lastAuthenticator"; import { sessionAtom } from "@/module/common/atoms/session"; import type { Session } from "@/types/Session"; +import { WebAuthN } from "@frak-labs/app-essentials"; import { backendApi } from "@frak-labs/shared/context/server"; import { startAuthentication } from "@simplewebauthn/browser"; import { generateAuthenticationOptions } from "@simplewebauthn/server"; @@ -51,7 +51,7 @@ export function useLogin( // Get the authenticate options const authenticationOptions = await generateAuthenticationOptions({ - rpID: rpId, + rpID: WebAuthN.rpId, userVerification: "required", allowCredentials, // timeout in ms (3min, can be useful for mobile phone linking)