From c1ccc4651f5b12222061e929eac042e9eab51dac Mon Sep 17 00:00:00 2001 From: Kyle Breeding Date: Thu, 11 Jul 2024 09:46:32 -0700 Subject: [PATCH 1/3] feat: replace requestId double with UUID v7 --- README.md | 10 +++++----- example/package-lock.json | 3 ++- example/src/main.ts | 1 + package-lock.json | 16 +++++++++++++++- package.json | 7 +++++-- src/signal.ts | 28 ++++++++++++++++------------ test/signal.spec.ts | 13 +++++++------ 7 files changed, 51 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index ce5b0ff..3254ee4 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ await client.attestation(async (challenge: Uint8Array) => ({ type: 'algorand', // The type of signature and public key address: testAccount.addr, // The address of the account signature: toBase64URL(nacl.sign.detached(challenge, testAccount.sk)), // The signature of the challenge - requestId: 12345, // Optionally authenticate a remote peer + requestId: '019097ff-bb8d-7f68-9062-89543625aca5', // Optionally authenticate a remote peer device: 'Demo Web Wallet' // Optional device name })) ``` @@ -37,14 +37,14 @@ await client.attestation(async (challenge: Uint8Array) => ({ ```typescript await client.assertion( credentialId, // Some known credential ID - {requestId: 12345} // Optional requestId to link + {requestId: '019097ff-bb8d-7f68-9062-89543625aca5'} // Optional requestId to link ) ``` #### Peering with a remote client ```typescript // Create the Peer Connection and await the remote client's answer -client.peer(12345, 'answer').then((dataChannel: RTCDataChannel)=>{ +client.peer('019097ff-bb8d-7f68-9062-89543625aca5', 'answer').then((dataChannel: RTCDataChannel)=>{ // Handle the data channel dataChannel.onmessage = (event: MessageEvent) => { console.log(event.data) @@ -82,7 +82,7 @@ interface SignalClient { /** * Generate a Request ID */ - generateRequestId(): any; + generateRequestId(): string; attestation(...args: any[]): Promise; assertion(...args: any[]): Promise; @@ -91,7 +91,7 @@ interface SignalClient { * Top level Friendly interface for signaling * @param args */ - peer(requestId: any, type: 'offer' | 'answer', config?: RTCConfiguration): Promise; + peer(requestId: string, type: 'offer' | 'answer', config?: RTCConfiguration): Promise; /** * Link a Request ID to this client diff --git a/example/package-lock.json b/example/package-lock.json index 7dd2982..65e2f55 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -25,7 +25,8 @@ "eventemitter3": "^5.0.1", "qr-code-styling": "*", "socket.io-client": "^4.7.5", - "tweetnacl": "^1.0.3" + "tweetnacl": "^1.0.3", + "uuid": "^10.0.0" }, "devDependencies": { "@semantic-release/changelog": "^6.0.3", diff --git a/example/src/main.ts b/example/src/main.ts index 9ccf3c5..319fbfc 100644 --- a/example/src/main.ts +++ b/example/src/main.ts @@ -173,6 +173,7 @@ function handleDataChannel(dataChannel: RTCDataChannel) { */ async function handleOfferClient() { // Peer to the remote client and await their offer + console.log('requestId', requestId); client.peer(requestId, 'offer', RTC_CONFIGURATION).then(handleDataChannel) // Once the link message is received by the remote wallet, hide the offer client.on('link-message', () => { diff --git a/package-lock.json b/package-lock.json index fd4dc89..4636bd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "eventemitter3": "^5.0.1", "qr-code-styling": "*", "socket.io-client": "^4.7.5", - "tweetnacl": "^1.0.3" + "tweetnacl": "^1.0.3", + "uuid": "^10.0.0" }, "devDependencies": { "@semantic-release/changelog": "^6.0.3", @@ -12523,6 +12524,19 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", diff --git a/package.json b/package.json index 2a7001f..aee0f9b 100644 --- a/package.json +++ b/package.json @@ -75,11 +75,14 @@ "eventemitter3": "^5.0.1", "qr-code-styling": "*", "socket.io-client": "^4.7.5", - "tweetnacl": "^1.0.3" + "tweetnacl": "^1.0.3", + "uuid": "^10.0.0" }, "jest": { "preset": "ts-jest", - "extensionsToTreatAsEsm": [".ts"], + "extensionsToTreatAsEsm": [ + ".ts" + ], "moduleNameMapper": { "^(\\.{1,2}/.*)\\.js$": "$1" }, diff --git a/src/signal.ts b/src/signal.ts index e7d2ef8..ba14a1c 100644 --- a/src/signal.ts +++ b/src/signal.ts @@ -2,10 +2,11 @@ import { io, ManagerOptions, Socket, SocketOptions } from 'socket.io-client'; import QRCodeStyling, { Options as QRCodeOptions } from 'qr-code-styling'; import { EventEmitter } from 'eventemitter3'; import { attestation, DEFAULT_ATTESTATION_OPTIONS } from './attestation.js'; +import { v7 as uuidv7 } from 'uuid'; export type LinkMessage = { credId?: string; - requestId: string | number; + requestId: string; wallet: string; }; export const REQUEST_IS_MISSING_MESSAGE = 'Request id is required'; @@ -61,10 +62,11 @@ export const DEFAULT_QR_CODE_OPTIONS: QRCodeOptions = { }; export async function generateQRCode( - { requestId, url }: { requestId: any; url: string }, + { requestId, url }: { requestId?: string; url: string }, qrCodeOptions: QRCodeOptions = DEFAULT_QR_CODE_OPTIONS, ) { if (typeof requestId === 'undefined') + console.log('generateQRCode'); throw new Error(REQUEST_IS_MISSING_MESSAGE); qrCodeOptions.data = generateDeepLink(url, requestId); @@ -81,11 +83,12 @@ export async function generateQRCode( * @param {string} origin * @param requestId */ -export function generateDeepLink(origin: string, requestId: any) { +export function generateDeepLink(origin: string, requestId: string) { if (typeof origin !== 'string') { throw new Error(ORIGIN_IS_MISSING_MESSAGE); } - if (typeof requestId === 'undefined') { + if (typeof requestId !== 'string') { + console.log('generateDeepLink'); throw new Error(REQUEST_IS_MISSING_MESSAGE); } return `liquid://${origin.replace('https://', '')}/?requestId=${requestId}`; @@ -97,7 +100,7 @@ export class SignalClient extends EventEmitter { private url: string; type: 'offer' | 'answer'; private authenticated: boolean = false; - private requestId: any | undefined; + private requestId: string | undefined; peerClient: RTCPeerConnection | undefined; private qrCodeOptions: QRCodeOptions = DEFAULT_QR_CODE_OPTIONS; socket: Socket; @@ -124,9 +127,8 @@ export class SignalClient extends EventEmitter { }); } - static generateRequestId() { - //TODO: replace with toBase64URL(nacl.randomBytes(nacl.sign.seedLength) - return Math.random() as any; + static generateRequestId(): string { + return uuidv7() } attestation( onChallenge: (challenge: Uint8Array) => any, @@ -147,6 +149,7 @@ export class SignalClient extends EventEmitter { */ async qrCode() { if (typeof this.requestId === 'undefined') + console.log('qrCode'); throw new Error(REQUEST_IS_MISSING_MESSAGE); return generateQRCode( { requestId: this.requestId, url: this.url }, @@ -158,11 +161,12 @@ export class SignalClient extends EventEmitter { * Create a Deep Link URI * @param requestId */ - deepLink(requestId: any) { + deepLink(requestId: string) { if ( - typeof requestId === 'undefined' && + typeof requestId !== 'string' && typeof this.requestId === 'undefined' ) { + console.log('deepLink'); throw new Error(REQUEST_IS_MISSING_MESSAGE); } return generateDeepLink(this.url, requestId || this.requestId); @@ -187,7 +191,7 @@ export class SignalClient extends EventEmitter { * @param config */ async peer( - requestId: any, + requestId: string | undefined, type: 'offer' | 'answer', config: RTCConfiguration = { iceServers: [ @@ -292,7 +296,7 @@ export class SignalClient extends EventEmitter { * Await for a link message for a given requestId * @param requestId */ - async link(requestId: any) { + async link(requestId: string) { if (typeof this.requestId !== 'undefined') throw new Error(REQUEST_IN_PROCESS_MESSAGE); this.requestId = requestId; diff --git a/test/signal.spec.ts b/test/signal.spec.ts index 98eca09..93e9539 100644 --- a/test/signal.spec.ts +++ b/test/signal.spec.ts @@ -120,7 +120,8 @@ describe("SignalClient", function() { // Request ID test("generateRequestId", function() { const id = SignalClient.generateRequestId(); - expect(id).toBeGreaterThan(0); + expect(id).toBeDefined(); + expect(id.length).toBe(36); }); // TODO: Decide what to do with QR Code generation, possibly fork and maintain the library since Pera also uses it @@ -267,8 +268,8 @@ describe("SignalClient", function() { }) test("peer(answer) unbuffered", async function(){ // Only allow one peer session at a time - client.requestId = 1234 - await expect(()=>client.peer(1234, "answer")).rejects.toThrow(new Error(mod.REQUEST_IN_PROCESS_MESSAGE)); + client.requestId = '019097ff-bb8d-7a31-83bb-aa934d351662' + await expect(()=>client.peer('019097ff-bb8d-7a31-83bb-aa934d351662', "answer")).rejects.toThrow(new Error(mod.REQUEST_IN_PROCESS_MESSAGE)); delete client.requestId; const sdpFixture = { @@ -312,8 +313,8 @@ describe("SignalClient", function() { }) test("peer(answer)", async function() { // Only allow one peer session at a time - client.requestId = 1234 - await expect(()=>client.peer(1234, "answer")).rejects.toThrow(new Error(mod.REQUEST_IN_PROCESS_MESSAGE)); + client.requestId = '019097ff-bb8d-7a31-83bb-aa934d351662' + await expect(()=>client.peer('019097ff-bb8d-7a31-83bb-aa934d351662', "answer")).rejects.toThrow(new Error(mod.REQUEST_IN_PROCESS_MESSAGE)); delete client.requestId; const sdpFixture = { @@ -409,7 +410,6 @@ test("generateQRCode", async () => { const qrFixture = { url: "https://liquid-auth.onrender.com", requestId: SignalClient.generateRequestId() }; expect(generateQRCode(qrFixture)).toBeDefined(); await expect(() => - // @ts-expect-error, needed for testing generateQRCode({ url: qrFixture.url }) ).rejects.toThrow(new Error(mod.REQUEST_IS_MISSING_MESSAGE)); getRawData = () => { @@ -431,6 +431,7 @@ test('generateDeepLink', async () => { generateDeepLink(url) ).toThrow(new Error(REQUEST_IS_MISSING_MESSAGE)); expect(() => + // @ts-expect-error, needed for testing generateDeepLink(undefined, requestId) ).toThrow(new Error(ORIGIN_IS_MISSING_MESSAGE)); }) From 1c92e433d186bb8b2b58aec4f6c46694f8172e81 Mon Sep 17 00:00:00 2001 From: Kyle Breeding Date: Sun, 14 Jul 2024 23:02:39 -0700 Subject: [PATCH 2/3] chore: removing console logs --- src/signal.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/signal.ts b/src/signal.ts index ba14a1c..b905b11 100644 --- a/src/signal.ts +++ b/src/signal.ts @@ -66,7 +66,6 @@ export async function generateQRCode( qrCodeOptions: QRCodeOptions = DEFAULT_QR_CODE_OPTIONS, ) { if (typeof requestId === 'undefined') - console.log('generateQRCode'); throw new Error(REQUEST_IS_MISSING_MESSAGE); qrCodeOptions.data = generateDeepLink(url, requestId); @@ -88,7 +87,6 @@ export function generateDeepLink(origin: string, requestId: string) { throw new Error(ORIGIN_IS_MISSING_MESSAGE); } if (typeof requestId !== 'string') { - console.log('generateDeepLink'); throw new Error(REQUEST_IS_MISSING_MESSAGE); } return `liquid://${origin.replace('https://', '')}/?requestId=${requestId}`; @@ -149,7 +147,6 @@ export class SignalClient extends EventEmitter { */ async qrCode() { if (typeof this.requestId === 'undefined') - console.log('qrCode'); throw new Error(REQUEST_IS_MISSING_MESSAGE); return generateQRCode( { requestId: this.requestId, url: this.url }, @@ -166,7 +163,6 @@ export class SignalClient extends EventEmitter { typeof requestId !== 'string' && typeof this.requestId === 'undefined' ) { - console.log('deepLink'); throw new Error(REQUEST_IS_MISSING_MESSAGE); } return generateDeepLink(this.url, requestId || this.requestId); From 334a3434afaf3dc4fab62bcb14ee152922501c57 Mon Sep 17 00:00:00 2001 From: Kyle Breeding Date: Mon, 15 Jul 2024 07:44:57 -0700 Subject: [PATCH 3/3] chore: removed an unnecessary test case --- test/signal.spec.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/signal.spec.ts b/test/signal.spec.ts index 93e9539..8fcf27e 100644 --- a/test/signal.spec.ts +++ b/test/signal.spec.ts @@ -430,8 +430,4 @@ test('generateDeepLink', async () => { // @ts-expect-error, needed for testing generateDeepLink(url) ).toThrow(new Error(REQUEST_IS_MISSING_MESSAGE)); - expect(() => - // @ts-expect-error, needed for testing - generateDeepLink(undefined, requestId) - ).toThrow(new Error(ORIGIN_IS_MISSING_MESSAGE)); })